1 /*
2 +----------------------------------------------------------------------+
3 | Zend OPcache |
4 +----------------------------------------------------------------------+
5 | Copyright (c) The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Andi Gutmans <andi@php.net> |
16 | Zeev Suraski <zeev@php.net> |
17 | Stanislav Malyshev <stas@zend.com> |
18 | Dmitry Stogov <dmitry@php.net> |
19 +----------------------------------------------------------------------+
20 */
21
22 #include "main/php.h"
23 #include "main/php_globals.h"
24 #include "zend.h"
25 #include "zend_extensions.h"
26 #include "zend_compile.h"
27 #include "ZendAccelerator.h"
28 #include "zend_persist.h"
29 #include "zend_shared_alloc.h"
30 #include "zend_accelerator_module.h"
31 #include "zend_accelerator_blacklist.h"
32 #include "zend_list.h"
33 #include "zend_execute.h"
34 #include "zend_inheritance.h"
35 #include "zend_exceptions.h"
36 #include "main/php_main.h"
37 #include "main/SAPI.h"
38 #include "main/php_streams.h"
39 #include "main/php_open_temporary_file.h"
40 #include "zend_API.h"
41 #include "zend_ini.h"
42 #include "zend_virtual_cwd.h"
43 #include "zend_accelerator_util_funcs.h"
44 #include "zend_accelerator_hash.h"
45 #include "zend_file_cache.h"
46 #include "ext/pcre/php_pcre.h"
47 #include "ext/standard/md5.h"
48 #include "ext/hash/php_hash.h"
49
50 #ifndef ZEND_WIN32
51 #include <netdb.h>
52 #endif
53
54 #ifdef ZEND_WIN32
55 typedef int uid_t;
56 typedef int gid_t;
57 #include <io.h>
58 #include <lmcons.h>
59 #endif
60
61 #ifndef ZEND_WIN32
62 # include <sys/time.h>
63 #else
64 # include <process.h>
65 #endif
66
67 #ifdef HAVE_UNISTD_H
68 # include <unistd.h>
69 #endif
70 #include <fcntl.h>
71 #include <signal.h>
72 #include <time.h>
73
74 #ifndef ZEND_WIN32
75 # include <sys/types.h>
76 # include <sys/wait.h>
77 # include <sys/ipc.h>
78 # include <pwd.h>
79 # include <grp.h>
80 #endif
81
82 #include <sys/stat.h>
83 #include <errno.h>
84
85 #ifdef __AVX__
86 #include <immintrin.h>
87 #endif
88
89 #define SHM_PROTECT() \
90 do { \
91 if (ZCG(accel_directives).protect_memory) { \
92 zend_accel_shared_protect(1); \
93 } \
94 } while (0)
95 #define SHM_UNPROTECT() \
96 do { \
97 if (ZCG(accel_directives).protect_memory) { \
98 zend_accel_shared_protect(0); \
99 } \
100 } while (0)
101
102 ZEND_EXTENSION();
103
104 #ifndef ZTS
105 zend_accel_globals accel_globals;
106 #else
107 int accel_globals_id;
108 #if defined(COMPILE_DL_OPCACHE)
109 ZEND_TSRMLS_CACHE_DEFINE()
110 #endif
111 #endif
112
113 /* Points to the structure shared across all PHP processes */
114 zend_accel_shared_globals *accel_shared_globals = NULL;
115
116 /* true globals, no need for thread safety */
117 char accel_system_id[32];
118 #ifdef ZEND_WIN32
119 char accel_uname_id[32];
120 #endif
121 zend_bool accel_startup_ok = 0;
122 static char *zps_failure_reason = NULL;
123 char *zps_api_failure_reason = NULL;
124 zend_bool file_cache_only = 0; /* process uses file cache only */
125 #if ENABLE_FILE_CACHE_FALLBACK
126 zend_bool fallback_process = 0; /* process uses file cache fallback */
127 #endif
128
129 static zend_op_array *(*accelerator_orig_compile_file)(zend_file_handle *file_handle, int type);
130 static int (*accelerator_orig_zend_stream_open_function)(const char *filename, zend_file_handle *handle );
131 static zend_string *(*accelerator_orig_zend_resolve_path)(const char *filename, size_t filename_len);
132 static zif_handler orig_chdir = NULL;
133 static ZEND_INI_MH((*orig_include_path_on_modify)) = NULL;
134 static int (*orig_post_startup_cb)(void);
135
136 static void accel_gen_system_id(void);
137 static int accel_post_startup(void);
138 static int accel_finish_startup(void);
139
140 static void preload_shutdown(void);
141 static void preload_activate(void);
142 static void preload_restart(void);
143
144 #ifdef ZEND_WIN32
145 # define INCREMENT(v) InterlockedIncrement64(&ZCSG(v))
146 # define DECREMENT(v) InterlockedDecrement64(&ZCSG(v))
147 # define LOCKVAL(v) (ZCSG(v))
148 #endif
149
150 #ifdef ZEND_WIN32
zend_accel_get_time(void)151 static time_t zend_accel_get_time(void)
152 {
153 FILETIME now;
154 GetSystemTimeAsFileTime(&now);
155
156 return (time_t) ((((((__int64)now.dwHighDateTime) << 32)|now.dwLowDateTime) - 116444736000000000L)/10000000);
157 }
158 #else
159 # define zend_accel_get_time() time(NULL)
160 #endif
161
is_stream_path(const char * filename)162 static inline int is_stream_path(const char *filename)
163 {
164 const char *p;
165
166 for (p = filename;
167 (*p >= 'a' && *p <= 'z') ||
168 (*p >= 'A' && *p <= 'Z') ||
169 (*p >= '0' && *p <= '9') ||
170 *p == '+' || *p == '-' || *p == '.';
171 p++);
172 return ((p != filename) && (p[0] == ':') && (p[1] == '/') && (p[2] == '/'));
173 }
174
is_cacheable_stream_path(const char * filename)175 static inline int is_cacheable_stream_path(const char *filename)
176 {
177 return memcmp(filename, "file://", sizeof("file://") - 1) == 0 ||
178 memcmp(filename, "phar://", sizeof("phar://") - 1) == 0;
179 }
180
181 /* O+ overrides PHP chdir() function and remembers the current working directory
182 * in ZCG(cwd) and ZCG(cwd_len). Later accel_getcwd() can use stored value and
183 * avoid getcwd() call.
184 */
ZEND_FUNCTION(accel_chdir)185 static ZEND_FUNCTION(accel_chdir)
186 {
187 char cwd[MAXPATHLEN];
188
189 orig_chdir(INTERNAL_FUNCTION_PARAM_PASSTHRU);
190 if (VCWD_GETCWD(cwd, MAXPATHLEN)) {
191 if (ZCG(cwd)) {
192 zend_string_release_ex(ZCG(cwd), 0);
193 }
194 ZCG(cwd) = zend_string_init(cwd, strlen(cwd), 0);
195 } else {
196 if (ZCG(cwd)) {
197 zend_string_release_ex(ZCG(cwd), 0);
198 ZCG(cwd) = NULL;
199 }
200 }
201 ZCG(cwd_key_len) = 0;
202 ZCG(cwd_check) = 1;
203 }
204
accel_getcwd(void)205 static inline zend_string* accel_getcwd(void)
206 {
207 if (ZCG(cwd)) {
208 return ZCG(cwd);
209 } else {
210 char cwd[MAXPATHLEN + 1];
211
212 if (!VCWD_GETCWD(cwd, MAXPATHLEN)) {
213 return NULL;
214 }
215 ZCG(cwd) = zend_string_init(cwd, strlen(cwd), 0);
216 ZCG(cwd_key_len) = 0;
217 ZCG(cwd_check) = 1;
218 return ZCG(cwd);
219 }
220 }
221
zend_accel_schedule_restart_if_necessary(zend_accel_restart_reason reason)222 void zend_accel_schedule_restart_if_necessary(zend_accel_restart_reason reason)
223 {
224 if ((((double) ZSMMG(wasted_shared_memory)) / ZCG(accel_directives).memory_consumption) >= ZCG(accel_directives).max_wasted_percentage) {
225 zend_accel_schedule_restart(reason);
226 }
227 }
228
229 /* O+ tracks changes of "include_path" directive. It stores all the requested
230 * values in ZCG(include_paths) shared hash table, current value in
231 * ZCG(include_path)/ZCG(include_path_len) and one letter "path key" in
232 * ZCG(include_path_key).
233 */
ZEND_INI_MH(accel_include_path_on_modify)234 static ZEND_INI_MH(accel_include_path_on_modify)
235 {
236 int ret = orig_include_path_on_modify(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
237
238 if (ret == SUCCESS) {
239 ZCG(include_path) = new_value;
240 ZCG(include_path_key_len) = 0;
241 ZCG(include_path_check) = 1;
242 }
243 return ret;
244 }
245
accel_restart_enter(void)246 static inline void accel_restart_enter(void)
247 {
248 #ifdef ZEND_WIN32
249 INCREMENT(restart_in);
250 #else
251 struct flock restart_in_progress;
252
253 restart_in_progress.l_type = F_WRLCK;
254 restart_in_progress.l_whence = SEEK_SET;
255 restart_in_progress.l_start = 2;
256 restart_in_progress.l_len = 1;
257
258 if (fcntl(lock_file, F_SETLK, &restart_in_progress) == -1) {
259 zend_accel_error(ACCEL_LOG_DEBUG, "RestartC(+1): %s (%d)", strerror(errno), errno);
260 }
261 #endif
262 ZCSG(restart_in_progress) = 1;
263 }
264
accel_restart_leave(void)265 static inline void accel_restart_leave(void)
266 {
267 #ifdef ZEND_WIN32
268 ZCSG(restart_in_progress) = 0;
269 DECREMENT(restart_in);
270 #else
271 struct flock restart_finished;
272
273 restart_finished.l_type = F_UNLCK;
274 restart_finished.l_whence = SEEK_SET;
275 restart_finished.l_start = 2;
276 restart_finished.l_len = 1;
277
278 ZCSG(restart_in_progress) = 0;
279 if (fcntl(lock_file, F_SETLK, &restart_finished) == -1) {
280 zend_accel_error(ACCEL_LOG_DEBUG, "RestartC(-1): %s (%d)", strerror(errno), errno);
281 }
282 #endif
283 }
284
accel_restart_is_active(void)285 static inline int accel_restart_is_active(void)
286 {
287 if (ZCSG(restart_in_progress)) {
288 #ifndef ZEND_WIN32
289 struct flock restart_check;
290
291 restart_check.l_type = F_WRLCK;
292 restart_check.l_whence = SEEK_SET;
293 restart_check.l_start = 2;
294 restart_check.l_len = 1;
295
296 if (fcntl(lock_file, F_GETLK, &restart_check) == -1) {
297 zend_accel_error(ACCEL_LOG_DEBUG, "RestartC: %s (%d)", strerror(errno), errno);
298 return FAILURE;
299 }
300 if (restart_check.l_type == F_UNLCK) {
301 ZCSG(restart_in_progress) = 0;
302 return 0;
303 } else {
304 return 1;
305 }
306 #else
307 return LOCKVAL(restart_in) != 0;
308 #endif
309 }
310 return 0;
311 }
312
313 /* Creates a read lock for SHM access */
accel_activate_add(void)314 static inline int accel_activate_add(void)
315 {
316 #ifdef ZEND_WIN32
317 SHM_UNPROTECT();
318 INCREMENT(mem_usage);
319 SHM_PROTECT();
320 #else
321 struct flock mem_usage_lock;
322
323 mem_usage_lock.l_type = F_RDLCK;
324 mem_usage_lock.l_whence = SEEK_SET;
325 mem_usage_lock.l_start = 1;
326 mem_usage_lock.l_len = 1;
327
328 if (fcntl(lock_file, F_SETLK, &mem_usage_lock) == -1) {
329 zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC(+1): %s (%d)", strerror(errno), errno);
330 return FAILURE;
331 }
332 #endif
333 return SUCCESS;
334 }
335
336 /* Releases a lock for SHM access */
accel_deactivate_sub(void)337 static inline void accel_deactivate_sub(void)
338 {
339 #ifdef ZEND_WIN32
340 if (ZCG(counted)) {
341 SHM_UNPROTECT();
342 DECREMENT(mem_usage);
343 ZCG(counted) = 0;
344 SHM_PROTECT();
345 }
346 #else
347 struct flock mem_usage_unlock;
348
349 mem_usage_unlock.l_type = F_UNLCK;
350 mem_usage_unlock.l_whence = SEEK_SET;
351 mem_usage_unlock.l_start = 1;
352 mem_usage_unlock.l_len = 1;
353
354 if (fcntl(lock_file, F_SETLK, &mem_usage_unlock) == -1) {
355 zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC(-1): %s (%d)", strerror(errno), errno);
356 }
357 #endif
358 }
359
accel_unlock_all(void)360 static inline void accel_unlock_all(void)
361 {
362 #ifdef ZEND_WIN32
363 accel_deactivate_sub();
364 #else
365 struct flock mem_usage_unlock_all;
366
367 mem_usage_unlock_all.l_type = F_UNLCK;
368 mem_usage_unlock_all.l_whence = SEEK_SET;
369 mem_usage_unlock_all.l_start = 0;
370 mem_usage_unlock_all.l_len = 0;
371
372 if (fcntl(lock_file, F_SETLK, &mem_usage_unlock_all) == -1) {
373 zend_accel_error(ACCEL_LOG_DEBUG, "UnlockAll: %s (%d)", strerror(errno), errno);
374 }
375 #endif
376 }
377
378 /* Interned strings support */
379
380 /* O+ disables creation of interned strings by regular PHP compiler, instead,
381 * it creates interned strings in shared memory when saves a script.
382 * Such interned strings are shared across all PHP processes
383 */
384
385 #define STRTAB_INVALID_POS 0
386
387 #define STRTAB_HASH_TO_SLOT(tab, h) \
388 ((uint32_t*)((char*)(tab) + sizeof(*(tab)) + ((h) & (tab)->nTableMask)))
389 #define STRTAB_STR_TO_POS(tab, s) \
390 ((uint32_t)((char*)s - (char*)(tab)))
391 #define STRTAB_POS_TO_STR(tab, pos) \
392 ((zend_string*)((char*)(tab) + (pos)))
393 #define STRTAB_COLLISION(s) \
394 (*((uint32_t*)((char*)s - sizeof(uint32_t))))
395 #define STRTAB_STR_SIZE(s) \
396 ZEND_MM_ALIGNED_SIZE_EX(_ZSTR_HEADER_SIZE + ZSTR_LEN(s) + 5, 8)
397 #define STRTAB_NEXT(s) \
398 ((zend_string*)((char*)(s) + STRTAB_STR_SIZE(s)))
399
accel_interned_strings_restore_state(void)400 static void accel_interned_strings_restore_state(void)
401 {
402 zend_string *s, *top;
403 uint32_t *hash_slot, n;
404
405 /* clear removed content */
406 memset(ZCSG(interned_strings).saved_top,
407 0, (char*)ZCSG(interned_strings).top - (char*)ZCSG(interned_strings).saved_top);
408
409 /* Reset "top" */
410 ZCSG(interned_strings).top = ZCSG(interned_strings).saved_top;
411
412 /* rehash */
413 memset((char*)&ZCSG(interned_strings) + sizeof(zend_string_table),
414 STRTAB_INVALID_POS,
415 (char*)ZCSG(interned_strings).start -
416 ((char*)&ZCSG(interned_strings) + sizeof(zend_string_table)));
417 s = ZCSG(interned_strings).start;
418 top = ZCSG(interned_strings).top;
419 n = 0;
420 if (EXPECTED(s < top)) {
421 do {
422 hash_slot = STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), ZSTR_H(s));
423 STRTAB_COLLISION(s) = *hash_slot;
424 *hash_slot = STRTAB_STR_TO_POS(&ZCSG(interned_strings), s);
425 s = STRTAB_NEXT(s);
426 n++;
427 } while (s < top);
428 }
429 ZCSG(interned_strings).nNumOfElements = n;
430 }
431
accel_interned_strings_save_state(void)432 static void accel_interned_strings_save_state(void)
433 {
434 ZCSG(interned_strings).saved_top = ZCSG(interned_strings).top;
435 }
436
accel_find_interned_string(zend_string * str)437 static zend_always_inline zend_string *accel_find_interned_string(zend_string *str)
438 {
439 /* for now interned strings are supported only for non-ZTS build */
440 zend_ulong h;
441 uint32_t pos;
442 zend_string *s;
443
444 if (IS_ACCEL_INTERNED(str)) {
445 /* this is already an interned string */
446 return str;
447 }
448
449 if (!ZCG(counted)) {
450 if (!ZCG(accelerator_enabled) || accel_activate_add() == FAILURE) {
451 return NULL;
452 }
453 ZCG(counted) = 1;
454 }
455
456 h = zend_string_hash_val(str);
457
458 /* check for existing interned string */
459 pos = *STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), h);
460 if (EXPECTED(pos != STRTAB_INVALID_POS)) {
461 do {
462 s = STRTAB_POS_TO_STR(&ZCSG(interned_strings), pos);
463 if (EXPECTED(ZSTR_H(s) == h) && zend_string_equal_content(s, str)) {
464 return s;
465 }
466 pos = STRTAB_COLLISION(s);
467 } while (pos != STRTAB_INVALID_POS);
468 }
469
470 return NULL;
471 }
472
accel_new_interned_string(zend_string * str)473 zend_string* ZEND_FASTCALL accel_new_interned_string(zend_string *str)
474 {
475 zend_ulong h;
476 uint32_t pos, *hash_slot;
477 zend_string *s;
478
479 if (UNEXPECTED(file_cache_only)) {
480 return str;
481 }
482
483 if (IS_ACCEL_INTERNED(str)) {
484 /* this is already an interned string */
485 return str;
486 }
487
488 h = zend_string_hash_val(str);
489
490 /* check for existing interned string */
491 hash_slot = STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), h);
492 pos = *hash_slot;
493 if (EXPECTED(pos != STRTAB_INVALID_POS)) {
494 do {
495 s = STRTAB_POS_TO_STR(&ZCSG(interned_strings), pos);
496 if (EXPECTED(ZSTR_H(s) == h) && zend_string_equal_content(s, str)) {
497 zend_string_release(str);
498 return s;
499 }
500 pos = STRTAB_COLLISION(s);
501 } while (pos != STRTAB_INVALID_POS);
502 }
503
504 if (UNEXPECTED((char*)ZCSG(interned_strings).end - (char*)ZCSG(interned_strings).top < STRTAB_STR_SIZE(str))) {
505 /* no memory, return the same non-interned string */
506 zend_accel_error(ACCEL_LOG_WARNING, "Interned string buffer overflow");
507 return str;
508 }
509
510 /* create new interning string in shared interned strings buffer */
511 ZCSG(interned_strings).nNumOfElements++;
512 s = ZCSG(interned_strings).top;
513 hash_slot = STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), h);
514 STRTAB_COLLISION(s) = *hash_slot;
515 *hash_slot = STRTAB_STR_TO_POS(&ZCSG(interned_strings), s);
516 GC_SET_REFCOUNT(s, 1);
517 GC_TYPE_INFO(s) = IS_STRING | ((IS_STR_INTERNED | IS_STR_PERMANENT) << GC_FLAGS_SHIFT);
518 ZSTR_H(s) = h;
519 ZSTR_LEN(s) = ZSTR_LEN(str);
520 memcpy(ZSTR_VAL(s), ZSTR_VAL(str), ZSTR_LEN(s) + 1);
521 ZCSG(interned_strings).top = STRTAB_NEXT(s);
522
523 zend_string_release(str);
524 return s;
525 }
526
accel_new_interned_string_for_php(zend_string * str)527 static zend_string* ZEND_FASTCALL accel_new_interned_string_for_php(zend_string *str)
528 {
529 zend_string_hash_val(str);
530 if (ZCG(counted)) {
531 zend_string *ret = accel_find_interned_string(str);
532
533 if (ret) {
534 zend_string_release(str);
535 return ret;
536 }
537 }
538 return str;
539 }
540
accel_find_interned_string_ex(zend_ulong h,const char * str,size_t size)541 static zend_always_inline zend_string *accel_find_interned_string_ex(zend_ulong h, const char *str, size_t size)
542 {
543 uint32_t pos;
544 zend_string *s;
545
546 /* check for existing interned string */
547 pos = *STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), h);
548 if (EXPECTED(pos != STRTAB_INVALID_POS)) {
549 do {
550 s = STRTAB_POS_TO_STR(&ZCSG(interned_strings), pos);
551 if (EXPECTED(ZSTR_H(s) == h) && EXPECTED(ZSTR_LEN(s) == size)) {
552 if (!memcmp(ZSTR_VAL(s), str, size)) {
553 return s;
554 }
555 }
556 pos = STRTAB_COLLISION(s);
557 } while (pos != STRTAB_INVALID_POS);
558 }
559 return NULL;
560 }
561
accel_init_interned_string_for_php(const char * str,size_t size,int permanent)562 static zend_string* ZEND_FASTCALL accel_init_interned_string_for_php(const char *str, size_t size, int permanent)
563 {
564 if (ZCG(counted)) {
565 zend_ulong h = zend_inline_hash_func(str, size);
566 zend_string *ret = accel_find_interned_string_ex(h, str, size);
567
568 if (!ret) {
569 ret = zend_string_init(str, size, permanent);
570 ZSTR_H(ret) = h;
571 }
572
573 return ret;
574 }
575
576 return zend_string_init(str, size, permanent);
577 }
578
579 /* Copy PHP interned strings from PHP process memory into the shared memory */
accel_copy_permanent_strings(zend_new_interned_string_func_t new_interned_string)580 static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_interned_string)
581 {
582 uint32_t j;
583 Bucket *p, *q;
584 HashTable *ht;
585
586 /* empty string */
587 zend_empty_string = new_interned_string(zend_empty_string);
588 for (j = 0; j < 256; j++) {
589 zend_one_char_string[j] = new_interned_string(ZSTR_CHAR(j));
590 }
591 for (j = 0; j < ZEND_STR_LAST_KNOWN; j++) {
592 zend_known_strings[j] = new_interned_string(zend_known_strings[j]);
593 }
594
595 /* function table hash keys */
596 ZEND_HASH_FOREACH_BUCKET(CG(function_table), p) {
597 if (p->key) {
598 p->key = new_interned_string(p->key);
599 }
600 if (Z_FUNC(p->val)->common.function_name) {
601 Z_FUNC(p->val)->common.function_name = new_interned_string(Z_FUNC(p->val)->common.function_name);
602 }
603 if (Z_FUNC(p->val)->common.arg_info &&
604 (Z_FUNC(p->val)->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS))) {
605 uint32_t i;
606 uint32_t num_args = Z_FUNC(p->val)->common.num_args + 1;
607 zend_arg_info *arg_info = Z_FUNC(p->val)->common.arg_info - 1;
608
609 if (Z_FUNC(p->val)->common.fn_flags & ZEND_ACC_VARIADIC) {
610 num_args++;
611 }
612 for (i = 0 ; i < num_args; i++) {
613 if (ZEND_TYPE_IS_CLASS(arg_info[i].type)) {
614 zend_bool allow_null = ZEND_TYPE_ALLOW_NULL(arg_info[i].type);
615 arg_info[i].type = ZEND_TYPE_ENCODE_CLASS(new_interned_string(ZEND_TYPE_NAME(arg_info[i].type)), allow_null);
616 }
617 }
618 }
619 } ZEND_HASH_FOREACH_END();
620
621 /* class table hash keys, class names, properties, methods, constants, etc */
622 ZEND_HASH_FOREACH_BUCKET(CG(class_table), p) {
623 zend_class_entry *ce;
624
625 ce = (zend_class_entry*)Z_PTR(p->val);
626
627 if (p->key) {
628 p->key = new_interned_string(p->key);
629 }
630
631 if (ce->name) {
632 ce->name = new_interned_string(ce->name);
633 }
634
635 ZEND_HASH_FOREACH_BUCKET(&ce->properties_info, q) {
636 zend_property_info *info;
637
638 info = (zend_property_info*)Z_PTR(q->val);
639
640 if (q->key) {
641 q->key = new_interned_string(q->key);
642 }
643
644 if (info->name) {
645 info->name = new_interned_string(info->name);
646 }
647 } ZEND_HASH_FOREACH_END();
648
649 ZEND_HASH_FOREACH_BUCKET(&ce->function_table, q) {
650 if (q->key) {
651 q->key = new_interned_string(q->key);
652 }
653 if (Z_FUNC(q->val)->common.function_name) {
654 Z_FUNC(q->val)->common.function_name = new_interned_string(Z_FUNC(q->val)->common.function_name);
655 }
656 } ZEND_HASH_FOREACH_END();
657
658 ZEND_HASH_FOREACH_BUCKET(&ce->constants_table, q) {
659 if (q->key) {
660 q->key = new_interned_string(q->key);
661 }
662 } ZEND_HASH_FOREACH_END();
663 } ZEND_HASH_FOREACH_END();
664
665 /* constant hash keys */
666 ZEND_HASH_FOREACH_BUCKET(EG(zend_constants), p) {
667 zend_constant *c;
668
669 if (p->key) {
670 p->key = new_interned_string(p->key);
671 }
672 c = (zend_constant*)Z_PTR(p->val);
673 if (c->name) {
674 c->name = new_interned_string(c->name);
675 }
676 if (Z_TYPE(c->value) == IS_STRING) {
677 ZVAL_STR(&c->value, new_interned_string(Z_STR(c->value)));
678 }
679 } ZEND_HASH_FOREACH_END();
680
681 /* auto globals hash keys and names */
682 ZEND_HASH_FOREACH_BUCKET(CG(auto_globals), p) {
683 zend_auto_global *auto_global;
684
685 auto_global = (zend_auto_global*)Z_PTR(p->val);
686
687 zend_string_addref(auto_global->name);
688 auto_global->name = new_interned_string(auto_global->name);
689 if (p->key) {
690 p->key = new_interned_string(p->key);
691 }
692 } ZEND_HASH_FOREACH_END();
693
694 ZEND_HASH_FOREACH_BUCKET(&module_registry, p) {
695 if (p->key) {
696 p->key = new_interned_string(p->key);
697 }
698 } ZEND_HASH_FOREACH_END();
699
700 ZEND_HASH_FOREACH_BUCKET(EG(ini_directives), p) {
701 zend_ini_entry *entry = (zend_ini_entry*)Z_PTR(p->val);
702
703 if (p->key) {
704 p->key = new_interned_string(p->key);
705 }
706 if (entry->name) {
707 entry->name = new_interned_string(entry->name);
708 }
709 if (entry->value) {
710 entry->value = new_interned_string(entry->value);
711 }
712 if (entry->orig_value) {
713 entry->orig_value = new_interned_string(entry->orig_value);
714 }
715 } ZEND_HASH_FOREACH_END();
716
717 ht = php_get_stream_filters_hash_global();
718 ZEND_HASH_FOREACH_BUCKET(ht, p) {
719 if (p->key) {
720 p->key = new_interned_string(p->key);
721 }
722 } ZEND_HASH_FOREACH_END();
723
724 ht = php_stream_get_url_stream_wrappers_hash_global();
725 ZEND_HASH_FOREACH_BUCKET(ht, p) {
726 if (p->key) {
727 p->key = new_interned_string(p->key);
728 }
729 } ZEND_HASH_FOREACH_END();
730
731 ht = php_stream_xport_get_hash();
732 ZEND_HASH_FOREACH_BUCKET(ht, p) {
733 if (p->key) {
734 p->key = new_interned_string(p->key);
735 }
736 } ZEND_HASH_FOREACH_END();
737 }
738
accel_replace_string_by_shm_permanent(zend_string * str)739 static zend_string* ZEND_FASTCALL accel_replace_string_by_shm_permanent(zend_string *str)
740 {
741 zend_string *ret = accel_find_interned_string(str);
742
743 if (ret) {
744 zend_string_release(str);
745 return ret;
746 }
747 return str;
748 }
749
accel_use_shm_interned_strings(void)750 static void accel_use_shm_interned_strings(void)
751 {
752 HANDLE_BLOCK_INTERRUPTIONS();
753 SHM_UNPROTECT();
754 zend_shared_alloc_lock();
755
756 if (ZCSG(interned_strings).saved_top == NULL) {
757 accel_copy_permanent_strings(accel_new_interned_string);
758 } else {
759 ZCG(counted) = 1;
760 accel_copy_permanent_strings(accel_replace_string_by_shm_permanent);
761 ZCG(counted) = 0;
762 }
763 accel_interned_strings_save_state();
764
765 zend_shared_alloc_unlock();
766 SHM_PROTECT();
767 HANDLE_UNBLOCK_INTERRUPTIONS();
768 }
769
770 #ifndef ZEND_WIN32
kill_all_lockers(struct flock * mem_usage_check)771 static inline void kill_all_lockers(struct flock *mem_usage_check)
772 {
773 int success, tries;
774 /* so that other process won't try to force while we are busy cleaning up */
775 ZCSG(force_restart_time) = 0;
776 while (mem_usage_check->l_pid > 0) {
777 /* Try SIGTERM first, switch to SIGKILL if not successful. */
778 int signal = SIGTERM;
779 errno = 0;
780 success = 0;
781 tries = 10;
782
783 while (tries--) {
784 zend_accel_error(ACCEL_LOG_WARNING, "Attempting to kill locker %d", mem_usage_check->l_pid);
785 if (kill(mem_usage_check->l_pid, signal)) {
786 if (errno == ESRCH) {
787 /* Process died before the signal was sent */
788 success = 1;
789 zend_accel_error(ACCEL_LOG_WARNING, "Process %d died before SIGKILL was sent", mem_usage_check->l_pid);
790 }
791 break;
792 }
793 /* give it a chance to die */
794 usleep(20000);
795 if (kill(mem_usage_check->l_pid, 0)) {
796 if (errno == ESRCH) {
797 /* successfully killed locker, process no longer exists */
798 success = 1;
799 zend_accel_error(ACCEL_LOG_WARNING, "Killed locker %d", mem_usage_check->l_pid);
800 }
801 break;
802 }
803 usleep(10000);
804 /* If SIGTERM was not sufficient, use SIGKILL. */
805 signal = SIGKILL;
806 }
807 if (!success) {
808 /* errno is not ESRCH or we ran out of tries to kill the locker */
809 ZCSG(force_restart_time) = time(NULL); /* restore forced restart request */
810 /* cannot kill the locker, bail out with error */
811 zend_accel_error(ACCEL_LOG_ERROR, "Cannot kill process %d: %s!", mem_usage_check->l_pid, strerror(errno));
812 }
813
814 mem_usage_check->l_type = F_WRLCK;
815 mem_usage_check->l_whence = SEEK_SET;
816 mem_usage_check->l_start = 1;
817 mem_usage_check->l_len = 1;
818 mem_usage_check->l_pid = -1;
819 if (fcntl(lock_file, F_GETLK, mem_usage_check) == -1) {
820 zend_accel_error(ACCEL_LOG_DEBUG, "KLockers: %s (%d)", strerror(errno), errno);
821 break;
822 }
823
824 if (mem_usage_check->l_type == F_UNLCK || mem_usage_check->l_pid <= 0) {
825 break;
826 }
827 }
828 }
829 #endif
830
accel_is_inactive(void)831 static inline int accel_is_inactive(void)
832 {
833 #ifdef ZEND_WIN32
834 if (LOCKVAL(mem_usage) == 0) {
835 return SUCCESS;
836 }
837 #else
838 struct flock mem_usage_check;
839
840 mem_usage_check.l_type = F_WRLCK;
841 mem_usage_check.l_whence = SEEK_SET;
842 mem_usage_check.l_start = 1;
843 mem_usage_check.l_len = 1;
844 mem_usage_check.l_pid = -1;
845 if (fcntl(lock_file, F_GETLK, &mem_usage_check) == -1) {
846 zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC: %s (%d)", strerror(errno), errno);
847 return FAILURE;
848 }
849 if (mem_usage_check.l_type == F_UNLCK) {
850 return SUCCESS;
851 }
852
853 if (ZCG(accel_directives).force_restart_timeout
854 && ZCSG(force_restart_time)
855 && time(NULL) >= ZCSG(force_restart_time)) {
856 zend_accel_error(ACCEL_LOG_WARNING, "Forced restart at %ld (after " ZEND_LONG_FMT " seconds), locked by %d", (long)time(NULL), ZCG(accel_directives).force_restart_timeout, mem_usage_check.l_pid);
857 kill_all_lockers(&mem_usage_check);
858
859 return FAILURE; /* next request should be able to restart it */
860 }
861 #endif
862
863 return FAILURE;
864 }
865
zend_get_stream_timestamp(const char * filename,zend_stat_t * statbuf)866 static int zend_get_stream_timestamp(const char *filename, zend_stat_t *statbuf)
867 {
868 php_stream_wrapper *wrapper;
869 php_stream_statbuf stream_statbuf;
870 int ret, er;
871
872 if (!filename) {
873 return FAILURE;
874 }
875
876 wrapper = php_stream_locate_url_wrapper(filename, NULL, STREAM_LOCATE_WRAPPERS_ONLY);
877 if (!wrapper) {
878 return FAILURE;
879 }
880 if (!wrapper->wops || !wrapper->wops->url_stat) {
881 statbuf->st_mtime = 1;
882 return SUCCESS; /* anything other than 0 is considered to be a valid timestamp */
883 }
884
885 er = EG(error_reporting);
886 EG(error_reporting) = 0;
887 zend_try {
888 ret = wrapper->wops->url_stat(wrapper, (char*)filename, PHP_STREAM_URL_STAT_QUIET, &stream_statbuf, NULL);
889 } zend_catch {
890 ret = -1;
891 } zend_end_try();
892 EG(error_reporting) = er;
893
894 if (ret != 0) {
895 return FAILURE;
896 }
897
898 *statbuf = stream_statbuf.sb;
899 return SUCCESS;
900 }
901
902 #if ZEND_WIN32
zend_get_file_handle_timestamp_win(zend_file_handle * file_handle,size_t * size)903 static accel_time_t zend_get_file_handle_timestamp_win(zend_file_handle *file_handle, size_t *size)
904 {
905 static unsigned __int64 utc_base = 0;
906 static FILETIME utc_base_ft;
907 WIN32_FILE_ATTRIBUTE_DATA fdata;
908
909 if (!file_handle->opened_path) {
910 return 0;
911 }
912
913 if (!utc_base) {
914 SYSTEMTIME st;
915
916 st.wYear = 1970;
917 st.wMonth = 1;
918 st.wDay = 1;
919 st.wHour = 0;
920 st.wMinute = 0;
921 st.wSecond = 0;
922 st.wMilliseconds = 0;
923
924 SystemTimeToFileTime (&st, &utc_base_ft);
925 utc_base = (((unsigned __int64)utc_base_ft.dwHighDateTime) << 32) + utc_base_ft.dwLowDateTime;
926 }
927
928 if (file_handle->opened_path && GetFileAttributesEx(file_handle->opened_path->val, GetFileExInfoStandard, &fdata) != 0) {
929 unsigned __int64 ftime;
930
931 if (CompareFileTime (&fdata.ftLastWriteTime, &utc_base_ft) < 0) {
932 return 0;
933 }
934
935 ftime = (((unsigned __int64)fdata.ftLastWriteTime.dwHighDateTime) << 32) + fdata.ftLastWriteTime.dwLowDateTime - utc_base;
936 ftime /= 10000000L;
937
938 if (size) {
939 *size = (size_t)((((unsigned __int64)fdata.nFileSizeHigh) << 32) + (unsigned __int64)fdata.nFileSizeLow);
940 }
941 return (accel_time_t)ftime;
942 }
943 return 0;
944 }
945 #endif
946
zend_get_file_handle_timestamp(zend_file_handle * file_handle,size_t * size)947 accel_time_t zend_get_file_handle_timestamp(zend_file_handle *file_handle, size_t *size)
948 {
949 zend_stat_t statbuf;
950 #ifdef ZEND_WIN32
951 accel_time_t res;
952 #endif
953
954 if (sapi_module.get_stat &&
955 !EG(current_execute_data) &&
956 file_handle->filename == SG(request_info).path_translated) {
957
958 zend_stat_t *tmpbuf = sapi_module.get_stat();
959
960 if (tmpbuf) {
961 if (size) {
962 *size = tmpbuf->st_size;
963 }
964 return tmpbuf->st_mtime;
965 }
966 }
967
968 #ifdef ZEND_WIN32
969 res = zend_get_file_handle_timestamp_win(file_handle, size);
970 if (res) {
971 return res;
972 }
973 #endif
974
975 switch (file_handle->type) {
976 case ZEND_HANDLE_FP:
977 if (zend_fstat(fileno(file_handle->handle.fp), &statbuf) == -1) {
978 if (zend_get_stream_timestamp(file_handle->filename, &statbuf) != SUCCESS) {
979 return 0;
980 }
981 }
982 break;
983 case ZEND_HANDLE_FILENAME:
984 if (file_handle->opened_path) {
985 char *file_path = ZSTR_VAL(file_handle->opened_path);
986
987 if (is_stream_path(file_path)) {
988 if (zend_get_stream_timestamp(file_path, &statbuf) == SUCCESS) {
989 break;
990 }
991 }
992 if (VCWD_STAT(file_path, &statbuf) != -1) {
993 break;
994 }
995 }
996
997 if (zend_get_stream_timestamp(file_handle->filename, &statbuf) != SUCCESS) {
998 return 0;
999 }
1000 break;
1001 case ZEND_HANDLE_STREAM:
1002 {
1003 php_stream *stream = (php_stream *)file_handle->handle.stream.handle;
1004 php_stream_statbuf sb;
1005 int ret, er;
1006
1007 if (!stream ||
1008 !stream->ops ||
1009 !stream->ops->stat) {
1010 return 0;
1011 }
1012
1013 er = EG(error_reporting);
1014 EG(error_reporting) = 0;
1015 zend_try {
1016 ret = stream->ops->stat(stream, &sb);
1017 } zend_catch {
1018 ret = -1;
1019 } zend_end_try();
1020 EG(error_reporting) = er;
1021 if (ret != 0) {
1022 return 0;
1023 }
1024
1025 statbuf = sb.sb;
1026 }
1027 break;
1028
1029 default:
1030 return 0;
1031 }
1032
1033 if (size) {
1034 *size = statbuf.st_size;
1035 }
1036 return statbuf.st_mtime;
1037 }
1038
do_validate_timestamps(zend_persistent_script * persistent_script,zend_file_handle * file_handle)1039 static inline int do_validate_timestamps(zend_persistent_script *persistent_script, zend_file_handle *file_handle)
1040 {
1041 zend_file_handle ps_handle;
1042 zend_string *full_path_ptr = NULL;
1043
1044 /** check that the persistent script is indeed the same file we cached
1045 * (if part of the path is a symlink than it possible that the user will change it)
1046 * See bug #15140
1047 */
1048 if (file_handle->opened_path) {
1049 if (persistent_script->script.filename != file_handle->opened_path &&
1050 !zend_string_equal_content(persistent_script->script.filename, file_handle->opened_path)) {
1051 return FAILURE;
1052 }
1053 } else {
1054 full_path_ptr = accelerator_orig_zend_resolve_path(file_handle->filename, strlen(file_handle->filename));
1055 if (full_path_ptr &&
1056 persistent_script->script.filename != full_path_ptr &&
1057 !zend_string_equal_content(persistent_script->script.filename, full_path_ptr)) {
1058 zend_string_release_ex(full_path_ptr, 0);
1059 return FAILURE;
1060 }
1061 file_handle->opened_path = full_path_ptr;
1062 }
1063
1064 if (persistent_script->timestamp == 0) {
1065 if (full_path_ptr) {
1066 zend_string_release_ex(full_path_ptr, 0);
1067 file_handle->opened_path = NULL;
1068 }
1069 return FAILURE;
1070 }
1071
1072 if (zend_get_file_handle_timestamp(file_handle, NULL) == persistent_script->timestamp) {
1073 if (full_path_ptr) {
1074 zend_string_release_ex(full_path_ptr, 0);
1075 file_handle->opened_path = NULL;
1076 }
1077 return SUCCESS;
1078 }
1079 if (full_path_ptr) {
1080 zend_string_release_ex(full_path_ptr, 0);
1081 file_handle->opened_path = NULL;
1082 }
1083
1084 zend_stream_init_filename(&ps_handle, ZSTR_VAL(persistent_script->script.filename));
1085 ps_handle.opened_path = persistent_script->script.filename;
1086
1087 if (zend_get_file_handle_timestamp(&ps_handle, NULL) == persistent_script->timestamp) {
1088 return SUCCESS;
1089 }
1090
1091 return FAILURE;
1092 }
1093
validate_timestamp_and_record(zend_persistent_script * persistent_script,zend_file_handle * file_handle)1094 int validate_timestamp_and_record(zend_persistent_script *persistent_script, zend_file_handle *file_handle)
1095 {
1096 if (persistent_script->timestamp == 0) {
1097 return SUCCESS; /* Don't check timestamps of preloaded scripts */
1098 } else if (ZCG(accel_directives).revalidate_freq &&
1099 persistent_script->dynamic_members.revalidate >= ZCG(request_time)) {
1100 return SUCCESS;
1101 } else if (do_validate_timestamps(persistent_script, file_handle) == FAILURE) {
1102 return FAILURE;
1103 } else {
1104 persistent_script->dynamic_members.revalidate = ZCG(request_time) + ZCG(accel_directives).revalidate_freq;
1105 return SUCCESS;
1106 }
1107 }
1108
validate_timestamp_and_record_ex(zend_persistent_script * persistent_script,zend_file_handle * file_handle)1109 int validate_timestamp_and_record_ex(zend_persistent_script *persistent_script, zend_file_handle *file_handle)
1110 {
1111 int ret;
1112
1113 SHM_UNPROTECT();
1114 ret = validate_timestamp_and_record(persistent_script, file_handle);
1115 SHM_PROTECT();
1116
1117 return ret;
1118 }
1119
1120 /* Instead of resolving full real path name each time we need to identify file,
1121 * we create a key that consist from requested file name, current working
1122 * directory, current include_path, etc */
accel_make_persistent_key(const char * path,size_t path_length,int * key_len)1123 char *accel_make_persistent_key(const char *path, size_t path_length, int *key_len)
1124 {
1125 int key_length;
1126
1127 /* CWD and include_path don't matter for absolute file names and streams */
1128 if (IS_ABSOLUTE_PATH(path, path_length)) {
1129 /* pass */
1130 ZCG(key_len) = 0;
1131 } else if (UNEXPECTED(is_stream_path(path))) {
1132 if (!is_cacheable_stream_path(path)) {
1133 return NULL;
1134 }
1135 /* pass */
1136 ZCG(key_len) = 0;
1137 } else if (UNEXPECTED(!ZCG(accel_directives).use_cwd)) {
1138 /* pass */
1139 ZCG(key_len) = 0;
1140 } else {
1141 const char *include_path = NULL, *cwd = NULL;
1142 int include_path_len = 0, cwd_len = 0;
1143 zend_string *parent_script = NULL;
1144 size_t parent_script_len = 0;
1145
1146 if (EXPECTED(ZCG(cwd_key_len))) {
1147 cwd = ZCG(cwd_key);
1148 cwd_len = ZCG(cwd_key_len);
1149 } else {
1150 zend_string *cwd_str = accel_getcwd();
1151
1152 if (UNEXPECTED(!cwd_str)) {
1153 /* we don't handle this well for now. */
1154 zend_accel_error(ACCEL_LOG_INFO, "getcwd() failed for '%s' (%d), please try to set opcache.use_cwd to 0 in ini file", path, errno);
1155 return NULL;
1156 }
1157 cwd = ZSTR_VAL(cwd_str);
1158 cwd_len = ZSTR_LEN(cwd_str);
1159 if (ZCG(cwd_check)) {
1160 ZCG(cwd_check) = 0;
1161 if (ZCG(accelerator_enabled)) {
1162
1163 zend_string *str = accel_find_interned_string(cwd_str);
1164 if (!str) {
1165 HANDLE_BLOCK_INTERRUPTIONS();
1166 SHM_UNPROTECT();
1167 zend_shared_alloc_lock();
1168 str = accel_new_interned_string(zend_string_copy(cwd_str));
1169 if (str == cwd_str) {
1170 zend_string_release_ex(str, 0);
1171 str = NULL;
1172 }
1173 zend_shared_alloc_unlock();
1174 SHM_PROTECT();
1175 HANDLE_UNBLOCK_INTERRUPTIONS();
1176 }
1177 if (str) {
1178 char buf[32];
1179 char *res = zend_print_long_to_buf(buf + sizeof(buf) - 1, STRTAB_STR_TO_POS(&ZCSG(interned_strings), str));
1180
1181 cwd_len = ZCG(cwd_key_len) = buf + sizeof(buf) - 1 - res;
1182 cwd = ZCG(cwd_key);
1183 memcpy(ZCG(cwd_key), res, cwd_len + 1);
1184 } else {
1185 return NULL;
1186 }
1187 } else {
1188 return NULL;
1189 }
1190 }
1191 }
1192
1193 if (EXPECTED(ZCG(include_path_key_len))) {
1194 include_path = ZCG(include_path_key);
1195 include_path_len = ZCG(include_path_key_len);
1196 } else if (!ZCG(include_path) || ZSTR_LEN(ZCG(include_path)) == 0) {
1197 include_path = "";
1198 include_path_len = 0;
1199 } else {
1200 include_path = ZSTR_VAL(ZCG(include_path));
1201 include_path_len = ZSTR_LEN(ZCG(include_path));
1202
1203 if (ZCG(include_path_check)) {
1204 ZCG(include_path_check) = 0;
1205 if (ZCG(accelerator_enabled)) {
1206
1207 zend_string *str = accel_find_interned_string(ZCG(include_path));
1208 if (!str) {
1209 HANDLE_BLOCK_INTERRUPTIONS();
1210 SHM_UNPROTECT();
1211 zend_shared_alloc_lock();
1212 str = accel_new_interned_string(zend_string_copy(ZCG(include_path)));
1213 if (str == ZCG(include_path)) {
1214 str = NULL;
1215 }
1216 zend_shared_alloc_unlock();
1217 SHM_PROTECT();
1218 HANDLE_UNBLOCK_INTERRUPTIONS();
1219 }
1220 if (str) {
1221 char buf[32];
1222 char *res = zend_print_long_to_buf(buf + sizeof(buf) - 1, STRTAB_STR_TO_POS(&ZCSG(interned_strings), str));
1223
1224 include_path_len = ZCG(include_path_key_len) = buf + sizeof(buf) - 1 - res;
1225 include_path = ZCG(include_path_key);
1226 memcpy(ZCG(include_path_key), res, include_path_len + 1);
1227 } else {
1228 return NULL;
1229 }
1230 } else {
1231 return NULL;
1232 }
1233 }
1234 }
1235
1236 /* Calculate key length */
1237 if (UNEXPECTED((size_t)(cwd_len + path_length + include_path_len + 2) >= sizeof(ZCG(key)))) {
1238 return NULL;
1239 }
1240
1241 /* Generate key
1242 * Note - the include_path must be the last element in the key,
1243 * since in itself, it may include colons (which we use to separate
1244 * different components of the key)
1245 */
1246 memcpy(ZCG(key), path, path_length);
1247 ZCG(key)[path_length] = ':';
1248 key_length = path_length + 1;
1249 memcpy(ZCG(key) + key_length, cwd, cwd_len);
1250 key_length += cwd_len;
1251
1252 if (include_path_len) {
1253 ZCG(key)[key_length] = ':';
1254 key_length += 1;
1255 memcpy(ZCG(key) + key_length, include_path, include_path_len);
1256 key_length += include_path_len;
1257 }
1258
1259 /* Here we add to the key the parent script directory,
1260 * since fopen_wrappers from version 4.0.7 use current script's path
1261 * in include path too.
1262 */
1263 if (EXPECTED(EG(current_execute_data)) &&
1264 EXPECTED((parent_script = zend_get_executed_filename_ex()) != NULL)) {
1265
1266 parent_script_len = ZSTR_LEN(parent_script);
1267 while ((--parent_script_len > 0) && !IS_SLASH(ZSTR_VAL(parent_script)[parent_script_len]));
1268
1269 if (UNEXPECTED((size_t)(key_length + parent_script_len + 1) >= sizeof(ZCG(key)))) {
1270 return NULL;
1271 }
1272 ZCG(key)[key_length] = ':';
1273 key_length += 1;
1274 memcpy(ZCG(key) + key_length, ZSTR_VAL(parent_script), parent_script_len);
1275 key_length += parent_script_len;
1276 }
1277 ZCG(key)[key_length] = '\0';
1278 *key_len = ZCG(key_len) = key_length;
1279 return ZCG(key);
1280 }
1281
1282 /* not use_cwd */
1283 *key_len = path_length;
1284 return (char*)path;
1285 }
1286
zend_accel_invalidate(const char * filename,size_t filename_len,zend_bool force)1287 int zend_accel_invalidate(const char *filename, size_t filename_len, zend_bool force)
1288 {
1289 zend_string *realpath;
1290 zend_persistent_script *persistent_script;
1291
1292 if (!ZCG(accelerator_enabled) || accelerator_shm_read_lock() != SUCCESS) {
1293 return FAILURE;
1294 }
1295
1296 realpath = accelerator_orig_zend_resolve_path(filename, filename_len);
1297
1298 if (!realpath) {
1299 return FAILURE;
1300 }
1301
1302 if (ZCG(accel_directives).file_cache) {
1303 zend_file_cache_invalidate(realpath);
1304 }
1305
1306 persistent_script = zend_accel_hash_find(&ZCSG(hash), realpath);
1307 if (persistent_script && !persistent_script->corrupted) {
1308 zend_file_handle file_handle;
1309 zend_stream_init_filename(&file_handle, ZSTR_VAL(realpath));
1310 file_handle.opened_path = realpath;
1311
1312 if (force ||
1313 !ZCG(accel_directives).validate_timestamps ||
1314 do_validate_timestamps(persistent_script, &file_handle) == FAILURE) {
1315 HANDLE_BLOCK_INTERRUPTIONS();
1316 SHM_UNPROTECT();
1317 zend_shared_alloc_lock();
1318 if (!persistent_script->corrupted) {
1319 persistent_script->corrupted = 1;
1320 persistent_script->timestamp = 0;
1321 ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
1322 if (ZSMMG(memory_exhausted)) {
1323 zend_accel_restart_reason reason =
1324 zend_accel_hash_is_full(&ZCSG(hash)) ? ACCEL_RESTART_HASH : ACCEL_RESTART_OOM;
1325 zend_accel_schedule_restart_if_necessary(reason);
1326 }
1327 }
1328 zend_shared_alloc_unlock();
1329 SHM_PROTECT();
1330 HANDLE_UNBLOCK_INTERRUPTIONS();
1331 }
1332 }
1333
1334 accelerator_shm_read_unlock();
1335 zend_string_release_ex(realpath, 0);
1336
1337 return SUCCESS;
1338 }
1339
1340 /* Adds another key for existing cached script */
zend_accel_add_key(const char * key,unsigned int key_length,zend_accel_hash_entry * bucket)1341 static void zend_accel_add_key(const char *key, unsigned int key_length, zend_accel_hash_entry *bucket)
1342 {
1343 if (!zend_accel_hash_str_find(&ZCSG(hash), key, key_length)) {
1344 if (zend_accel_hash_is_full(&ZCSG(hash))) {
1345 zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1346 ZSMMG(memory_exhausted) = 1;
1347 zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
1348 } else {
1349 char *new_key = zend_shared_alloc(key_length + 1);
1350 if (new_key) {
1351 memcpy(new_key, key, key_length + 1);
1352 if (zend_accel_hash_update(&ZCSG(hash), new_key, key_length, 1, bucket)) {
1353 zend_accel_error(ACCEL_LOG_INFO, "Added key '%s'", new_key);
1354 }
1355 } else {
1356 zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
1357 }
1358 }
1359 }
1360 }
1361
is_phar_file(zend_string * filename)1362 static zend_always_inline zend_bool is_phar_file(zend_string *filename)
1363 {
1364 return filename && ZSTR_LEN(filename) >= sizeof(".phar") &&
1365 !memcmp(ZSTR_VAL(filename) + ZSTR_LEN(filename) - (sizeof(".phar")-1), ".phar", sizeof(".phar")-1) &&
1366 !strstr(ZSTR_VAL(filename), "://");
1367 }
1368
store_script_in_file_cache(zend_persistent_script * new_persistent_script)1369 static zend_persistent_script *store_script_in_file_cache(zend_persistent_script *new_persistent_script)
1370 {
1371 uint32_t memory_used;
1372
1373 zend_shared_alloc_init_xlat_table();
1374
1375 /* Calculate the required memory size */
1376 memory_used = zend_accel_script_persist_calc(new_persistent_script, NULL, 0, 0);
1377
1378 /* Allocate memory block */
1379 #if defined(__AVX__) || defined(__SSE2__)
1380 /* Align to 64-byte boundary */
1381 ZCG(mem) = zend_arena_alloc(&CG(arena), memory_used + 64);
1382 ZCG(mem) = (void*)(((zend_uintptr_t)ZCG(mem) + 63L) & ~63L);
1383 #elif ZEND_MM_ALIGNMENT < 8
1384 /* Align to 8-byte boundary */
1385 ZCG(mem) = zend_arena_alloc(&CG(arena), memory_used + 8);
1386 ZCG(mem) = (void*)(((zend_uintptr_t)ZCG(mem) + 7L) & ~7L);
1387 #else
1388 ZCG(mem) = zend_arena_alloc(&CG(arena), memory_used);
1389 #endif
1390
1391 zend_shared_alloc_clear_xlat_table();
1392
1393 /* Copy into memory block */
1394 new_persistent_script = zend_accel_script_persist(new_persistent_script, NULL, 0, 0);
1395
1396 zend_shared_alloc_destroy_xlat_table();
1397
1398 new_persistent_script->is_phar = is_phar_file(new_persistent_script->script.filename);
1399
1400 /* Consistency check */
1401 if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
1402 zend_accel_error(
1403 ((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
1404 "Internal error: wrong size calculation: %s start=" ZEND_ADDR_FMT ", end=" ZEND_ADDR_FMT ", real=" ZEND_ADDR_FMT "\n",
1405 ZSTR_VAL(new_persistent_script->script.filename),
1406 (size_t)new_persistent_script->mem,
1407 (size_t)((char *)new_persistent_script->mem + new_persistent_script->size),
1408 (size_t)ZCG(mem));
1409 }
1410
1411 new_persistent_script->dynamic_members.checksum = zend_accel_script_checksum(new_persistent_script);
1412
1413 zend_file_cache_script_store(new_persistent_script, 0);
1414
1415 return new_persistent_script;
1416 }
1417
cache_script_in_file_cache(zend_persistent_script * new_persistent_script,int * from_shared_memory)1418 static zend_persistent_script *cache_script_in_file_cache(zend_persistent_script *new_persistent_script, int *from_shared_memory)
1419 {
1420 uint32_t orig_compiler_options;
1421
1422 /* Check if script may be stored in shared memory */
1423 if (!zend_accel_script_persistable(new_persistent_script)) {
1424 return new_persistent_script;
1425 }
1426
1427 orig_compiler_options = CG(compiler_options);
1428 CG(compiler_options) |= ZEND_COMPILE_WITH_FILE_CACHE;
1429 if (!zend_optimize_script(&new_persistent_script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level)) {
1430 CG(compiler_options) = orig_compiler_options;
1431 return new_persistent_script;
1432 }
1433 CG(compiler_options) = orig_compiler_options;
1434
1435 *from_shared_memory = 1;
1436 return store_script_in_file_cache(new_persistent_script);
1437 }
1438
cache_script_in_shared_memory(zend_persistent_script * new_persistent_script,const char * key,unsigned int key_length,int * from_shared_memory)1439 static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_script *new_persistent_script, const char *key, unsigned int key_length, int *from_shared_memory)
1440 {
1441 zend_accel_hash_entry *bucket;
1442 uint32_t memory_used;
1443 uint32_t orig_compiler_options;
1444
1445 /* Check if script may be stored in shared memory */
1446 if (!zend_accel_script_persistable(new_persistent_script)) {
1447 return new_persistent_script;
1448 }
1449
1450 orig_compiler_options = CG(compiler_options);
1451 if (ZCG(accel_directives).file_cache) {
1452 CG(compiler_options) |= ZEND_COMPILE_WITH_FILE_CACHE;
1453 }
1454 if (!zend_optimize_script(&new_persistent_script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level)) {
1455 CG(compiler_options) = orig_compiler_options;
1456 return new_persistent_script;
1457 }
1458 CG(compiler_options) = orig_compiler_options;
1459
1460 /* exclusive lock */
1461 zend_shared_alloc_lock();
1462
1463 /* Check if we still need to put the file into the cache (may be it was
1464 * already stored by another process. This final check is done under
1465 * exclusive lock) */
1466 bucket = zend_accel_hash_find_entry(&ZCSG(hash), new_persistent_script->script.filename);
1467 if (bucket) {
1468 zend_persistent_script *existing_persistent_script = (zend_persistent_script *)bucket->data;
1469
1470 if (!existing_persistent_script->corrupted) {
1471 if (key &&
1472 (!ZCG(accel_directives).validate_timestamps ||
1473 (new_persistent_script->timestamp == existing_persistent_script->timestamp))) {
1474 zend_accel_add_key(key, key_length, bucket);
1475 }
1476 zend_shared_alloc_unlock();
1477 return new_persistent_script;
1478 }
1479 }
1480
1481 if (zend_accel_hash_is_full(&ZCSG(hash))) {
1482 zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1483 ZSMMG(memory_exhausted) = 1;
1484 zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
1485 zend_shared_alloc_unlock();
1486 if (ZCG(accel_directives).file_cache) {
1487 new_persistent_script = store_script_in_file_cache(new_persistent_script);
1488 *from_shared_memory = 1;
1489 }
1490 return new_persistent_script;
1491 }
1492
1493 zend_shared_alloc_init_xlat_table();
1494
1495 /* Calculate the required memory size */
1496 memory_used = zend_accel_script_persist_calc(new_persistent_script, key, key_length, 1);
1497
1498 /* Allocate shared memory */
1499 #if defined(__AVX__) || defined(__SSE2__)
1500 /* Align to 64-byte boundary */
1501 ZCG(mem) = zend_shared_alloc(memory_used + 64);
1502 if (ZCG(mem)) {
1503 ZCG(mem) = (void*)(((zend_uintptr_t)ZCG(mem) + 63L) & ~63L);
1504 #if defined(__x86_64__)
1505 memset(ZCG(mem), 0, memory_used);
1506 #elif defined(__AVX__)
1507 {
1508 char *p = (char*)ZCG(mem);
1509 char *end = p + memory_used;
1510 __m256i ymm0 = _mm256_setzero_si256();
1511
1512 while (p < end) {
1513 _mm256_store_si256((__m256i*)p, ymm0);
1514 _mm256_store_si256((__m256i*)(p+32), ymm0);
1515 p += 64;
1516 }
1517 }
1518 #else
1519 {
1520 char *p = (char*)ZCG(mem);
1521 char *end = p + memory_used;
1522 __m128i xmm0 = _mm_setzero_si128();
1523
1524 while (p < end) {
1525 _mm_store_si128((__m128i*)p, xmm0);
1526 _mm_store_si128((__m128i*)(p+16), xmm0);
1527 _mm_store_si128((__m128i*)(p+32), xmm0);
1528 _mm_store_si128((__m128i*)(p+48), xmm0);
1529 p += 64;
1530 }
1531 }
1532 #endif
1533 }
1534 #else
1535 ZCG(mem) = zend_shared_alloc(memory_used);
1536 if (ZCG(mem)) {
1537 memset(ZCG(mem), 0, memory_used);
1538 }
1539 #endif
1540 if (!ZCG(mem)) {
1541 zend_shared_alloc_destroy_xlat_table();
1542 zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
1543 zend_shared_alloc_unlock();
1544 if (ZCG(accel_directives).file_cache) {
1545 new_persistent_script = store_script_in_file_cache(new_persistent_script);
1546 *from_shared_memory = 1;
1547 }
1548 return new_persistent_script;
1549 }
1550
1551 zend_shared_alloc_clear_xlat_table();
1552
1553 /* Copy into shared memory */
1554 new_persistent_script = zend_accel_script_persist(new_persistent_script, &key, key_length, 1);
1555
1556 zend_shared_alloc_destroy_xlat_table();
1557
1558 new_persistent_script->is_phar = is_phar_file(new_persistent_script->script.filename);
1559
1560 /* Consistency check */
1561 if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
1562 zend_accel_error(
1563 ((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
1564 "Internal error: wrong size calculation: %s start=" ZEND_ADDR_FMT ", end=" ZEND_ADDR_FMT ", real=" ZEND_ADDR_FMT "\n",
1565 ZSTR_VAL(new_persistent_script->script.filename),
1566 (size_t)new_persistent_script->mem,
1567 (size_t)((char *)new_persistent_script->mem + new_persistent_script->size),
1568 (size_t)ZCG(mem));
1569 }
1570
1571 new_persistent_script->dynamic_members.checksum = zend_accel_script_checksum(new_persistent_script);
1572
1573 /* store script structure in the hash table */
1574 bucket = zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL(new_persistent_script->script.filename), ZSTR_LEN(new_persistent_script->script.filename), 0, new_persistent_script);
1575 if (bucket) {
1576 zend_accel_error(ACCEL_LOG_INFO, "Cached script '%s'", ZSTR_VAL(new_persistent_script->script.filename));
1577 if (key &&
1578 /* key may contain non-persistent PHAR aliases (see issues #115 and #149) */
1579 memcmp(key, "phar://", sizeof("phar://") - 1) != 0 &&
1580 (ZSTR_LEN(new_persistent_script->script.filename) != key_length ||
1581 memcmp(ZSTR_VAL(new_persistent_script->script.filename), key, key_length) != 0)) {
1582 /* link key to the same persistent script in hash table */
1583 if (zend_accel_hash_update(&ZCSG(hash), key, key_length, 1, bucket)) {
1584 zend_accel_error(ACCEL_LOG_INFO, "Added key '%s'", key);
1585 } else {
1586 zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1587 ZSMMG(memory_exhausted) = 1;
1588 zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
1589 }
1590 }
1591 }
1592
1593 new_persistent_script->dynamic_members.memory_consumption = ZEND_ALIGNED_SIZE(new_persistent_script->size);
1594
1595 zend_shared_alloc_unlock();
1596
1597 if (ZCG(accel_directives).file_cache) {
1598 SHM_PROTECT();
1599 zend_file_cache_script_store(new_persistent_script, 1);
1600 SHM_UNPROTECT();
1601 }
1602
1603 *from_shared_memory = 1;
1604 return new_persistent_script;
1605 }
1606
1607 static const struct jit_auto_global_info
1608 {
1609 const char *name;
1610 size_t len;
1611 } jit_auto_globals_info[] = {
1612 { "_SERVER", sizeof("_SERVER")-1},
1613 { "_ENV", sizeof("_ENV")-1},
1614 { "_REQUEST", sizeof("_REQUEST")-1},
1615 { "GLOBALS", sizeof("GLOBALS")-1},
1616 };
1617
1618 static zend_string *jit_auto_globals_str[4];
1619
zend_accel_get_auto_globals(void)1620 static int zend_accel_get_auto_globals(void)
1621 {
1622 int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0]));
1623 int n = 1;
1624 int mask = 0;
1625
1626 for (i = 0; i < ag_size ; i++) {
1627 if (zend_hash_exists(&EG(symbol_table), jit_auto_globals_str[i])) {
1628 mask |= n;
1629 }
1630 n += n;
1631 }
1632 return mask;
1633 }
1634
zend_accel_get_auto_globals_no_jit(void)1635 static int zend_accel_get_auto_globals_no_jit(void)
1636 {
1637 if (zend_hash_exists(&EG(symbol_table), jit_auto_globals_str[3])) {
1638 return 8;
1639 }
1640 return 0;
1641 }
1642
zend_accel_set_auto_globals(int mask)1643 static void zend_accel_set_auto_globals(int mask)
1644 {
1645 int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0]));
1646 int n = 1;
1647
1648 for (i = 0; i < ag_size ; i++) {
1649 if ((mask & n) && !(ZCG(auto_globals_mask) & n)) {
1650 ZCG(auto_globals_mask) |= n;
1651 zend_is_auto_global(jit_auto_globals_str[i]);
1652 }
1653 n += n;
1654 }
1655 }
1656
zend_accel_init_auto_globals(void)1657 static void zend_accel_init_auto_globals(void)
1658 {
1659 int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0]));
1660
1661 for (i = 0; i < ag_size ; i++) {
1662 jit_auto_globals_str[i] = zend_string_init(jit_auto_globals_info[i].name, jit_auto_globals_info[i].len, 1);
1663 zend_string_hash_val(jit_auto_globals_str[i]);
1664 jit_auto_globals_str[i] = accel_new_interned_string(jit_auto_globals_str[i]);
1665 }
1666 }
1667
opcache_compile_file(zend_file_handle * file_handle,int type,const char * key,zend_op_array ** op_array_p)1668 static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handle, int type, const char *key, zend_op_array **op_array_p)
1669 {
1670 zend_persistent_script *new_persistent_script;
1671 uint32_t orig_functions_count, orig_class_count;
1672 zend_op_array *orig_active_op_array;
1673 zval orig_user_error_handler;
1674 zend_op_array *op_array;
1675 int do_bailout = 0;
1676 accel_time_t timestamp = 0;
1677 uint32_t orig_compiler_options = 0;
1678
1679 /* Try to open file */
1680 if (file_handle->type == ZEND_HANDLE_FILENAME) {
1681 if (accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle) != SUCCESS) {
1682 *op_array_p = NULL;
1683 if (!EG(exception)) {
1684 if (type == ZEND_REQUIRE) {
1685 zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
1686 zend_bailout();
1687 } else {
1688 zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
1689 }
1690 }
1691 return NULL;
1692 }
1693 }
1694
1695 /* check blacklist right after ensuring that file was opened */
1696 if (file_handle->opened_path && zend_accel_blacklist_is_blacklisted(&accel_blacklist, ZSTR_VAL(file_handle->opened_path), ZSTR_LEN(file_handle->opened_path))) {
1697 SHM_UNPROTECT();
1698 ZCSG(blacklist_misses)++;
1699 SHM_PROTECT();
1700 *op_array_p = accelerator_orig_compile_file(file_handle, type);
1701 return NULL;
1702 }
1703
1704 if (ZCG(accel_directives).validate_timestamps ||
1705 ZCG(accel_directives).file_update_protection ||
1706 ZCG(accel_directives).max_file_size > 0) {
1707 size_t size = 0;
1708
1709 /* Obtain the file timestamps, *before* actually compiling them,
1710 * otherwise we have a race-condition.
1711 */
1712 timestamp = zend_get_file_handle_timestamp(file_handle, ZCG(accel_directives).max_file_size > 0 ? &size : NULL);
1713
1714 /* If we can't obtain a timestamp (that means file is possibly socket)
1715 * we won't cache it
1716 */
1717 if (timestamp == 0) {
1718 *op_array_p = accelerator_orig_compile_file(file_handle, type);
1719 return NULL;
1720 }
1721
1722 /* check if file is too new (may be it's not written completely yet) */
1723 if (ZCG(accel_directives).file_update_protection &&
1724 ((accel_time_t)(ZCG(request_time) - ZCG(accel_directives).file_update_protection) < timestamp)) {
1725 *op_array_p = accelerator_orig_compile_file(file_handle, type);
1726 return NULL;
1727 }
1728
1729 if (ZCG(accel_directives).max_file_size > 0 && size > (size_t)ZCG(accel_directives).max_file_size) {
1730 SHM_UNPROTECT();
1731 ZCSG(blacklist_misses)++;
1732 SHM_PROTECT();
1733 *op_array_p = accelerator_orig_compile_file(file_handle, type);
1734 return NULL;
1735 }
1736 }
1737
1738 /* Save the original values for the op_array, function table and class table */
1739 orig_active_op_array = CG(active_op_array);
1740 orig_functions_count = EG(function_table)->nNumUsed;
1741 orig_class_count = EG(class_table)->nNumUsed;
1742 ZVAL_COPY_VALUE(&orig_user_error_handler, &EG(user_error_handler));
1743
1744 /* Override them with ours */
1745 ZVAL_UNDEF(&EG(user_error_handler));
1746
1747 zend_try {
1748 orig_compiler_options = CG(compiler_options);
1749 CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
1750 CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES;
1751 CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING;
1752 CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION;
1753 CG(compiler_options) |= ZEND_COMPILE_IGNORE_OTHER_FILES;
1754 if (ZCG(accel_directives).file_cache) {
1755 CG(compiler_options) |= ZEND_COMPILE_WITH_FILE_CACHE;
1756 }
1757 op_array = *op_array_p = accelerator_orig_compile_file(file_handle, type);
1758 CG(compiler_options) = orig_compiler_options;
1759 } zend_catch {
1760 op_array = NULL;
1761 do_bailout = 1;
1762 CG(compiler_options) = orig_compiler_options;
1763 } zend_end_try();
1764
1765 /* Restore originals */
1766 CG(active_op_array) = orig_active_op_array;
1767 EG(user_error_handler) = orig_user_error_handler;
1768
1769 if (!op_array) {
1770 /* compilation failed */
1771 if (do_bailout) {
1772 zend_bailout();
1773 }
1774 return NULL;
1775 }
1776
1777 /* Build the persistent_script structure.
1778 Here we aren't sure we would store it, but we will need it
1779 further anyway.
1780 */
1781 new_persistent_script = create_persistent_script();
1782 new_persistent_script->script.main_op_array = *op_array;
1783 zend_accel_move_user_functions(CG(function_table), CG(function_table)->nNumUsed - orig_functions_count, &new_persistent_script->script);
1784 zend_accel_move_user_classes(CG(class_table), CG(class_table)->nNumUsed - orig_class_count, &new_persistent_script->script);
1785 new_persistent_script->script.first_early_binding_opline =
1786 (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) ?
1787 zend_build_delayed_early_binding_list(op_array) :
1788 (uint32_t)-1;
1789
1790 efree(op_array); /* we have valid persistent_script, so it's safe to free op_array */
1791
1792 /* Fill in the ping_auto_globals_mask for the new script. If jit for auto globals is enabled we
1793 will have to ping the used auto global variables before execution */
1794 if (PG(auto_globals_jit)) {
1795 new_persistent_script->ping_auto_globals_mask = zend_accel_get_auto_globals();
1796 } else {
1797 new_persistent_script->ping_auto_globals_mask = zend_accel_get_auto_globals_no_jit();
1798 }
1799
1800 if (ZCG(accel_directives).validate_timestamps) {
1801 /* Obtain the file timestamps, *before* actually compiling them,
1802 * otherwise we have a race-condition.
1803 */
1804 new_persistent_script->timestamp = timestamp;
1805 new_persistent_script->dynamic_members.revalidate = ZCG(request_time) + ZCG(accel_directives).revalidate_freq;
1806 }
1807
1808 if (file_handle->opened_path) {
1809 new_persistent_script->script.filename = zend_string_copy(file_handle->opened_path);
1810 } else {
1811 new_persistent_script->script.filename = zend_string_init(file_handle->filename, strlen(file_handle->filename), 0);
1812 }
1813 zend_string_hash_val(new_persistent_script->script.filename);
1814
1815 /* Now persistent_script structure is ready in process memory */
1816 return new_persistent_script;
1817 }
1818
file_cache_compile_file(zend_file_handle * file_handle,int type)1819 zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int type)
1820 {
1821 zend_persistent_script *persistent_script;
1822 zend_op_array *op_array = NULL;
1823 int from_memory; /* if the script we've got is stored in SHM */
1824
1825 if (is_stream_path(file_handle->filename) &&
1826 !is_cacheable_stream_path(file_handle->filename)) {
1827 return accelerator_orig_compile_file(file_handle, type);
1828 }
1829
1830 if (!file_handle->opened_path) {
1831 if (file_handle->type == ZEND_HANDLE_FILENAME &&
1832 accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle) == FAILURE) {
1833 if (!EG(exception)) {
1834 if (type == ZEND_REQUIRE) {
1835 zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
1836 zend_bailout();
1837 } else {
1838 zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
1839 }
1840 }
1841 return NULL;
1842 }
1843 }
1844
1845 HANDLE_BLOCK_INTERRUPTIONS();
1846 SHM_UNPROTECT();
1847 persistent_script = zend_file_cache_script_load(file_handle);
1848 SHM_PROTECT();
1849 HANDLE_UNBLOCK_INTERRUPTIONS();
1850 if (persistent_script) {
1851 /* see bug #15471 (old BTS) */
1852 if (persistent_script->script.filename) {
1853 if (!EG(current_execute_data) || !EG(current_execute_data)->opline ||
1854 !EG(current_execute_data)->func ||
1855 !ZEND_USER_CODE(EG(current_execute_data)->func->common.type) ||
1856 EG(current_execute_data)->opline->opcode != ZEND_INCLUDE_OR_EVAL ||
1857 (EG(current_execute_data)->opline->extended_value != ZEND_INCLUDE_ONCE &&
1858 EG(current_execute_data)->opline->extended_value != ZEND_REQUIRE_ONCE)) {
1859 if (zend_hash_add_empty_element(&EG(included_files), persistent_script->script.filename) != NULL) {
1860 /* ext/phar has to load phar's metadata into memory */
1861 if (persistent_script->is_phar) {
1862 php_stream_statbuf ssb;
1863 char *fname = emalloc(sizeof("phar://") + ZSTR_LEN(persistent_script->script.filename));
1864
1865 memcpy(fname, "phar://", sizeof("phar://") - 1);
1866 memcpy(fname + sizeof("phar://") - 1, ZSTR_VAL(persistent_script->script.filename), ZSTR_LEN(persistent_script->script.filename) + 1);
1867 php_stream_stat_path(fname, &ssb);
1868 efree(fname);
1869 }
1870 }
1871 }
1872 }
1873 zend_file_handle_dtor(file_handle);
1874
1875 if (persistent_script->ping_auto_globals_mask) {
1876 zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask);
1877 }
1878
1879 return zend_accel_load_script(persistent_script, 1);
1880 }
1881
1882 persistent_script = opcache_compile_file(file_handle, type, NULL, &op_array);
1883
1884 if (persistent_script) {
1885 from_memory = 0;
1886 persistent_script = cache_script_in_file_cache(persistent_script, &from_memory);
1887 return zend_accel_load_script(persistent_script, from_memory);
1888 }
1889
1890 return op_array;
1891 }
1892
check_persistent_script_access(zend_persistent_script * persistent_script)1893 int check_persistent_script_access(zend_persistent_script *persistent_script)
1894 {
1895 char *phar_path, *ptr;
1896 int ret;
1897 if ((ZSTR_LEN(persistent_script->script.filename)<sizeof("phar://.phar")) ||
1898 memcmp(ZSTR_VAL(persistent_script->script.filename), "phar://", sizeof("phar://")-1)) {
1899
1900 return access(ZSTR_VAL(persistent_script->script.filename), R_OK) != 0;
1901
1902 } else {
1903 /* we got a cached file from .phar, so we have to strip prefix and path inside .phar to check access() */
1904 phar_path = estrdup(ZSTR_VAL(persistent_script->script.filename)+sizeof("phar://")-1);
1905 if ((ptr = strstr(phar_path, ".phar/")) != NULL)
1906 {
1907 *(ptr+sizeof(".phar/")-2) = 0; /* strip path inside .phar file */
1908 }
1909 ret = access(phar_path, R_OK) != 0;
1910 efree(phar_path);
1911 return ret;
1912 }
1913 }
1914
1915
1916 /* zend_compile() replacement */
persistent_compile_file(zend_file_handle * file_handle,int type)1917 zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
1918 {
1919 zend_persistent_script *persistent_script = NULL;
1920 char *key = NULL;
1921 int key_length;
1922 int from_shared_memory; /* if the script we've got is stored in SHM */
1923
1924 if (!file_handle->filename || !ZCG(accelerator_enabled)) {
1925 /* The Accelerator is disabled, act as if without the Accelerator */
1926 ZCG(cache_opline) = NULL;
1927 ZCG(cache_persistent_script) = NULL;
1928 if (file_handle->filename
1929 && ZCG(accel_directives).file_cache
1930 && ZCG(enabled) && accel_startup_ok) {
1931 return file_cache_compile_file(file_handle, type);
1932 }
1933 return accelerator_orig_compile_file(file_handle, type);
1934 } else if (file_cache_only) {
1935 ZCG(cache_opline) = NULL;
1936 ZCG(cache_persistent_script) = NULL;
1937 return file_cache_compile_file(file_handle, type);
1938 } else if (!ZCG(accelerator_enabled) ||
1939 (ZCSG(restart_in_progress) && accel_restart_is_active())) {
1940 if (ZCG(accel_directives).file_cache) {
1941 return file_cache_compile_file(file_handle, type);
1942 }
1943 ZCG(cache_opline) = NULL;
1944 ZCG(cache_persistent_script) = NULL;
1945 return accelerator_orig_compile_file(file_handle, type);
1946 }
1947
1948 /* In case this callback is called from include_once, require_once or it's
1949 * a main FastCGI request, the key must be already calculated, and cached
1950 * persistent script already found */
1951 if (ZCG(cache_persistent_script) &&
1952 ((!EG(current_execute_data) &&
1953 file_handle->filename == SG(request_info).path_translated &&
1954 ZCG(cache_opline) == NULL) ||
1955 (EG(current_execute_data) &&
1956 EG(current_execute_data)->func &&
1957 ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
1958 ZCG(cache_opline) == EG(current_execute_data)->opline))) {
1959
1960 persistent_script = ZCG(cache_persistent_script);
1961 if (ZCG(key_len)) {
1962 key = ZCG(key);
1963 key_length = ZCG(key_len);
1964 }
1965
1966 } else {
1967 if (!ZCG(accel_directives).revalidate_path) {
1968 /* try to find cached script by key */
1969 key = accel_make_persistent_key(file_handle->filename, strlen(file_handle->filename), &key_length);
1970 if (!key) {
1971 ZCG(cache_opline) = NULL;
1972 ZCG(cache_persistent_script) = NULL;
1973 return accelerator_orig_compile_file(file_handle, type);
1974 }
1975 persistent_script = zend_accel_hash_str_find(&ZCSG(hash), key, key_length);
1976 } else if (UNEXPECTED(is_stream_path(file_handle->filename) && !is_cacheable_stream_path(file_handle->filename))) {
1977 ZCG(cache_opline) = NULL;
1978 ZCG(cache_persistent_script) = NULL;
1979 return accelerator_orig_compile_file(file_handle, type);
1980 }
1981
1982 if (!persistent_script) {
1983 /* try to find cached script by full real path */
1984 zend_accel_hash_entry *bucket;
1985
1986 /* open file to resolve the path */
1987 if (file_handle->type == ZEND_HANDLE_FILENAME &&
1988 accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle) == FAILURE) {
1989 if (!EG(exception)) {
1990 if (type == ZEND_REQUIRE) {
1991 zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
1992 zend_bailout();
1993 } else {
1994 zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
1995 }
1996 }
1997 return NULL;
1998 }
1999
2000 if (file_handle->opened_path) {
2001 bucket = zend_accel_hash_find_entry(&ZCSG(hash), file_handle->opened_path);
2002
2003 if (bucket) {
2004 persistent_script = (zend_persistent_script *)bucket->data;
2005
2006 if (key && !persistent_script->corrupted) {
2007 HANDLE_BLOCK_INTERRUPTIONS();
2008 SHM_UNPROTECT();
2009 zend_shared_alloc_lock();
2010 zend_accel_add_key(key, key_length, bucket);
2011 zend_shared_alloc_unlock();
2012 SHM_PROTECT();
2013 HANDLE_UNBLOCK_INTERRUPTIONS();
2014 }
2015 }
2016 }
2017 }
2018 }
2019
2020 /* clear cache */
2021 ZCG(cache_opline) = NULL;
2022 ZCG(cache_persistent_script) = NULL;
2023
2024 if (persistent_script && persistent_script->corrupted) {
2025 persistent_script = NULL;
2026 }
2027
2028 /* Make sure we only increase the currently running processes semaphore
2029 * once each execution (this function can be called more than once on
2030 * each execution)
2031 */
2032 if (!ZCG(counted)) {
2033 if (accel_activate_add() == FAILURE) {
2034 if (ZCG(accel_directives).file_cache) {
2035 return file_cache_compile_file(file_handle, type);
2036 }
2037 return accelerator_orig_compile_file(file_handle, type);
2038 }
2039 ZCG(counted) = 1;
2040 }
2041
2042 /* Revalidate accessibility of cached file */
2043 if (EXPECTED(persistent_script != NULL) &&
2044 UNEXPECTED(ZCG(accel_directives).validate_permission) &&
2045 file_handle->type == ZEND_HANDLE_FILENAME &&
2046 UNEXPECTED(check_persistent_script_access(persistent_script))) {
2047 if (!EG(exception)) {
2048 if (type == ZEND_REQUIRE) {
2049 zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
2050 zend_bailout();
2051 } else {
2052 zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
2053 }
2054 }
2055 return NULL;
2056 }
2057
2058 HANDLE_BLOCK_INTERRUPTIONS();
2059 SHM_UNPROTECT();
2060
2061 /* If script is found then validate_timestamps if option is enabled */
2062 if (persistent_script && ZCG(accel_directives).validate_timestamps) {
2063 if (validate_timestamp_and_record(persistent_script, file_handle) == FAILURE) {
2064 zend_shared_alloc_lock();
2065 if (!persistent_script->corrupted) {
2066 persistent_script->corrupted = 1;
2067 persistent_script->timestamp = 0;
2068 ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
2069 if (ZSMMG(memory_exhausted)) {
2070 zend_accel_restart_reason reason =
2071 zend_accel_hash_is_full(&ZCSG(hash)) ? ACCEL_RESTART_HASH : ACCEL_RESTART_OOM;
2072 zend_accel_schedule_restart_if_necessary(reason);
2073 }
2074 }
2075 zend_shared_alloc_unlock();
2076 persistent_script = NULL;
2077 }
2078 }
2079
2080 /* if turned on - check the compiled script ADLER32 checksum */
2081 if (persistent_script && ZCG(accel_directives).consistency_checks
2082 && persistent_script->dynamic_members.hits % ZCG(accel_directives).consistency_checks == 0) {
2083
2084 unsigned int checksum = zend_accel_script_checksum(persistent_script);
2085 if (checksum != persistent_script->dynamic_members.checksum ) {
2086 /* The checksum is wrong */
2087 zend_accel_error(ACCEL_LOG_INFO, "Checksum failed for '%s': expected=0x%08x, found=0x%08x",
2088 ZSTR_VAL(persistent_script->script.filename), persistent_script->dynamic_members.checksum, checksum);
2089 zend_shared_alloc_lock();
2090 if (!persistent_script->corrupted) {
2091 persistent_script->corrupted = 1;
2092 persistent_script->timestamp = 0;
2093 ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
2094 if (ZSMMG(memory_exhausted)) {
2095 zend_accel_restart_reason reason =
2096 zend_accel_hash_is_full(&ZCSG(hash)) ? ACCEL_RESTART_HASH : ACCEL_RESTART_OOM;
2097 zend_accel_schedule_restart_if_necessary(reason);
2098 }
2099 }
2100 zend_shared_alloc_unlock();
2101 persistent_script = NULL;
2102 }
2103 }
2104
2105 /* Check the second level cache */
2106 if (!persistent_script && ZCG(accel_directives).file_cache) {
2107 persistent_script = zend_file_cache_script_load(file_handle);
2108 }
2109
2110 /* If script was not found or invalidated by validate_timestamps */
2111 if (!persistent_script) {
2112 uint32_t old_const_num = zend_hash_next_free_element(EG(zend_constants));
2113 zend_op_array *op_array;
2114
2115 /* Cache miss.. */
2116 ZCSG(misses)++;
2117
2118 /* No memory left. Behave like without the Accelerator */
2119 if (ZSMMG(memory_exhausted) || ZCSG(restart_pending)) {
2120 SHM_PROTECT();
2121 HANDLE_UNBLOCK_INTERRUPTIONS();
2122 if (ZCG(accel_directives).file_cache) {
2123 return file_cache_compile_file(file_handle, type);
2124 }
2125 return accelerator_orig_compile_file(file_handle, type);
2126 }
2127
2128 SHM_PROTECT();
2129 HANDLE_UNBLOCK_INTERRUPTIONS();
2130 persistent_script = opcache_compile_file(file_handle, type, key, &op_array);
2131 HANDLE_BLOCK_INTERRUPTIONS();
2132 SHM_UNPROTECT();
2133
2134 /* Try and cache the script and assume that it is returned from_shared_memory.
2135 * If it isn't compile_and_cache_file() changes the flag to 0
2136 */
2137 from_shared_memory = 0;
2138 if (persistent_script) {
2139 persistent_script = cache_script_in_shared_memory(persistent_script, key, key ? key_length : 0, &from_shared_memory);
2140 }
2141
2142 /* Caching is disabled, returning op_array;
2143 * or something went wrong during compilation, returning NULL
2144 */
2145 if (!persistent_script) {
2146 SHM_PROTECT();
2147 HANDLE_UNBLOCK_INTERRUPTIONS();
2148 return op_array;
2149 }
2150 if (from_shared_memory) {
2151 /* Delete immutable arrays moved into SHM */
2152 uint32_t new_const_num = zend_hash_next_free_element(EG(zend_constants));
2153 while (new_const_num > old_const_num) {
2154 new_const_num--;
2155 zend_hash_index_del(EG(zend_constants), new_const_num);
2156 }
2157 }
2158 } else {
2159
2160 #if !ZEND_WIN32
2161 ZCSG(hits)++; /* TBFixed: may lose one hit */
2162 persistent_script->dynamic_members.hits++; /* see above */
2163 #else
2164 #ifdef _M_X64
2165 InterlockedIncrement64(&ZCSG(hits));
2166 #else
2167 InterlockedIncrement(&ZCSG(hits));
2168 #endif
2169 InterlockedIncrement64(&persistent_script->dynamic_members.hits);
2170 #endif
2171
2172 /* see bug #15471 (old BTS) */
2173 if (persistent_script->script.filename) {
2174 if (!EG(current_execute_data) || !EG(current_execute_data)->opline ||
2175 !EG(current_execute_data)->func ||
2176 !ZEND_USER_CODE(EG(current_execute_data)->func->common.type) ||
2177 EG(current_execute_data)->opline->opcode != ZEND_INCLUDE_OR_EVAL ||
2178 (EG(current_execute_data)->opline->extended_value != ZEND_INCLUDE_ONCE &&
2179 EG(current_execute_data)->opline->extended_value != ZEND_REQUIRE_ONCE)) {
2180 if (zend_hash_add_empty_element(&EG(included_files), persistent_script->script.filename) != NULL) {
2181 /* ext/phar has to load phar's metadata into memory */
2182 if (persistent_script->is_phar) {
2183 php_stream_statbuf ssb;
2184 char *fname = emalloc(sizeof("phar://") + ZSTR_LEN(persistent_script->script.filename));
2185
2186 memcpy(fname, "phar://", sizeof("phar://") - 1);
2187 memcpy(fname + sizeof("phar://") - 1, ZSTR_VAL(persistent_script->script.filename), ZSTR_LEN(persistent_script->script.filename) + 1);
2188 php_stream_stat_path(fname, &ssb);
2189 efree(fname);
2190 }
2191 }
2192 }
2193 }
2194 zend_file_handle_dtor(file_handle);
2195 from_shared_memory = 1;
2196 }
2197
2198 persistent_script->dynamic_members.last_used = ZCG(request_time);
2199
2200 SHM_PROTECT();
2201 HANDLE_UNBLOCK_INTERRUPTIONS();
2202
2203 /* Fetch jit auto globals used in the script before execution */
2204 if (persistent_script->ping_auto_globals_mask) {
2205 zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask);
2206 }
2207
2208 return zend_accel_load_script(persistent_script, from_shared_memory);
2209 }
2210
2211 #ifdef ZEND_WIN32
accel_gen_uname_id(void)2212 static int accel_gen_uname_id(void)
2213 {
2214 PHP_MD5_CTX ctx;
2215 unsigned char digest[16];
2216 wchar_t uname[UNLEN + 1];
2217 DWORD unsize = UNLEN;
2218
2219 if (!GetUserNameW(uname, &unsize)) {
2220 return FAILURE;
2221 }
2222 PHP_MD5Init(&ctx);
2223 PHP_MD5Update(&ctx, (void *) uname, (unsize - 1) * sizeof(wchar_t));
2224 PHP_MD5Update(&ctx, ZCG(accel_directives).cache_id, strlen(ZCG(accel_directives).cache_id));
2225 PHP_MD5Final(digest, &ctx);
2226 php_hash_bin2hex(accel_uname_id, digest, sizeof digest);
2227 return SUCCESS;
2228 }
2229 #endif
2230
2231 /* zend_stream_open_function() replacement for PHP 5.3 and above */
persistent_stream_open_function(const char * filename,zend_file_handle * handle)2232 static int persistent_stream_open_function(const char *filename, zend_file_handle *handle)
2233 {
2234 if (ZCG(cache_persistent_script)) {
2235 /* check if callback is called from include_once or it's a main request */
2236 if ((!EG(current_execute_data) &&
2237 filename == SG(request_info).path_translated &&
2238 ZCG(cache_opline) == NULL) ||
2239 (EG(current_execute_data) &&
2240 EG(current_execute_data)->func &&
2241 ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
2242 ZCG(cache_opline) == EG(current_execute_data)->opline)) {
2243
2244 /* we are in include_once or FastCGI request */
2245 zend_stream_init_filename(handle, (char*) filename);
2246 handle->opened_path = zend_string_copy(ZCG(cache_persistent_script)->script.filename);
2247 return SUCCESS;
2248 }
2249 ZCG(cache_opline) = NULL;
2250 ZCG(cache_persistent_script) = NULL;
2251 }
2252 return accelerator_orig_zend_stream_open_function(filename, handle);
2253 }
2254
2255 /* zend_resolve_path() replacement for PHP 5.3 and above */
persistent_zend_resolve_path(const char * filename,size_t filename_len)2256 static zend_string* persistent_zend_resolve_path(const char *filename, size_t filename_len)
2257 {
2258 if (!file_cache_only &&
2259 ZCG(accelerator_enabled)) {
2260
2261 /* check if callback is called from include_once or it's a main request */
2262 if ((!EG(current_execute_data) &&
2263 filename == SG(request_info).path_translated) ||
2264 (EG(current_execute_data) &&
2265 EG(current_execute_data)->func &&
2266 ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
2267 EG(current_execute_data)->opline->opcode == ZEND_INCLUDE_OR_EVAL &&
2268 (EG(current_execute_data)->opline->extended_value == ZEND_INCLUDE_ONCE ||
2269 EG(current_execute_data)->opline->extended_value == ZEND_REQUIRE_ONCE))) {
2270
2271 /* we are in include_once or FastCGI request */
2272 zend_string *resolved_path;
2273 int key_length;
2274 char *key = NULL;
2275
2276 if (!ZCG(accel_directives).revalidate_path) {
2277 /* lookup by "not-real" path */
2278 key = accel_make_persistent_key(filename, filename_len, &key_length);
2279 if (key) {
2280 zend_accel_hash_entry *bucket = zend_accel_hash_str_find_entry(&ZCSG(hash), key, key_length);
2281 if (bucket != NULL) {
2282 zend_persistent_script *persistent_script = (zend_persistent_script *)bucket->data;
2283 if (!persistent_script->corrupted) {
2284 ZCG(cache_opline) = EG(current_execute_data) ? EG(current_execute_data)->opline : NULL;
2285 ZCG(cache_persistent_script) = persistent_script;
2286 return zend_string_copy(persistent_script->script.filename);
2287 }
2288 }
2289 } else {
2290 ZCG(cache_opline) = NULL;
2291 ZCG(cache_persistent_script) = NULL;
2292 return accelerator_orig_zend_resolve_path(filename, filename_len);
2293 }
2294 }
2295
2296 /* find the full real path */
2297 resolved_path = accelerator_orig_zend_resolve_path(filename, filename_len);
2298
2299 if (resolved_path) {
2300 /* lookup by real path */
2301 zend_accel_hash_entry *bucket = zend_accel_hash_find_entry(&ZCSG(hash), resolved_path);
2302 if (bucket) {
2303 zend_persistent_script *persistent_script = (zend_persistent_script *)bucket->data;
2304 if (!persistent_script->corrupted) {
2305 if (key) {
2306 /* add another "key" for the same bucket */
2307 HANDLE_BLOCK_INTERRUPTIONS();
2308 SHM_UNPROTECT();
2309 zend_shared_alloc_lock();
2310 zend_accel_add_key(key, key_length, bucket);
2311 zend_shared_alloc_unlock();
2312 SHM_PROTECT();
2313 HANDLE_UNBLOCK_INTERRUPTIONS();
2314 } else {
2315 ZCG(key_len) = 0;
2316 }
2317 ZCG(cache_opline) = EG(current_execute_data) ? EG(current_execute_data)->opline : NULL;
2318 ZCG(cache_persistent_script) = persistent_script;
2319 return resolved_path;
2320 }
2321 }
2322 }
2323
2324 ZCG(cache_opline) = NULL;
2325 ZCG(cache_persistent_script) = NULL;
2326 return resolved_path;
2327 }
2328 }
2329 ZCG(cache_opline) = NULL;
2330 ZCG(cache_persistent_script) = NULL;
2331 return accelerator_orig_zend_resolve_path(filename, filename_len);
2332 }
2333
zend_reset_cache_vars(void)2334 static void zend_reset_cache_vars(void)
2335 {
2336 ZSMMG(memory_exhausted) = 0;
2337 ZCSG(hits) = 0;
2338 ZCSG(misses) = 0;
2339 ZCSG(blacklist_misses) = 0;
2340 ZSMMG(wasted_shared_memory) = 0;
2341 ZCSG(restart_pending) = 0;
2342 ZCSG(force_restart_time) = 0;
2343 ZCSG(map_ptr_last) = CG(map_ptr_last);
2344 }
2345
accel_reset_pcre_cache(void)2346 static void accel_reset_pcre_cache(void)
2347 {
2348 Bucket *p;
2349
2350 if (PCRE_G(per_request_cache)) {
2351 return;
2352 }
2353
2354 ZEND_HASH_FOREACH_BUCKET(&PCRE_G(pcre_cache), p) {
2355 /* Remove PCRE cache entries with inconsistent keys */
2356 if (zend_accel_in_shm(p->key)) {
2357 p->key = NULL;
2358 zend_hash_del_bucket(&PCRE_G(pcre_cache), p);
2359 }
2360 } ZEND_HASH_FOREACH_END();
2361 }
2362
accel_activate(INIT_FUNC_ARGS)2363 int accel_activate(INIT_FUNC_ARGS)
2364 {
2365 if (!ZCG(enabled) || !accel_startup_ok) {
2366 ZCG(accelerator_enabled) = 0;
2367 return SUCCESS;
2368 }
2369
2370 /* PHP-5.4 and above return "double", but we use 1 sec precision */
2371 ZCG(auto_globals_mask) = 0;
2372 ZCG(request_time) = (time_t)sapi_get_request_time();
2373 ZCG(cache_opline) = NULL;
2374 ZCG(cache_persistent_script) = NULL;
2375 ZCG(include_path_key_len) = 0;
2376 ZCG(include_path_check) = 1;
2377
2378 ZCG(cwd) = NULL;
2379 ZCG(cwd_key_len) = 0;
2380 ZCG(cwd_check) = 1;
2381
2382 if (file_cache_only) {
2383 ZCG(accelerator_enabled) = 0;
2384 return SUCCESS;
2385 }
2386
2387 #ifndef ZEND_WIN32
2388 if (ZCG(accel_directives).validate_root) {
2389 struct stat buf;
2390
2391 if (stat("/", &buf) != 0) {
2392 ZCG(root_hash) = 0;
2393 } else {
2394 ZCG(root_hash) = buf.st_ino;
2395 if (sizeof(buf.st_ino) > sizeof(ZCG(root_hash))) {
2396 if (ZCG(root_hash) != buf.st_ino) {
2397 zend_string *key = zend_string_init("opcache.enable", sizeof("opcache.enable")-1, 0);
2398 zend_alter_ini_entry_chars(key, "0", 1, ZEND_INI_SYSTEM, ZEND_INI_STAGE_RUNTIME);
2399 zend_string_release_ex(key, 0);
2400 zend_accel_error(ACCEL_LOG_WARNING, "Can't cache files in chroot() directory with too big inode");
2401 return SUCCESS;
2402 }
2403 }
2404 }
2405 } else {
2406 ZCG(root_hash) = 0;
2407 }
2408 #endif
2409
2410 HANDLE_BLOCK_INTERRUPTIONS();
2411 SHM_UNPROTECT();
2412
2413 if (ZCG(counted)) {
2414 #ifdef ZTS
2415 zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for thread id %lu", (unsigned long) tsrm_thread_id());
2416 #else
2417 zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for pid %d", getpid());
2418 #endif
2419 accel_unlock_all();
2420 ZCG(counted) = 0;
2421 }
2422
2423 if (ZCSG(restart_pending)) {
2424 zend_shared_alloc_lock();
2425 if (ZCSG(restart_pending) != 0) { /* check again, to ensure that the cache wasn't already cleaned by another process */
2426 if (accel_is_inactive() == SUCCESS) {
2427 zend_accel_error(ACCEL_LOG_DEBUG, "Restarting!");
2428 ZCSG(restart_pending) = 0;
2429 switch ZCSG(restart_reason) {
2430 case ACCEL_RESTART_OOM:
2431 ZCSG(oom_restarts)++;
2432 break;
2433 case ACCEL_RESTART_HASH:
2434 ZCSG(hash_restarts)++;
2435 break;
2436 case ACCEL_RESTART_USER:
2437 ZCSG(manual_restarts)++;
2438 break;
2439 }
2440 accel_restart_enter();
2441
2442 zend_map_ptr_reset();
2443 zend_reset_cache_vars();
2444 zend_accel_hash_clean(&ZCSG(hash));
2445
2446 if (ZCG(accel_directives).interned_strings_buffer) {
2447 accel_interned_strings_restore_state();
2448 }
2449
2450 zend_shared_alloc_restore_state();
2451 if (ZCSG(preload_script)) {
2452 preload_restart();
2453 }
2454 ZCSG(accelerator_enabled) = ZCSG(cache_status_before_restart);
2455 if (ZCSG(last_restart_time) < ZCG(request_time)) {
2456 ZCSG(last_restart_time) = ZCG(request_time);
2457 } else {
2458 ZCSG(last_restart_time)++;
2459 }
2460 accel_restart_leave();
2461 }
2462 }
2463 zend_shared_alloc_unlock();
2464 }
2465
2466 ZCG(accelerator_enabled) = ZCSG(accelerator_enabled);
2467
2468 SHM_PROTECT();
2469 HANDLE_UNBLOCK_INTERRUPTIONS();
2470
2471 if (ZCG(accelerator_enabled) && ZCSG(last_restart_time) != ZCG(last_restart_time)) {
2472 /* SHM was reinitialized. */
2473 ZCG(last_restart_time) = ZCSG(last_restart_time);
2474
2475 /* Reset in-process realpath cache */
2476 realpath_cache_clean();
2477
2478 accel_reset_pcre_cache();
2479 ZCG(pcre_reseted) = 0;
2480 } else if (!ZCG(accelerator_enabled) && !ZCG(pcre_reseted)) {
2481 accel_reset_pcre_cache();
2482 ZCG(pcre_reseted) = 1;
2483 }
2484
2485 if (ZCSG(preload_script)) {
2486 preload_activate();
2487 }
2488
2489 return SUCCESS;
2490 }
2491
accel_post_deactivate(void)2492 int accel_post_deactivate(void)
2493 {
2494 if (ZCG(cwd)) {
2495 zend_string_release_ex(ZCG(cwd), 0);
2496 ZCG(cwd) = NULL;
2497 }
2498
2499 if (!ZCG(enabled) || !accel_startup_ok) {
2500 return SUCCESS;
2501 }
2502
2503 zend_shared_alloc_safe_unlock(); /* be sure we didn't leave cache locked */
2504 accel_unlock_all();
2505 ZCG(counted) = 0;
2506
2507 return SUCCESS;
2508 }
2509
accelerator_remove_cb(zend_extension * element1,zend_extension * element2)2510 static int accelerator_remove_cb(zend_extension *element1, zend_extension *element2)
2511 {
2512 (void)element2; /* keep the compiler happy */
2513
2514 if (!strcmp(element1->name, ACCELERATOR_PRODUCT_NAME )) {
2515 element1->startup = NULL;
2516 #if 0
2517 /* We have to call shutdown callback it to free TS resources */
2518 element1->shutdown = NULL;
2519 #endif
2520 element1->activate = NULL;
2521 element1->deactivate = NULL;
2522 element1->op_array_handler = NULL;
2523
2524 #ifdef __DEBUG_MESSAGES__
2525 fprintf(stderr, ACCELERATOR_PRODUCT_NAME " is disabled: %s\n", (zps_failure_reason ? zps_failure_reason : "unknown error"));
2526 fflush(stderr);
2527 #endif
2528 }
2529
2530 return 0;
2531 }
2532
zps_startup_failure(char * reason,char * api_reason,int (* cb)(zend_extension *,zend_extension *))2533 static void zps_startup_failure(char *reason, char *api_reason, int (*cb)(zend_extension *, zend_extension *))
2534 {
2535 accel_startup_ok = 0;
2536 zps_failure_reason = reason;
2537 zps_api_failure_reason = api_reason?api_reason:reason;
2538 zend_llist_del_element(&zend_extensions, NULL, (int (*)(void *, void *))cb);
2539 }
2540
accel_find_sapi(void)2541 static inline int accel_find_sapi(void)
2542 {
2543 static const char *supported_sapis[] = {
2544 "apache",
2545 "fastcgi",
2546 "cli-server",
2547 "cgi-fcgi",
2548 "fpm-fcgi",
2549 "fpmi-fcgi",
2550 "apache2handler",
2551 "litespeed",
2552 "uwsgi",
2553 NULL
2554 };
2555 const char **sapi_name;
2556
2557 if (sapi_module.name) {
2558 for (sapi_name = supported_sapis; *sapi_name; sapi_name++) {
2559 if (strcmp(sapi_module.name, *sapi_name) == 0) {
2560 return SUCCESS;
2561 }
2562 }
2563 if (ZCG(accel_directives).enable_cli && (
2564 strcmp(sapi_module.name, "cli") == 0
2565 || strcmp(sapi_module.name, "phpdbg") == 0)) {
2566 return SUCCESS;
2567 }
2568 }
2569
2570 return FAILURE;
2571 }
2572
zend_accel_init_shm(void)2573 static int zend_accel_init_shm(void)
2574 {
2575 int i;
2576
2577 zend_shared_alloc_lock();
2578
2579 if (ZCG(accel_directives).interned_strings_buffer) {
2580 accel_shared_globals = zend_shared_alloc((ZCG(accel_directives).interned_strings_buffer * 1024 * 1024));
2581 } else {
2582 /* Make sure there is always at least one interned string hash slot,
2583 * so the table can be queried unconditionally. */
2584 accel_shared_globals = zend_shared_alloc(sizeof(zend_accel_shared_globals) + sizeof(uint32_t));
2585 }
2586 if (!accel_shared_globals) {
2587 zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
2588 zend_shared_alloc_unlock();
2589 return FAILURE;
2590 }
2591 memset(accel_shared_globals, 0, sizeof(zend_accel_shared_globals));
2592 ZSMMG(app_shared_globals) = accel_shared_globals;
2593
2594 zend_accel_hash_init(&ZCSG(hash), ZCG(accel_directives).max_accelerated_files);
2595
2596 if (ZCG(accel_directives).interned_strings_buffer) {
2597 uint32_t hash_size;
2598
2599 /* must be a power of two */
2600 hash_size = ZCG(accel_directives).interned_strings_buffer * (32 * 1024);
2601 hash_size |= (hash_size >> 1);
2602 hash_size |= (hash_size >> 2);
2603 hash_size |= (hash_size >> 4);
2604 hash_size |= (hash_size >> 8);
2605 hash_size |= (hash_size >> 16);
2606
2607 ZCSG(interned_strings).nTableMask = hash_size << 2;
2608 ZCSG(interned_strings).nNumOfElements = 0;
2609 ZCSG(interned_strings).start =
2610 (zend_string*)((char*)&ZCSG(interned_strings) +
2611 sizeof(zend_string_table) +
2612 ((hash_size + 1) * sizeof(uint32_t))) +
2613 8;
2614 ZCSG(interned_strings).top =
2615 ZCSG(interned_strings).start;
2616 ZCSG(interned_strings).end =
2617 (zend_string*)((char*)accel_shared_globals +
2618 ZCG(accel_directives).interned_strings_buffer * 1024 * 1024);
2619 ZCSG(interned_strings).saved_top = NULL;
2620
2621 memset((char*)&ZCSG(interned_strings) + sizeof(zend_string_table),
2622 STRTAB_INVALID_POS,
2623 (char*)ZCSG(interned_strings).start -
2624 ((char*)&ZCSG(interned_strings) + sizeof(zend_string_table)));
2625 } else {
2626 *STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), 0) = STRTAB_INVALID_POS;
2627 }
2628
2629 zend_interned_strings_set_request_storage_handlers(accel_new_interned_string_for_php, accel_init_interned_string_for_php);
2630
2631 zend_reset_cache_vars();
2632
2633 ZCSG(oom_restarts) = 0;
2634 ZCSG(hash_restarts) = 0;
2635 ZCSG(manual_restarts) = 0;
2636
2637 ZCSG(accelerator_enabled) = 1;
2638 ZCSG(start_time) = zend_accel_get_time();
2639 ZCSG(last_restart_time) = 0;
2640 ZCSG(restart_in_progress) = 0;
2641
2642 for (i = 0; i < -HT_MIN_MASK; i++) {
2643 ZCSG(uninitialized_bucket)[i] = HT_INVALID_IDX;
2644 }
2645
2646 zend_shared_alloc_unlock();
2647
2648 return SUCCESS;
2649 }
2650
accel_globals_ctor(zend_accel_globals * accel_globals)2651 static void accel_globals_ctor(zend_accel_globals *accel_globals)
2652 {
2653 #if defined(COMPILE_DL_OPCACHE) && defined(ZTS)
2654 ZEND_TSRMLS_CACHE_UPDATE();
2655 #endif
2656 memset(accel_globals, 0, sizeof(zend_accel_globals));
2657 }
2658
2659 #define ZEND_BIN_ID "BIN_" ZEND_TOSTR(SIZEOF_INT) ZEND_TOSTR(SIZEOF_LONG) ZEND_TOSTR(SIZEOF_SIZE_T) ZEND_TOSTR(SIZEOF_ZEND_LONG) ZEND_TOSTR(ZEND_MM_ALIGNMENT)
2660
accel_gen_system_id(void)2661 static void accel_gen_system_id(void)
2662 {
2663 PHP_MD5_CTX context;
2664 unsigned char digest[16];
2665 zend_module_entry *module;
2666 zend_extension *extension;
2667 zend_llist_position pos;
2668
2669 PHP_MD5Init(&context);
2670 PHP_MD5Update(&context, PHP_VERSION, sizeof(PHP_VERSION)-1);
2671 PHP_MD5Update(&context, ZEND_EXTENSION_BUILD_ID, sizeof(ZEND_EXTENSION_BUILD_ID)-1);
2672 PHP_MD5Update(&context, ZEND_BIN_ID, sizeof(ZEND_BIN_ID)-1);
2673 if (strstr(PHP_VERSION, "-dev") != 0) {
2674 /* Development versions may be changed from build to build */
2675 PHP_MD5Update(&context, __DATE__, sizeof(__DATE__)-1);
2676 PHP_MD5Update(&context, __TIME__, sizeof(__TIME__)-1);
2677 }
2678 /* Modules may have changed after restart which can cause dangling pointers from
2679 * custom opcode handlers in the second-level cache files
2680 */
2681 ZEND_HASH_FOREACH_PTR(&module_registry, module) {
2682 PHP_MD5Update(&context, module->name, strlen(module->name));
2683 if (module->version != NULL) {
2684 PHP_MD5Update(&context, module->version, strlen(module->version));
2685 }
2686 } ZEND_HASH_FOREACH_END();
2687 extension = (zend_extension *) zend_llist_get_first_ex(&zend_extensions, &pos);
2688 while (extension) {
2689 PHP_MD5Update(&context, extension->name, strlen(extension->name));
2690 if (extension->version != NULL) {
2691 PHP_MD5Update(&context, extension->version, strlen(extension->version));
2692 }
2693 extension = (zend_extension *) zend_llist_get_next_ex(&zend_extensions, &pos);
2694 }
2695 PHP_MD5Final(digest, &context);
2696 php_hash_bin2hex(accel_system_id, digest, sizeof digest);
2697 }
2698
2699 #ifdef HAVE_HUGE_CODE_PAGES
2700 # ifndef _WIN32
2701 # include <sys/mman.h>
2702 # ifndef MAP_ANON
2703 # ifdef MAP_ANONYMOUS
2704 # define MAP_ANON MAP_ANONYMOUS
2705 # endif
2706 # endif
2707 # ifndef MAP_FAILED
2708 # define MAP_FAILED ((void*)-1)
2709 # endif
2710 # ifdef MAP_ALIGNED_SUPER
2711 # include <sys/types.h>
2712 # include <sys/sysctl.h>
2713 # include <sys/user.h>
2714 # define MAP_HUGETLB MAP_ALIGNED_SUPER
2715 # endif
2716 # endif
2717
2718 # if defined(MAP_HUGETLB) || defined(MADV_HUGEPAGE)
accel_remap_huge_pages(void * start,size_t size,size_t real_size,const char * name,size_t offset)2719 static int accel_remap_huge_pages(void *start, size_t size, size_t real_size, const char *name, size_t offset)
2720 {
2721 void *ret = MAP_FAILED;
2722 void *mem;
2723
2724 mem = mmap(NULL, size,
2725 PROT_READ | PROT_WRITE,
2726 MAP_PRIVATE | MAP_ANONYMOUS,
2727 -1, 0);
2728 if (mem == MAP_FAILED) {
2729 zend_error(E_WARNING,
2730 ACCELERATOR_PRODUCT_NAME " huge_code_pages: mmap failed: %s (%d)",
2731 strerror(errno), errno);
2732 return -1;
2733 }
2734 memcpy(mem, start, real_size);
2735
2736 # ifdef MAP_HUGETLB
2737 ret = mmap(start, size,
2738 PROT_READ | PROT_WRITE | PROT_EXEC,
2739 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_HUGETLB,
2740 -1, 0);
2741 # endif
2742 if (ret == MAP_FAILED) {
2743 ret = mmap(start, size,
2744 PROT_READ | PROT_WRITE | PROT_EXEC,
2745 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
2746 -1, 0);
2747 /* this should never happen? */
2748 ZEND_ASSERT(ret != MAP_FAILED);
2749 # ifdef MADV_HUGEPAGE
2750 if (-1 == madvise(start, size, MADV_HUGEPAGE)) {
2751 memcpy(start, mem, real_size);
2752 mprotect(start, size, PROT_READ | PROT_EXEC);
2753 munmap(mem, size);
2754 zend_error(E_WARNING,
2755 ACCELERATOR_PRODUCT_NAME " huge_code_pages: madvise(HUGEPAGE) failed: %s (%d)",
2756 strerror(errno), errno);
2757 return -1;
2758 }
2759 # else
2760 memcpy(start, mem, real_size);
2761 mprotect(start, size, PROT_READ | PROT_EXEC);
2762 munmap(mem, size);
2763 zend_error(E_WARNING,
2764 ACCELERATOR_PRODUCT_NAME " huge_code_pages: mmap(HUGETLB) failed: %s (%d)",
2765 strerror(errno), errno);
2766 return -1;
2767 # endif
2768 }
2769
2770 if (ret == start) {
2771 memcpy(start, mem, real_size);
2772 mprotect(start, size, PROT_READ | PROT_EXEC);
2773 }
2774 munmap(mem, size);
2775
2776 return (ret == start) ? 0 : -1;
2777 }
2778
accel_move_code_to_huge_pages(void)2779 static void accel_move_code_to_huge_pages(void)
2780 {
2781 #if defined(__linux__)
2782 FILE *f;
2783 long unsigned int huge_page_size = 2 * 1024 * 1024;
2784
2785 f = fopen("/proc/self/maps", "r");
2786 if (f) {
2787 long unsigned int start, end, offset, inode;
2788 char perm[5], dev[6], name[MAXPATHLEN];
2789 int ret;
2790
2791 while (1) {
2792 ret = fscanf(f, "%lx-%lx %4s %lx %5s %ld %s\n", &start, &end, perm, &offset, dev, &inode, name);
2793 if (ret == 7) {
2794 if (perm[0] == 'r' && perm[1] == '-' && perm[2] == 'x' && name[0] == '/') {
2795 long unsigned int seg_start = ZEND_MM_ALIGNED_SIZE_EX(start, huge_page_size);
2796 long unsigned int seg_end = (end & ~(huge_page_size-1L));
2797 long unsigned int real_end;
2798
2799 ret = fscanf(f, "%lx-", &start);
2800 if (ret == 1 && start == seg_end + huge_page_size) {
2801 real_end = end;
2802 seg_end = start;
2803 } else {
2804 real_end = seg_end;
2805 }
2806
2807 if (seg_end > seg_start) {
2808 zend_accel_error(ACCEL_LOG_DEBUG, "remap to huge page %lx-%lx %s \n", seg_start, seg_end, name);
2809 accel_remap_huge_pages((void*)seg_start, seg_end - seg_start, real_end - seg_start, name, offset + seg_start - start);
2810 }
2811 break;
2812 }
2813 } else {
2814 break;
2815 }
2816 }
2817 fclose(f);
2818 }
2819 #elif defined(__FreeBSD__)
2820 size_t s = 0;
2821 int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()};
2822 long unsigned int huge_page_size = 2 * 1024 * 1024;
2823 if (sysctl(mib, 4, NULL, &s, NULL, 0) == 0) {
2824 s = s * 4 / 3;
2825 void *addr = mmap(NULL, s, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
2826 if (addr != MAP_FAILED) {
2827 if (sysctl(mib, 4, addr, &s, NULL, 0) == 0) {
2828 uintptr_t start = (uintptr_t)addr;
2829 uintptr_t end = start + s;
2830 while (start < end) {
2831 struct kinfo_vmentry *entry = (struct kinfo_vmentry *)start;
2832 size_t sz = entry->kve_structsize;
2833 if (sz == 0) {
2834 break;
2835 }
2836 int permflags = entry->kve_protection;
2837 if ((permflags & KVME_PROT_READ) && !(permflags & KVME_PROT_WRITE) &&
2838 (permflags & KVME_PROT_EXEC) && entry->kve_path[0] != '\0') {
2839 long unsigned int seg_start = ZEND_MM_ALIGNED_SIZE_EX(start, huge_page_size);
2840 long unsigned int seg_end = (end & ~(huge_page_size-1L));
2841 if (seg_end > seg_start) {
2842 zend_accel_error(ACCEL_LOG_DEBUG, "remap to huge page %lx-%lx %s \n", seg_start, seg_end, entry->kve_path);
2843 accel_remap_huge_pages((void*)seg_start, seg_end - seg_start, seg_end - seg_start, entry->kve_path, entry->kve_offset + seg_start - start);
2844 // First relevant segment found is our binary
2845 break;
2846 }
2847 }
2848 start += sz;
2849 }
2850 }
2851 munmap(addr, s);
2852 }
2853 }
2854 #endif
2855 }
2856 # else
accel_move_code_to_huge_pages(void)2857 static void accel_move_code_to_huge_pages(void)
2858 {
2859 zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages has no affect as huge page is not supported");
2860 return;
2861 }
2862 # endif /* defined(MAP_HUGETLB) || defined(MADV_HUGEPAGE) */
2863 #endif /* HAVE_HUGE_CODE_PAGES */
2864
accel_startup(zend_extension * extension)2865 static int accel_startup(zend_extension *extension)
2866 {
2867 #ifdef ZTS
2868 accel_globals_id = ts_allocate_id(&accel_globals_id, sizeof(zend_accel_globals), (ts_allocate_ctor) accel_globals_ctor, NULL);
2869 #else
2870 accel_globals_ctor(&accel_globals);
2871 #endif
2872
2873 #ifdef ZEND_WIN32
2874 # if !defined(__has_feature) || !__has_feature(address_sanitizer)
2875 _setmaxstdio(2048); /* The default configuration is limited to 512 stdio files */
2876 # endif
2877 #endif
2878
2879 accel_gen_system_id();
2880
2881 if (start_accel_module() == FAILURE) {
2882 accel_startup_ok = 0;
2883 zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": module registration failed!");
2884 return FAILURE;
2885 }
2886
2887 #ifdef ZEND_WIN32
2888 if (UNEXPECTED(accel_gen_uname_id() == FAILURE)) {
2889 zps_startup_failure("Unable to get user name", NULL, accelerator_remove_cb);
2890 return SUCCESS;
2891 }
2892 #endif
2893
2894 #ifdef HAVE_HUGE_CODE_PAGES
2895 if (ZCG(accel_directives).huge_code_pages &&
2896 (strcmp(sapi_module.name, "cli") == 0 ||
2897 strcmp(sapi_module.name, "cli-server") == 0 ||
2898 strcmp(sapi_module.name, "cgi-fcgi") == 0 ||
2899 strcmp(sapi_module.name, "fpm-fcgi") == 0)) {
2900 accel_move_code_to_huge_pages();
2901 }
2902 #endif
2903
2904 /* no supported SAPI found - disable acceleration and stop initialization */
2905 if (accel_find_sapi() == FAILURE) {
2906 accel_startup_ok = 0;
2907 if (!ZCG(accel_directives).enable_cli &&
2908 strcmp(sapi_module.name, "cli") == 0) {
2909 zps_startup_failure("Opcode Caching is disabled for CLI", NULL, accelerator_remove_cb);
2910 } else {
2911 zps_startup_failure("Opcode Caching is only supported in Apache, FPM, FastCGI and LiteSpeed SAPIs", NULL, accelerator_remove_cb);
2912 }
2913 return SUCCESS;
2914 }
2915
2916 if (ZCG(enabled) == 0) {
2917 return SUCCESS ;
2918 }
2919
2920 orig_post_startup_cb = zend_post_startup_cb;
2921 zend_post_startup_cb = accel_post_startup;
2922
2923 /* Prevent unloadig */
2924 extension->handle = 0;
2925
2926 return SUCCESS;
2927 }
2928
accel_post_startup(void)2929 static int accel_post_startup(void)
2930 {
2931 zend_function *func;
2932 zend_ini_entry *ini_entry;
2933
2934 if (orig_post_startup_cb) {
2935 int (*cb)(void) = orig_post_startup_cb;
2936
2937 orig_post_startup_cb = NULL;
2938 if (cb() != SUCCESS) {
2939 return FAILURE;
2940 }
2941 }
2942
2943 /********************************************/
2944 /* End of non-SHM dependent initializations */
2945 /********************************************/
2946 file_cache_only = ZCG(accel_directives).file_cache_only;
2947 if (!file_cache_only) {
2948 switch (zend_shared_alloc_startup(ZCG(accel_directives).memory_consumption)) {
2949 case ALLOC_SUCCESS:
2950 if (zend_accel_init_shm() == FAILURE) {
2951 accel_startup_ok = 0;
2952 return FAILURE;
2953 }
2954 break;
2955 case ALLOC_FAILURE:
2956 accel_startup_ok = 0;
2957 zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - probably not enough shared memory.");
2958 return SUCCESS;
2959 case SUCCESSFULLY_REATTACHED:
2960 zend_shared_alloc_lock();
2961 accel_shared_globals = (zend_accel_shared_globals *) ZSMMG(app_shared_globals);
2962 zend_interned_strings_set_request_storage_handlers(accel_new_interned_string_for_php, accel_init_interned_string_for_php);
2963 zend_shared_alloc_unlock();
2964 break;
2965 case FAILED_REATTACHED:
2966 accel_startup_ok = 0;
2967 zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - can not reattach to exiting shared memory.");
2968 return SUCCESS;
2969 break;
2970 #if ENABLE_FILE_CACHE_FALLBACK
2971 case ALLOC_FALLBACK:
2972 zend_shared_alloc_lock();
2973 file_cache_only = 1;
2974 fallback_process = 1;
2975 zend_accel_init_auto_globals();
2976 zend_shared_alloc_unlock();
2977 goto file_cache_fallback;
2978 break;
2979 #endif
2980 }
2981
2982 /* from this point further, shared memory is supposed to be OK */
2983
2984 /* remember the last restart time in the process memory */
2985 ZCG(last_restart_time) = ZCSG(last_restart_time);
2986
2987 /* Init auto-global strings */
2988 zend_accel_init_auto_globals();
2989
2990 zend_shared_alloc_lock();
2991 zend_shared_alloc_save_state();
2992 zend_shared_alloc_unlock();
2993
2994 SHM_PROTECT();
2995 } else if (!ZCG(accel_directives).file_cache) {
2996 accel_startup_ok = 0;
2997 zend_accel_error(ACCEL_LOG_FATAL, "opcache.file_cache_only is set without a proper setting of opcache.file_cache");
2998 return SUCCESS;
2999 } else {
3000 accel_shared_globals = calloc(1, sizeof(zend_accel_shared_globals));
3001
3002 /* Init auto-global strings */
3003 zend_accel_init_auto_globals();
3004 }
3005 #if ENABLE_FILE_CACHE_FALLBACK
3006 file_cache_fallback:
3007 #endif
3008
3009 /* Override compiler */
3010 accelerator_orig_compile_file = zend_compile_file;
3011 zend_compile_file = persistent_compile_file;
3012
3013 /* Override stream opener function (to eliminate open() call caused by
3014 * include/require statements ) */
3015 accelerator_orig_zend_stream_open_function = zend_stream_open_function;
3016 zend_stream_open_function = persistent_stream_open_function;
3017
3018 /* Override path resolver function (to eliminate stat() calls caused by
3019 * include_once/require_once statements */
3020 accelerator_orig_zend_resolve_path = zend_resolve_path;
3021 zend_resolve_path = persistent_zend_resolve_path;
3022
3023 /* Override chdir() function */
3024 if ((func = zend_hash_str_find_ptr(CG(function_table), "chdir", sizeof("chdir")-1)) != NULL &&
3025 func->type == ZEND_INTERNAL_FUNCTION) {
3026 orig_chdir = func->internal_function.handler;
3027 func->internal_function.handler = ZEND_FN(accel_chdir);
3028 }
3029 ZCG(cwd) = NULL;
3030 ZCG(include_path) = NULL;
3031
3032 /* Override "include_path" modifier callback */
3033 if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) {
3034 ZCG(include_path) = ini_entry->value;
3035 orig_include_path_on_modify = ini_entry->on_modify;
3036 ini_entry->on_modify = accel_include_path_on_modify;
3037 }
3038
3039 accel_startup_ok = 1;
3040
3041 /* Override file_exists(), is_file() and is_readable() */
3042 zend_accel_override_file_functions();
3043
3044 /* Load black list */
3045 accel_blacklist.entries = NULL;
3046 if (ZCG(enabled) && accel_startup_ok &&
3047 ZCG(accel_directives).user_blacklist_filename &&
3048 *ZCG(accel_directives.user_blacklist_filename)) {
3049 zend_accel_blacklist_init(&accel_blacklist);
3050 zend_accel_blacklist_load(&accel_blacklist, ZCG(accel_directives.user_blacklist_filename));
3051 }
3052
3053 zend_optimizer_startup();
3054
3055 if (!file_cache_only && ZCG(accel_directives).interned_strings_buffer) {
3056 accel_use_shm_interned_strings();
3057 }
3058
3059 return accel_finish_startup();
3060 }
3061
3062 static void (*orig_post_shutdown_cb)(void);
3063
accel_post_shutdown(void)3064 static void accel_post_shutdown(void)
3065 {
3066 zend_shared_alloc_shutdown();
3067 }
3068
accel_shutdown(void)3069 void accel_shutdown(void)
3070 {
3071 zend_ini_entry *ini_entry;
3072 zend_bool _file_cache_only = 0;
3073
3074 zend_optimizer_shutdown();
3075
3076 zend_accel_blacklist_shutdown(&accel_blacklist);
3077
3078 if (!ZCG(enabled) || !accel_startup_ok) {
3079 #ifdef ZTS
3080 ts_free_id(accel_globals_id);
3081 #endif
3082 return;
3083 }
3084
3085 if (ZCSG(preload_script)) {
3086 preload_shutdown();
3087 }
3088
3089 _file_cache_only = file_cache_only;
3090
3091 accel_reset_pcre_cache();
3092
3093 #ifdef ZTS
3094 ts_free_id(accel_globals_id);
3095 #endif
3096
3097 if (!_file_cache_only) {
3098 /* Delay SHM dettach */
3099 orig_post_shutdown_cb = zend_post_shutdown_cb;
3100 zend_post_shutdown_cb = accel_post_shutdown;
3101 }
3102
3103 zend_compile_file = accelerator_orig_compile_file;
3104
3105 if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) {
3106 ini_entry->on_modify = orig_include_path_on_modify;
3107 }
3108 }
3109
zend_accel_schedule_restart(zend_accel_restart_reason reason)3110 void zend_accel_schedule_restart(zend_accel_restart_reason reason)
3111 {
3112 const char *zend_accel_restart_reason_text[ACCEL_RESTART_USER + 1] = {
3113 "out of memory",
3114 "hash overflow",
3115 "user",
3116 };
3117
3118 if (ZCSG(restart_pending)) {
3119 /* don't schedule twice */
3120 return;
3121 }
3122 zend_accel_error(ACCEL_LOG_DEBUG, "Restart Scheduled! Reason: %s",
3123 zend_accel_restart_reason_text[reason]);
3124
3125 HANDLE_BLOCK_INTERRUPTIONS();
3126 SHM_UNPROTECT();
3127 ZCSG(restart_pending) = 1;
3128 ZCSG(restart_reason) = reason;
3129 ZCSG(cache_status_before_restart) = ZCSG(accelerator_enabled);
3130 ZCSG(accelerator_enabled) = 0;
3131
3132 if (ZCG(accel_directives).force_restart_timeout) {
3133 ZCSG(force_restart_time) = zend_accel_get_time() + ZCG(accel_directives).force_restart_timeout;
3134 } else {
3135 ZCSG(force_restart_time) = 0;
3136 }
3137 SHM_PROTECT();
3138 HANDLE_UNBLOCK_INTERRUPTIONS();
3139 }
3140
3141 /* this is needed because on WIN32 lock is not decreased unless ZCG(counted) is set */
3142 #ifdef ZEND_WIN32
3143 #define accel_deactivate_now() ZCG(counted) = 1; accel_deactivate_sub()
3144 #else
3145 #define accel_deactivate_now() accel_deactivate_sub()
3146 #endif
3147
3148 /* ensures it is OK to read SHM
3149 if it's not OK (restart in progress) returns FAILURE
3150 if OK returns SUCCESS
3151 MUST call accelerator_shm_read_unlock after done lock operations
3152 */
accelerator_shm_read_lock(void)3153 int accelerator_shm_read_lock(void)
3154 {
3155 if (ZCG(counted)) {
3156 /* counted means we are holding read lock for SHM, so that nothing bad can happen */
3157 return SUCCESS;
3158 } else {
3159 /* here accelerator is active but we do not hold SHM lock. This means restart was scheduled
3160 or is in progress now */
3161 if (accel_activate_add() == FAILURE) { /* acquire usage lock */
3162 return FAILURE;
3163 }
3164 /* Now if we weren't inside restart, restart would not begin until we remove usage lock */
3165 if (ZCSG(restart_in_progress)) {
3166 /* we already were inside restart this means it's not safe to touch shm */
3167 accel_deactivate_now(); /* drop usage lock */
3168 return FAILURE;
3169 }
3170 ZCG(counted) = 1;
3171 }
3172 return SUCCESS;
3173 }
3174
3175 /* must be called ONLY after SUCCESSFUL accelerator_shm_read_lock */
accelerator_shm_read_unlock(void)3176 void accelerator_shm_read_unlock(void)
3177 {
3178 if (!ZCG(counted)) {
3179 /* counted is 0 - meaning we had to readlock manually, release readlock now */
3180 accel_deactivate_now();
3181 }
3182 }
3183
3184 /* Preloading */
3185 static HashTable *preload_scripts = NULL;
3186 static zend_op_array *(*preload_orig_compile_file)(zend_file_handle *file_handle, int type);
3187
preload_shutdown(void)3188 static void preload_shutdown(void)
3189 {
3190 zval *zv;
3191
3192 #if 0
3193 if (EG(zend_constants)) {
3194 ZEND_HASH_REVERSE_FOREACH_VAL(EG(zend_constants), zv) {
3195 zend_constant *c = Z_PTR_P(zv);
3196 if (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT) {
3197 break;
3198 }
3199 } ZEND_HASH_FOREACH_END_DEL();
3200 }
3201 #endif
3202
3203 if (EG(function_table)) {
3204 ZEND_HASH_REVERSE_FOREACH_VAL(EG(function_table), zv) {
3205 zend_function *func = Z_PTR_P(zv);
3206 if (func->type == ZEND_INTERNAL_FUNCTION) {
3207 break;
3208 }
3209 } ZEND_HASH_FOREACH_END_DEL();
3210 }
3211
3212 if (EG(class_table)) {
3213 ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) {
3214 zend_class_entry *ce = Z_PTR_P(zv);
3215 if (ce->type == ZEND_INTERNAL_CLASS) {
3216 break;
3217 }
3218 } ZEND_HASH_FOREACH_END_DEL();
3219 }
3220 }
3221
preload_activate(void)3222 static void preload_activate(void)
3223 {
3224 if (ZCSG(preload_script)->ping_auto_globals_mask) {
3225 zend_accel_set_auto_globals(ZCSG(preload_script)->ping_auto_globals_mask);
3226 }
3227 }
3228
preload_restart(void)3229 static void preload_restart(void)
3230 {
3231 zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL(ZCSG(preload_script)->script.filename), ZSTR_LEN(ZCSG(preload_script)->script.filename), 0, ZCSG(preload_script));
3232 if (ZCSG(saved_scripts)) {
3233 zend_persistent_script **p = ZCSG(saved_scripts);
3234 while (*p) {
3235 zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL((*p)->script.filename), ZSTR_LEN((*p)->script.filename), 0, *p);
3236 p++;
3237 }
3238 }
3239 }
3240
preload_try_strip_filename(zend_string * filename)3241 static size_t preload_try_strip_filename(zend_string *filename) {
3242 /*FIXME: better way to hanlde eval()'d code? see COMPILED_STRING_DESCRIPTION_FORMAT */
3243 if (ZSTR_LEN(filename) > sizeof(" eval()'d code")
3244 && *(ZSTR_VAL(filename) + ZSTR_LEN(filename) - sizeof(" eval()'d code")) == ':') {
3245 const char *cfilename = ZSTR_VAL(filename);
3246 size_t cfilenamelen = ZSTR_LEN(filename) - sizeof(" eval()'d code") - 1 /*:*/;
3247 while (cfilenamelen && cfilename[--cfilenamelen] != '(');
3248 return cfilenamelen;
3249 }
3250 return 0;
3251 }
3252
preload_move_user_functions(HashTable * src,HashTable * dst)3253 static void preload_move_user_functions(HashTable *src, HashTable *dst)
3254 {
3255 Bucket *p;
3256 dtor_func_t orig_dtor = src->pDestructor;
3257 zend_string *filename = NULL;
3258 int copy = 0;
3259
3260 src->pDestructor = NULL;
3261 zend_hash_extend(dst, dst->nNumUsed + src->nNumUsed, 0);
3262 ZEND_HASH_REVERSE_FOREACH_BUCKET(src, p) {
3263 zend_function *function = Z_PTR(p->val);
3264
3265 if (EXPECTED(function->type == ZEND_USER_FUNCTION)) {
3266 if (function->op_array.filename != filename) {
3267 filename = function->op_array.filename;
3268 if (filename) {
3269 if (!(copy = zend_hash_exists(preload_scripts, filename))) {
3270 size_t eval_len = preload_try_strip_filename(filename);
3271 if (eval_len) {
3272 copy = zend_hash_str_exists(preload_scripts, ZSTR_VAL(filename), eval_len);
3273 }
3274 }
3275 } else {
3276 copy = 0;
3277 }
3278 }
3279 if (copy) {
3280 _zend_hash_append_ptr(dst, p->key, function);
3281 } else {
3282 orig_dtor(&p->val);
3283 }
3284 zend_hash_del_bucket(src, p);
3285 } else {
3286 break;
3287 }
3288 } ZEND_HASH_FOREACH_END();
3289 src->pDestructor = orig_dtor;
3290 }
3291
preload_move_user_classes(HashTable * src,HashTable * dst)3292 static void preload_move_user_classes(HashTable *src, HashTable *dst)
3293 {
3294 Bucket *p;
3295 dtor_func_t orig_dtor = src->pDestructor;
3296 zend_string *filename = NULL;
3297 int copy = 0;
3298
3299 src->pDestructor = NULL;
3300 zend_hash_extend(dst, dst->nNumUsed + src->nNumUsed, 0);
3301 ZEND_HASH_REVERSE_FOREACH_BUCKET(src, p) {
3302 zend_class_entry *ce = Z_PTR(p->val);
3303
3304 if (EXPECTED(ce->type == ZEND_USER_CLASS)) {
3305 if (ce->info.user.filename != filename) {
3306 filename = ce->info.user.filename;
3307 if (filename) {
3308 if (!(copy = zend_hash_exists(preload_scripts, filename))) {
3309 size_t eval_len = preload_try_strip_filename(filename);
3310 if (eval_len) {
3311 copy = zend_hash_str_exists(preload_scripts, ZSTR_VAL(filename), eval_len);
3312 }
3313 }
3314 } else {
3315 copy = 0;
3316 }
3317 }
3318 if (copy) {
3319 _zend_hash_append(dst, p->key, &p->val);
3320 } else {
3321 orig_dtor(&p->val);
3322 }
3323 zend_hash_del_bucket(src, p);
3324 } else {
3325 break;
3326 }
3327 } ZEND_HASH_FOREACH_END();
3328 src->pDestructor = orig_dtor;
3329 }
3330
preload_compile_file(zend_file_handle * file_handle,int type)3331 static zend_op_array *preload_compile_file(zend_file_handle *file_handle, int type)
3332 {
3333 zend_op_array *op_array = preload_orig_compile_file(file_handle, type);
3334
3335 if (op_array && op_array->refcount) {
3336 zend_persistent_script *script;
3337
3338 script = create_persistent_script();
3339 script->script.first_early_binding_opline = (uint32_t)-1;
3340 script->script.filename = zend_string_copy(op_array->filename);
3341 zend_string_hash_val(script->script.filename);
3342 script->script.main_op_array = *op_array;
3343
3344 //??? efree(op_array->refcount);
3345 op_array->refcount = NULL;
3346
3347 if (op_array->static_variables &&
3348 !(GC_FLAGS(op_array->static_variables) & IS_ARRAY_IMMUTABLE)) {
3349 GC_ADDREF(op_array->static_variables);
3350 }
3351
3352 zend_hash_add_ptr(preload_scripts, script->script.filename, script);
3353 }
3354
3355 return op_array;
3356 }
3357
preload_sort_classes(void * base,size_t count,size_t siz,compare_func_t compare,swap_func_t swp)3358 static void preload_sort_classes(void *base, size_t count, size_t siz, compare_func_t compare, swap_func_t swp)
3359 {
3360 Bucket *b1 = base;
3361 Bucket *b2;
3362 Bucket *end = b1 + count;
3363 Bucket tmp;
3364 zend_class_entry *ce, *p;
3365
3366 while (b1 < end) {
3367 try_again:
3368 ce = (zend_class_entry*)Z_PTR(b1->val);
3369 if (ce->parent && (ce->ce_flags & ZEND_ACC_LINKED)) {
3370 p = ce->parent;
3371 if (p->type == ZEND_USER_CLASS) {
3372 b2 = b1 + 1;
3373 while (b2 < end) {
3374 if (p == Z_PTR(b2->val)) {
3375 tmp = *b1;
3376 *b1 = *b2;
3377 *b2 = tmp;
3378 goto try_again;
3379 }
3380 b2++;
3381 }
3382 }
3383 }
3384 if (ce->num_interfaces && (ce->ce_flags & ZEND_ACC_LINKED)) {
3385 uint32_t i = 0;
3386 for (i = 0; i < ce->num_interfaces; i++) {
3387 p = ce->interfaces[i];
3388 if (p->type == ZEND_USER_CLASS) {
3389 b2 = b1 + 1;
3390 while (b2 < end) {
3391 if (p == Z_PTR(b2->val)) {
3392 tmp = *b1;
3393 *b1 = *b2;
3394 *b2 = tmp;
3395 goto try_again;
3396 }
3397 b2++;
3398 }
3399 }
3400 }
3401 }
3402 b1++;
3403 }
3404 }
3405
get_unresolved_initializer(zend_class_entry * ce,const char ** kind,const char ** name)3406 static void get_unresolved_initializer(zend_class_entry *ce, const char **kind, const char **name) {
3407 zend_string *key;
3408 zend_class_constant *c;
3409 zend_property_info *prop;
3410
3411 *kind = "unknown";
3412 *name = "";
3413
3414 ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) {
3415 if (Z_TYPE(c->value) == IS_CONSTANT_AST) {
3416 *kind = "constant ";
3417 *name = ZSTR_VAL(key);
3418 }
3419 } ZEND_HASH_FOREACH_END();
3420 ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->properties_info, key, prop) {
3421 zval *val;
3422 if (prop->flags & ZEND_ACC_STATIC) {
3423 val = &ce->default_static_members_table[prop->offset];
3424 } else {
3425 val = &ce->default_properties_table[OBJ_PROP_TO_NUM(prop->offset)];
3426 }
3427 if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
3428 *kind = (prop->flags & ZEND_ACC_STATIC) ? "static property $" : "property $";
3429 *name = ZSTR_VAL(key);
3430 }
3431 } ZEND_HASH_FOREACH_END();
3432 }
3433
3434 static zend_bool preload_needed_types_known(zend_class_entry *ce);
get_unlinked_dependency(zend_class_entry * ce,const char ** kind,const char ** name)3435 static void get_unlinked_dependency(zend_class_entry *ce, const char **kind, const char **name) {
3436 zend_class_entry *p;
3437 *kind = "Unknown reason";
3438 *name = "";
3439
3440 if (ce->parent_name) {
3441 zend_string *key = zend_string_tolower(ce->parent_name);
3442 p = zend_hash_find_ptr(EG(class_table), key);
3443 zend_string_release(key);
3444 if (!p) {
3445 *kind = "Unknown parent ";
3446 *name = ZSTR_VAL(ce->parent_name);
3447 return;
3448 }
3449 if (!(p->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
3450 *kind = "Parent with unresolved initializers ";
3451 *name = ZSTR_VAL(ce->parent_name);
3452 return;
3453 }
3454 if (!(p->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
3455 *kind = "Parent with unresolved property types ";
3456 *name = ZSTR_VAL(ce->parent_name);
3457 return;
3458 }
3459 }
3460
3461 if (ce->num_interfaces) {
3462 uint32_t i;
3463 for (i = 0; i < ce->num_interfaces; i++) {
3464 p = zend_hash_find_ptr(EG(class_table), ce->interface_names[i].lc_name);
3465 if (!p) {
3466 *kind = "Unknown interface ";
3467 *name = ZSTR_VAL(ce->interface_names[i].name);
3468 return;
3469 }
3470 }
3471 }
3472
3473 if (ce->num_traits) {
3474 uint32_t i;
3475 for (i = 0; i < ce->num_traits; i++) {
3476 p = zend_hash_find_ptr(EG(class_table), ce->trait_names[i].lc_name);
3477 if (!p) {
3478 *kind = "Unknown trait ";
3479 *name = ZSTR_VAL(ce->trait_names[i].name);
3480 return;
3481 }
3482 }
3483 }
3484
3485 if (!preload_needed_types_known(ce)) {
3486 *kind = "Unknown type dependencies";
3487 return;
3488 }
3489 }
3490
preload_try_resolve_constants(zend_class_entry * ce)3491 static zend_bool preload_try_resolve_constants(zend_class_entry *ce)
3492 {
3493 zend_bool ok, changed;
3494 zend_class_constant *c;
3495 zval *val;
3496
3497 EG(exception) = (void*)(uintptr_t)-1; /* prevent error reporting */
3498 CG(in_compilation) = 1; /* prevent autoloading */
3499 do {
3500 ok = 1;
3501 changed = 0;
3502 ZEND_HASH_FOREACH_PTR(&ce->constants_table, c) {
3503 val = &c->value;
3504 if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
3505 if (EXPECTED(zval_update_constant_ex(val, c->ce) == SUCCESS)) {
3506 changed = 1;
3507 } else {
3508 ok = 0;
3509 }
3510 }
3511 } ZEND_HASH_FOREACH_END();
3512 if (ce->default_properties_count) {
3513 uint32_t i;
3514 for (i = 0; i < ce->default_properties_count; i++) {
3515 val = &ce->default_properties_table[i];
3516 if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
3517 zend_property_info *prop = ce->properties_info_table[i];
3518 if (UNEXPECTED(zval_update_constant_ex(val, prop->ce) != SUCCESS)) {
3519 ok = 0;
3520 }
3521 }
3522 }
3523 }
3524 if (ce->default_static_members_count) {
3525 uint32_t count = ce->parent ? ce->default_static_members_count - ce->parent->default_static_members_count : ce->default_static_members_count;
3526
3527 val = ce->default_static_members_table + ce->default_static_members_count - 1;
3528 while (count) {
3529 if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
3530 if (UNEXPECTED(zval_update_constant_ex(val, ce) != SUCCESS)) {
3531 ok = 0;
3532 }
3533 }
3534 val--;
3535 count--;
3536 }
3537 }
3538 } while (changed && !ok);
3539 EG(exception) = NULL;
3540 CG(in_compilation) = 0;
3541
3542 return ok;
3543 }
3544
preload_try_resolve_property_types(zend_class_entry * ce)3545 static zend_bool preload_try_resolve_property_types(zend_class_entry *ce)
3546 {
3547 zend_bool ok = 1;
3548 zend_property_info *prop;
3549 zend_class_entry *p;
3550
3551 if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) {
3552 ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) {
3553 zend_string *name, *lcname;
3554
3555 if (!ZEND_TYPE_IS_NAME(prop->type)) {
3556 continue;
3557 }
3558
3559 name = ZEND_TYPE_NAME(prop->type);
3560 lcname = zend_string_tolower(name);
3561 p = zend_hash_find_ptr(EG(class_table), lcname);
3562 zend_string_release(lcname);
3563 if (!p) {
3564 ok = 0;
3565 continue;
3566 }
3567 if (p != ce) {
3568 if (!(p->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
3569 ok = 0;
3570 continue;
3571 }
3572 if (!(p->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
3573 ok = 0;
3574 continue;
3575 }
3576 }
3577
3578 zend_string_release(name);
3579 prop->type = ZEND_TYPE_ENCODE_CE(p, ZEND_TYPE_ALLOW_NULL(prop->type));
3580 } ZEND_HASH_FOREACH_END();
3581 }
3582
3583 return ok;
3584 }
3585
preload_is_type_known(zend_class_entry * ce,zend_type type)3586 static zend_bool preload_is_type_known(zend_class_entry *ce, zend_type type) {
3587 zend_string *name, *lcname;
3588 zend_bool known;
3589 if (!ZEND_TYPE_IS_NAME(type)) {
3590 return 1;
3591 }
3592
3593 name = ZEND_TYPE_NAME(type);
3594 if (zend_string_equals_literal_ci(name, "self") ||
3595 zend_string_equals_literal_ci(name, "parent") ||
3596 zend_string_equals_ci(name, ce->name)) {
3597 return 1;
3598 }
3599
3600 lcname = zend_string_tolower(name);
3601 known = zend_hash_exists(EG(class_table), lcname);
3602 zend_string_release(lcname);
3603 return known;
3604 }
3605
preload_is_method_maybe_override(zend_class_entry * ce,zend_string * lcname)3606 static zend_bool preload_is_method_maybe_override(zend_class_entry *ce, zend_string *lcname) {
3607 zend_class_entry *p;
3608 if (ce->trait_aliases || ce->trait_precedences) {
3609 return 1;
3610 }
3611
3612 if (ce->parent_name) {
3613 zend_string *key = zend_string_tolower(ce->parent_name);
3614 p = zend_hash_find_ptr(EG(class_table), key);
3615 zend_string_release(key);
3616 if (zend_hash_exists(&p->function_table, lcname)) {
3617 return 1;
3618 }
3619 }
3620
3621 if (ce->num_interfaces) {
3622 uint32_t i;
3623 for (i = 0; i < ce->num_interfaces; i++) {
3624 zend_class_entry *p = zend_hash_find_ptr(EG(class_table), ce->interface_names[i].lc_name);
3625 if (zend_hash_exists(&p->function_table, lcname)) {
3626 return 1;
3627 }
3628 }
3629 }
3630
3631 if (ce->num_traits) {
3632 uint32_t i;
3633 for (i = 0; i < ce->num_traits; i++) {
3634 zend_class_entry *p = zend_hash_find_ptr(EG(class_table), ce->trait_names[i].lc_name);
3635 if (zend_hash_exists(&p->function_table, lcname)) {
3636 return 1;
3637 }
3638 }
3639 }
3640
3641 return 0;
3642 }
3643
preload_needed_types_known(zend_class_entry * ce)3644 static zend_bool preload_needed_types_known(zend_class_entry *ce) {
3645 zend_function *fptr;
3646 zend_string *lcname;
3647 ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->function_table, lcname, fptr) {
3648 uint32_t i;
3649 if (fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
3650 if (!preload_is_type_known(ce, fptr->common.arg_info[-1].type) &&
3651 preload_is_method_maybe_override(ce, lcname)) {
3652 return 0;
3653 }
3654 }
3655 for (i = 0; i < fptr->common.num_args; i++) {
3656 if (!preload_is_type_known(ce, fptr->common.arg_info[i].type) &&
3657 preload_is_method_maybe_override(ce, lcname)) {
3658 return 0;
3659 }
3660 }
3661 } ZEND_HASH_FOREACH_END();
3662 return 1;
3663 }
3664
preload_link(void)3665 static void preload_link(void)
3666 {
3667 zval *zv;
3668 zend_persistent_script *script;
3669 zend_class_entry *ce, *parent, *p;
3670 zend_string *key;
3671 zend_bool found, changed;
3672 uint32_t i;
3673 dtor_func_t orig_dtor;
3674 zend_function *function;
3675
3676 /* Resolve class dependencies */
3677 do {
3678 changed = 0;
3679
3680 ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) {
3681 ce = Z_PTR_P(zv);
3682 if (ce->type == ZEND_INTERNAL_CLASS) {
3683 break;
3684 }
3685 if ((ce->ce_flags & (ZEND_ACC_TOP_LEVEL|ZEND_ACC_ANON_CLASS))
3686 && !(ce->ce_flags & ZEND_ACC_LINKED)) {
3687
3688 if (!(ce->ce_flags & ZEND_ACC_ANON_CLASS)) {
3689 key = zend_string_tolower(ce->name);
3690 if (zend_hash_exists(EG(class_table), key)) {
3691 zend_string_release(key);
3692 continue;
3693 }
3694 zend_string_release(key);
3695 }
3696
3697 parent = NULL;
3698
3699 if (ce->parent_name) {
3700 key = zend_string_tolower(ce->parent_name);
3701 parent = zend_hash_find_ptr(EG(class_table), key);
3702 zend_string_release(key);
3703 if (!parent) continue;
3704 if (!(parent->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
3705 continue;
3706 }
3707 if (!(parent->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
3708 continue;
3709 }
3710 }
3711
3712 if (ce->num_interfaces) {
3713 found = 1;
3714 for (i = 0; i < ce->num_interfaces; i++) {
3715 p = zend_hash_find_ptr(EG(class_table), ce->interface_names[i].lc_name);
3716 if (!p) {
3717 found = 0;
3718 break;
3719 }
3720 if (!(p->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
3721 found = 0;
3722 break;
3723 }
3724 }
3725 if (!found) continue;
3726 }
3727
3728 if (ce->num_traits) {
3729 found = 1;
3730 for (i = 0; i < ce->num_traits; i++) {
3731 p = zend_hash_find_ptr(EG(class_table), ce->trait_names[i].lc_name);
3732 if (!p) {
3733 found = 0;
3734 break;
3735 }
3736 }
3737 if (!found) continue;
3738 }
3739
3740 /* TODO: This is much more restrictive than necessary. We only need to actually
3741 * know the types for covariant checks, but don't need them if we can ensure
3742 * compatibility through a simple string comparison. We could improve this using
3743 * a more general version of zend_can_early_bind(). */
3744 if (!preload_needed_types_known(ce)) {
3745 continue;
3746 }
3747
3748 {
3749 zend_string *key = zend_string_tolower(ce->name);
3750 zv = zend_hash_set_bucket_key(EG(class_table), (Bucket*)zv, key);
3751 zend_string_release(key);
3752 }
3753
3754 if (EXPECTED(zv)) {
3755 /* Set filename & lineno information for inheritance errors */
3756 CG(in_compilation) = 1;
3757 CG(compiled_filename) = ce->info.user.filename;
3758 if (ce->parent_name
3759 && !ce->num_interfaces
3760 && !ce->num_traits
3761 && (parent->type == ZEND_INTERNAL_CLASS
3762 || parent->info.user.filename == ce->info.user.filename)) {
3763 /* simulate early binding */
3764 CG(zend_lineno) = ce->info.user.line_end;
3765 } else {
3766 CG(zend_lineno) = ce->info.user.line_start;
3767 }
3768 if (zend_do_link_class(ce, NULL) == FAILURE) {
3769 ZEND_ASSERT(0 && "Class linking failed?");
3770 }
3771 CG(in_compilation) = 0;
3772 CG(compiled_filename) = NULL;
3773
3774 changed = 1;
3775 }
3776 }
3777 if (ce->ce_flags & ZEND_ACC_LINKED) {
3778 if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
3779 if ((ce->ce_flags & ZEND_ACC_TRAIT) /* don't update traits */
3780 || preload_try_resolve_constants(ce)) {
3781 ce->ce_flags |= ZEND_ACC_CONSTANTS_UPDATED;
3782 changed = 1;
3783 }
3784 }
3785
3786 if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
3787 if ((ce->ce_flags & ZEND_ACC_TRAIT) /* don't update traits */
3788 || preload_try_resolve_property_types(ce)) {
3789 ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED;
3790 changed = 1;
3791 }
3792 }
3793 }
3794 } ZEND_HASH_FOREACH_END();
3795 } while (changed);
3796
3797 /* Move unlinked clases (and with unresilved constants) back to scripts */
3798 orig_dtor = EG(class_table)->pDestructor;
3799 EG(class_table)->pDestructor = NULL;
3800 ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(EG(class_table), key, zv) {
3801 ce = Z_PTR_P(zv);
3802 if (ce->type == ZEND_INTERNAL_CLASS) {
3803 break;
3804 }
3805 if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
3806 zend_string *key = zend_string_tolower(ce->name);
3807 if (!(ce->ce_flags & ZEND_ACC_ANON_CLASS)
3808 && zend_hash_exists(EG(class_table), key)) {
3809 zend_error_at(
3810 E_WARNING, ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start,
3811 "Can't preload already declared class %s", ZSTR_VAL(ce->name));
3812 } else {
3813 const char *kind, *name;
3814 get_unlinked_dependency(ce, &kind, &name);
3815 zend_error_at(
3816 E_WARNING, ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start,
3817 "Can't preload unlinked class %s: %s%s",
3818 ZSTR_VAL(ce->name), kind, name);
3819 }
3820 zend_string_release(key);
3821 } else if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
3822 const char *kind, *name;
3823 get_unresolved_initializer(ce, &kind, &name);
3824 zend_error_at(
3825 E_WARNING, ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start,
3826 "Can't preload class %s with unresolved initializer for %s%s",
3827 ZSTR_VAL(ce->name), kind, name);
3828 } else if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
3829 zend_error_at(
3830 E_WARNING, ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start,
3831 "Can't preload class %s with unresolved property types",
3832 ZSTR_VAL(ce->name));
3833 } else {
3834 continue;
3835 }
3836 ce->ce_flags &= ~ZEND_ACC_PRELOADED;
3837 ZEND_HASH_FOREACH_PTR(&ce->function_table, function) {
3838 if (EXPECTED(function->type == ZEND_USER_FUNCTION)
3839 && function->common.scope == ce) {
3840 function->common.fn_flags &= ~ZEND_ACC_PRELOADED;
3841 }
3842 } ZEND_HASH_FOREACH_END();
3843 script = zend_hash_find_ptr(preload_scripts, ce->info.user.filename);
3844 ZEND_ASSERT(script);
3845 zend_hash_add(&script->script.class_table, key, zv);
3846 ZVAL_UNDEF(zv);
3847 zend_string_release(key);
3848 EG(class_table)->nNumOfElements--;
3849 } ZEND_HASH_FOREACH_END();
3850 EG(class_table)->pDestructor = orig_dtor;
3851 zend_hash_rehash(EG(class_table));
3852
3853 /* Remove DECLARE opcodes */
3854 ZEND_HASH_FOREACH_PTR(preload_scripts, script) {
3855 zend_op_array *op_array = &script->script.main_op_array;
3856 zend_op *opline = op_array->opcodes;
3857 zend_op *end = opline + op_array->last;
3858
3859 while (opline != end) {
3860 switch (opline->opcode) {
3861 case ZEND_DECLARE_CLASS:
3862 case ZEND_DECLARE_CLASS_DELAYED:
3863 key = Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1);
3864 if (!zend_hash_exists(&script->script.class_table, key)) {
3865 MAKE_NOP(opline);
3866 }
3867 break;
3868 }
3869 opline++;
3870 }
3871
3872 if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) {
3873 script->script.first_early_binding_opline = zend_build_delayed_early_binding_list(op_array);
3874 if (script->script.first_early_binding_opline == (uint32_t)-1) {
3875 op_array->fn_flags &= ~ZEND_ACC_EARLY_BINDING;
3876 }
3877 }
3878 } ZEND_HASH_FOREACH_END();
3879 }
3880
preload_update_class_constants(zend_class_entry * ce)3881 static inline int preload_update_class_constants(zend_class_entry *ce) {
3882 /* This is a separate function to work around what appears to be a bug in GCC
3883 * maybe-uninitialized analysis. */
3884 int result;
3885 zend_try {
3886 result = zend_update_class_constants(ce);
3887 } zend_catch {
3888 result = FAILURE;
3889 } zend_end_try();
3890 return result;
3891 }
3892
preload_load_prop_type(zend_property_info * prop,zend_string * name)3893 static zend_class_entry *preload_load_prop_type(zend_property_info *prop, zend_string *name) {
3894 zend_class_entry *ce;
3895 if (zend_string_equals_literal_ci(name, "self")) {
3896 ce = prop->ce;
3897 } else if (zend_string_equals_literal_ci(name, "parent")) {
3898 ce = prop->ce->parent;
3899 } else {
3900 ce = zend_lookup_class(name);
3901 }
3902 if (ce) {
3903 return ce;
3904 }
3905
3906 zend_error_noreturn(E_ERROR,
3907 "Failed to load class %s used by typed property %s::$%s during preloading",
3908 ZSTR_VAL(name), ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name));
3909 return ce;
3910 }
3911
preload_ensure_classes_loadable()3912 static void preload_ensure_classes_loadable() {
3913 /* Run this in a loop, because additional classes may be loaded while updating constants etc. */
3914 uint32_t checked_classes_idx = 0;
3915 while (1) {
3916 zend_class_entry *ce;
3917 uint32_t num_classes = zend_hash_num_elements(EG(class_table));
3918 if (num_classes == checked_classes_idx) {
3919 return;
3920 }
3921
3922 ZEND_HASH_REVERSE_FOREACH_PTR(EG(class_table), ce) {
3923 if (ce->type == ZEND_INTERNAL_CLASS || _idx == checked_classes_idx) {
3924 break;
3925 }
3926
3927 if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
3928 /* Only require that already linked classes are loadable, we'll properly check
3929 * things when linking additional classes. */
3930 continue;
3931 }
3932
3933 if (!(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
3934 if (preload_update_class_constants(ce) == FAILURE) {
3935 zend_error_noreturn(E_ERROR,
3936 "Failed to resolve initializers of class %s during preloading",
3937 ZSTR_VAL(ce->name));
3938 }
3939 ZEND_ASSERT(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED);
3940 }
3941
3942 if (!(ce->ce_flags & ZEND_ACC_PROPERTY_TYPES_RESOLVED)) {
3943 if (ce->ce_flags & ZEND_ACC_HAS_TYPE_HINTS) {
3944 zend_property_info *prop;
3945 ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) {
3946 if (ZEND_TYPE_IS_NAME(prop->type)) {
3947 zend_class_entry *ce =
3948 preload_load_prop_type(prop, ZEND_TYPE_NAME(prop->type));
3949 prop->type = ZEND_TYPE_ENCODE_CE(ce, ZEND_TYPE_ALLOW_NULL(prop->type));
3950 }
3951 } ZEND_HASH_FOREACH_END();
3952 }
3953 ce->ce_flags |= ZEND_ACC_PROPERTY_TYPES_RESOLVED;
3954 }
3955 } ZEND_HASH_FOREACH_END();
3956 checked_classes_idx = num_classes;
3957 }
3958 }
3959
preload_resolve_path(zend_string * filename)3960 static zend_string *preload_resolve_path(zend_string *filename)
3961 {
3962 if (is_stream_path(ZSTR_VAL(filename))) {
3963 return NULL;
3964 }
3965 return zend_resolve_path(ZSTR_VAL(filename), ZSTR_LEN(filename));
3966 }
3967
preload_remove_empty_includes(void)3968 static void preload_remove_empty_includes(void)
3969 {
3970 zend_persistent_script *script;
3971 zend_bool changed;
3972
3973 /* mark all as empty */
3974 ZEND_HASH_FOREACH_PTR(preload_scripts, script) {
3975 script->empty = 1;
3976 } ZEND_HASH_FOREACH_END();
3977
3978 /* find non empty scripts */
3979 do {
3980 changed = 0;
3981 ZEND_HASH_FOREACH_PTR(preload_scripts, script) {
3982 if (script->empty) {
3983 int empty = 1;
3984 zend_op *opline = script->script.main_op_array.opcodes;
3985 zend_op *end = opline + script->script.main_op_array.last;
3986
3987 while (opline < end) {
3988 if (opline->opcode == ZEND_INCLUDE_OR_EVAL &&
3989 opline->extended_value != ZEND_EVAL &&
3990 opline->op1_type == IS_CONST &&
3991 Z_TYPE_P(RT_CONSTANT(opline, opline->op1)) == IS_STRING) {
3992
3993 zend_string *resolved_path = preload_resolve_path(Z_STR_P(RT_CONSTANT(opline, opline->op1)));
3994
3995 if (resolved_path) {
3996 zend_persistent_script *incl = zend_hash_find_ptr(preload_scripts, resolved_path);
3997 zend_string_release(resolved_path);
3998 if (!incl || !incl->empty) {
3999 empty = 0;
4000 break;
4001 }
4002 } else {
4003 empty = 0;
4004 break;
4005 }
4006 } else if (opline->opcode != ZEND_NOP &&
4007 opline->opcode != ZEND_RETURN &&
4008 opline->opcode != ZEND_HANDLE_EXCEPTION) {
4009 empty = 0;
4010 break;
4011 }
4012 opline++;
4013 }
4014 if (!empty) {
4015 script->empty = 0;
4016 changed = 1;
4017 }
4018 }
4019 } ZEND_HASH_FOREACH_END();
4020 } while (changed);
4021
4022 /* remove empty includes */
4023 ZEND_HASH_FOREACH_PTR(preload_scripts, script) {
4024 zend_op *opline = script->script.main_op_array.opcodes;
4025 zend_op *end = opline + script->script.main_op_array.last;
4026
4027 while (opline < end) {
4028 if (opline->opcode == ZEND_INCLUDE_OR_EVAL &&
4029 opline->extended_value != ZEND_EVAL &&
4030 opline->op1_type == IS_CONST &&
4031 Z_TYPE_P(RT_CONSTANT(opline, opline->op1)) == IS_STRING) {
4032
4033 zend_string *resolved_path = preload_resolve_path(Z_STR_P(RT_CONSTANT(opline, opline->op1)));
4034
4035 if (resolved_path) {
4036 zend_persistent_script *incl = zend_hash_find_ptr(preload_scripts, resolved_path);
4037 if (incl && incl->empty) {
4038 MAKE_NOP(opline);
4039 } else {
4040 if (!IS_ABSOLUTE_PATH(Z_STRVAL_P(RT_CONSTANT(opline, opline->op1)), Z_STRLEN_P(RT_CONSTANT(opline, opline->op1)))) {
4041 /* replace relative patch with absolute one */
4042 zend_string_release(Z_STR_P(RT_CONSTANT(opline, opline->op1)));
4043 ZVAL_STR_COPY(RT_CONSTANT(opline, opline->op1), resolved_path);
4044 }
4045 }
4046 zend_string_release(resolved_path);
4047 }
4048 }
4049 opline++;
4050 }
4051 } ZEND_HASH_FOREACH_END();
4052 }
4053
preload_register_trait_methods(zend_class_entry * ce)4054 static void preload_register_trait_methods(zend_class_entry *ce) {
4055 zend_op_array *op_array;
4056 ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
4057 if (!(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
4058 ZEND_ASSERT(op_array->refcount && "Must have refcount pointer");
4059 zend_shared_alloc_register_xlat_entry(op_array->refcount, op_array);
4060 }
4061 } ZEND_HASH_FOREACH_END();
4062 }
4063
preload_fix_trait_methods(zend_class_entry * ce)4064 static void preload_fix_trait_methods(zend_class_entry *ce)
4065 {
4066 zend_op_array *op_array;
4067
4068 ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
4069 if (op_array->fn_flags & ZEND_ACC_TRAIT_CLONE) {
4070 zend_op_array *orig_op_array = zend_shared_alloc_get_xlat_entry(op_array->refcount);
4071 ZEND_ASSERT(orig_op_array && "Must be in xlat table");
4072
4073 zend_class_entry *scope = op_array->scope;
4074 uint32_t fn_flags = op_array->fn_flags;
4075 zend_function *prototype = op_array->prototype;
4076 HashTable *ht = op_array->static_variables;
4077 *op_array = *orig_op_array;
4078 op_array->scope = scope;
4079 op_array->fn_flags = fn_flags;
4080 op_array->prototype = prototype;
4081 op_array->static_variables = ht;
4082 }
4083 } ZEND_HASH_FOREACH_END();
4084 }
4085
preload_optimize(zend_persistent_script * script)4086 static int preload_optimize(zend_persistent_script *script)
4087 {
4088 zend_class_entry *ce;
4089 zend_persistent_script *tmp_script;
4090
4091 zend_shared_alloc_init_xlat_table();
4092
4093 ZEND_HASH_FOREACH_PTR(&script->script.class_table, ce) {
4094 if (ce->ce_flags & ZEND_ACC_TRAIT) {
4095 preload_register_trait_methods(ce);
4096 }
4097 } ZEND_HASH_FOREACH_END();
4098
4099 ZEND_HASH_FOREACH_PTR(preload_scripts, tmp_script) {
4100 ZEND_HASH_FOREACH_PTR(&tmp_script->script.class_table, ce) {
4101 if (ce->ce_flags & ZEND_ACC_TRAIT) {
4102 preload_register_trait_methods(ce);
4103 }
4104 } ZEND_HASH_FOREACH_END();
4105 } ZEND_HASH_FOREACH_END();
4106
4107 if (!zend_optimize_script(&script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level)) {
4108 return FAILURE;
4109 }
4110
4111 ZEND_HASH_FOREACH_PTR(&script->script.class_table, ce) {
4112 preload_fix_trait_methods(ce);
4113 } ZEND_HASH_FOREACH_END();
4114
4115 ZEND_HASH_FOREACH_PTR(preload_scripts, script) {
4116 ZEND_HASH_FOREACH_PTR(&script->script.class_table, ce) {
4117 preload_fix_trait_methods(ce);
4118 } ZEND_HASH_FOREACH_END();
4119 } ZEND_HASH_FOREACH_END();
4120
4121 zend_shared_alloc_destroy_xlat_table();
4122
4123 ZEND_HASH_FOREACH_PTR(preload_scripts, script) {
4124 if (!zend_optimize_script(&script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level)) {
4125 return FAILURE;
4126 }
4127 } ZEND_HASH_FOREACH_END();
4128 return SUCCESS;
4129 }
4130
preload_script_in_shared_memory(zend_persistent_script * new_persistent_script)4131 static zend_persistent_script* preload_script_in_shared_memory(zend_persistent_script *new_persistent_script)
4132 {
4133 zend_accel_hash_entry *bucket;
4134 uint32_t memory_used;
4135 uint32_t checkpoint;
4136
4137 if (zend_accel_hash_is_full(&ZCSG(hash))) {
4138 zend_accel_error(ACCEL_LOG_FATAL, "Not enough entries in hash table for preloading. Consider increasing the value for the opcache.max_accelerated_files directive in php.ini.");
4139 return NULL;
4140 }
4141
4142 checkpoint = zend_shared_alloc_checkpoint_xlat_table();
4143
4144 /* Calculate the required memory size */
4145 memory_used = zend_accel_script_persist_calc(new_persistent_script, NULL, 0, 1);
4146
4147 /* Allocate shared memory */
4148 #if defined(__AVX__) || defined(__SSE2__)
4149 /* Align to 64-byte boundary */
4150 ZCG(mem) = zend_shared_alloc(memory_used + 64);
4151 if (ZCG(mem)) {
4152 ZCG(mem) = (void*)(((zend_uintptr_t)ZCG(mem) + 63L) & ~63L);
4153 #if defined(__x86_64__)
4154 memset(ZCG(mem), 0, memory_used);
4155 #elif defined(__AVX__)
4156 {
4157 char *p = (char*)ZCG(mem);
4158 char *end = p + memory_used;
4159 __m256i ymm0 = _mm256_setzero_si256();
4160
4161 while (p < end) {
4162 _mm256_store_si256((__m256i*)p, ymm0);
4163 _mm256_store_si256((__m256i*)(p+32), ymm0);
4164 p += 64;
4165 }
4166 }
4167 #else
4168 {
4169 char *p = (char*)ZCG(mem);
4170 char *end = p + memory_used;
4171 __m128i xmm0 = _mm_setzero_si128();
4172
4173 while (p < end) {
4174 _mm_store_si128((__m128i*)p, xmm0);
4175 _mm_store_si128((__m128i*)(p+16), xmm0);
4176 _mm_store_si128((__m128i*)(p+32), xmm0);
4177 _mm_store_si128((__m128i*)(p+48), xmm0);
4178 p += 64;
4179 }
4180 }
4181 #endif
4182 }
4183 #else
4184 ZCG(mem) = zend_shared_alloc(memory_used);
4185 if (ZCG(mem)) {
4186 memset(ZCG(mem), 0, memory_used);
4187 }
4188 #endif
4189 if (!ZCG(mem)) {
4190 zend_accel_error(ACCEL_LOG_FATAL, "Not enough shared memory for preloading. Consider increasing the value for the opcache.memory_consumption directive in php.ini.");
4191 return NULL;
4192 }
4193
4194 zend_shared_alloc_restore_xlat_table(checkpoint);
4195
4196 /* Copy into shared memory */
4197 new_persistent_script = zend_accel_script_persist(new_persistent_script, NULL, 0, 1);
4198
4199 new_persistent_script->is_phar = is_phar_file(new_persistent_script->script.filename);
4200
4201 /* Consistency check */
4202 if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
4203 zend_accel_error(
4204 ((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
4205 "Internal error: wrong size calculation: %s start=" ZEND_ADDR_FMT ", end=" ZEND_ADDR_FMT ", real=" ZEND_ADDR_FMT "\n",
4206 ZSTR_VAL(new_persistent_script->script.filename),
4207 (size_t)new_persistent_script->mem,
4208 (size_t)((char *)new_persistent_script->mem + new_persistent_script->size),
4209 (size_t)ZCG(mem));
4210 }
4211
4212 new_persistent_script->dynamic_members.checksum = zend_accel_script_checksum(new_persistent_script);
4213
4214 /* store script structure in the hash table */
4215 bucket = zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL(new_persistent_script->script.filename), ZSTR_LEN(new_persistent_script->script.filename), 0, new_persistent_script);
4216 if (bucket) {
4217 zend_accel_error(ACCEL_LOG_INFO, "Cached script '%s'", ZSTR_VAL(new_persistent_script->script.filename));
4218 }
4219
4220 new_persistent_script->dynamic_members.memory_consumption = ZEND_ALIGNED_SIZE(new_persistent_script->size);
4221
4222 return new_persistent_script;
4223 }
4224
preload_load(void)4225 static void preload_load(void)
4226 {
4227 /* Load into process tables */
4228 zend_script *script = &ZCSG(preload_script)->script;
4229 if (zend_hash_num_elements(&script->function_table)) {
4230 Bucket *p = script->function_table.arData;
4231 Bucket *end = p + script->function_table.nNumUsed;
4232
4233 zend_hash_extend(CG(function_table),
4234 CG(function_table)->nNumUsed + script->function_table.nNumUsed, 0);
4235 for (; p != end; p++) {
4236 _zend_hash_append_ptr_ex(CG(function_table), p->key, Z_PTR(p->val), 1);
4237 }
4238 }
4239
4240 if (zend_hash_num_elements(&script->class_table)) {
4241 Bucket *p = script->class_table.arData;
4242 Bucket *end = p + script->class_table.nNumUsed;
4243
4244 zend_hash_extend(CG(class_table),
4245 CG(class_table)->nNumUsed + script->class_table.nNumUsed, 0);
4246 for (; p != end; p++) {
4247 _zend_hash_append_ex(CG(class_table), p->key, &p->val, 1);
4248 }
4249 }
4250
4251 if (EG(zend_constants)) {
4252 EG(persistent_constants_count) = EG(zend_constants)->nNumUsed;
4253 }
4254 if (EG(function_table)) {
4255 EG(persistent_functions_count) = EG(function_table)->nNumUsed;
4256 }
4257 if (EG(class_table)) {
4258 EG(persistent_classes_count) = EG(class_table)->nNumUsed;
4259 }
4260 if (CG(map_ptr_last) != ZCSG(map_ptr_last)) {
4261 size_t old_map_ptr_last = CG(map_ptr_last);
4262 CG(map_ptr_last) = ZCSG(map_ptr_last);
4263 CG(map_ptr_size) = ZEND_MM_ALIGNED_SIZE_EX(CG(map_ptr_last) + 1, 4096);
4264 CG(map_ptr_base) = perealloc(CG(map_ptr_base), CG(map_ptr_size) * sizeof(void*), 1);
4265 memset((void **) CG(map_ptr_base) + old_map_ptr_last, 0,
4266 (CG(map_ptr_last) - old_map_ptr_last) * sizeof(void *));
4267 }
4268 }
4269
preload_autoload(zend_string * filename)4270 static int preload_autoload(zend_string *filename)
4271 {
4272 zend_persistent_script *persistent_script;
4273 zend_op_array *op_array;
4274 zend_execute_data *old_execute_data;
4275 zend_class_entry *old_fake_scope;
4276 zend_bool do_bailout = 0;
4277 int ret = SUCCESS;
4278
4279 if (zend_hash_exists(&EG(included_files), filename)) {
4280 return FAILURE;
4281 }
4282
4283 persistent_script = zend_accel_hash_find(&ZCSG(hash), filename);
4284 if (!persistent_script) {
4285 return FAILURE;
4286 }
4287
4288 zend_hash_add_empty_element(&EG(included_files), filename);
4289
4290 if (persistent_script->ping_auto_globals_mask) {
4291 zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask);
4292 }
4293
4294 op_array = zend_accel_load_script(persistent_script, 1);
4295 if (!op_array) {
4296 return FAILURE;
4297 }
4298
4299 /* Execute in global context */
4300 old_execute_data = EG(current_execute_data);
4301 EG(current_execute_data) = NULL;
4302 old_fake_scope = EG(fake_scope);
4303 EG(fake_scope) = NULL;
4304 zend_exception_save();
4305
4306 zend_try {
4307 zend_execute(op_array, NULL);
4308 } zend_catch {
4309 do_bailout = 1;
4310 } zend_end_try();
4311
4312 if (EG(exception)) {
4313 ret = FAILURE;
4314 }
4315
4316 zend_exception_restore();
4317 EG(fake_scope) = old_fake_scope;
4318 EG(current_execute_data) = old_execute_data;
4319 while (old_execute_data) {
4320 if (old_execute_data->func && (ZEND_CALL_INFO(old_execute_data) & ZEND_CALL_HAS_SYMBOL_TABLE)) {
4321 if (old_execute_data->symbol_table == &EG(symbol_table)) {
4322 zend_attach_symbol_table(old_execute_data);
4323 }
4324 break;
4325 }
4326 old_execute_data = old_execute_data->prev_execute_data;
4327 }
4328
4329 destroy_op_array(op_array);
4330 efree_size(op_array, sizeof(zend_op_array));
4331
4332 if (do_bailout) {
4333 zend_bailout();
4334 }
4335
4336 return ret;
4337 }
4338
accel_preload(const char * config)4339 static int accel_preload(const char *config)
4340 {
4341 zend_file_handle file_handle;
4342 int ret;
4343 char *orig_open_basedir;
4344 size_t orig_map_ptr_last;
4345 zval *zv;
4346
4347 ZCG(enabled) = 0;
4348 ZCG(accelerator_enabled) = 0;
4349 orig_open_basedir = PG(open_basedir);
4350 PG(open_basedir) = NULL;
4351 preload_orig_compile_file = accelerator_orig_compile_file;
4352 accelerator_orig_compile_file = preload_compile_file;
4353
4354 orig_map_ptr_last = CG(map_ptr_last);
4355
4356 /* Compile and execute proloading script */
4357 zend_stream_init_filename(&file_handle, (char *) config);
4358
4359 preload_scripts = emalloc(sizeof(HashTable));
4360 zend_hash_init(preload_scripts, 0, NULL, NULL, 0);
4361
4362 zend_try {
4363 zend_op_array *op_array;
4364
4365 ret = SUCCESS;
4366 op_array = zend_compile_file(&file_handle, ZEND_REQUIRE);
4367 if (file_handle.opened_path) {
4368 zend_hash_add_empty_element(&EG(included_files), file_handle.opened_path);
4369 }
4370 zend_destroy_file_handle(&file_handle);
4371 if (op_array) {
4372 zend_execute(op_array, NULL);
4373 zend_exception_restore();
4374 if (UNEXPECTED(EG(exception))) {
4375 if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) {
4376 zend_user_exception_handler();
4377 }
4378 if (EG(exception)) {
4379 zend_exception_error(EG(exception), E_ERROR);
4380 CG(unclean_shutdown) = 1;
4381 ret = FAILURE;
4382 }
4383 }
4384 destroy_op_array(op_array);
4385 efree_size(op_array, sizeof(zend_op_array));
4386 } else {
4387 CG(unclean_shutdown) = 1;
4388 ret = FAILURE;
4389 }
4390
4391 if (ret == SUCCESS) {
4392 preload_ensure_classes_loadable();
4393 }
4394 } zend_catch {
4395 ret = FAILURE;
4396 } zend_end_try();
4397
4398 PG(open_basedir) = orig_open_basedir;
4399 accelerator_orig_compile_file = preload_orig_compile_file;
4400 ZCG(enabled) = 1;
4401
4402 zend_destroy_file_handle(&file_handle);
4403
4404 if (ret == SUCCESS) {
4405 zend_persistent_script *script;
4406 zend_string *filename;
4407 int ping_auto_globals_mask;
4408 int i;
4409 zend_class_entry *ce;
4410 zend_op_array *op_array;
4411
4412 if (PG(auto_globals_jit)) {
4413 ping_auto_globals_mask = zend_accel_get_auto_globals();
4414 } else {
4415 ping_auto_globals_mask = zend_accel_get_auto_globals_no_jit();
4416 }
4417
4418 /* Cleanup executor */
4419 EG(flags) |= EG_FLAGS_IN_SHUTDOWN;
4420
4421 php_call_shutdown_functions();
4422 zend_call_destructors();
4423 php_free_shutdown_functions();
4424
4425 /* Release stored values to avoid dangling pointers */
4426 zend_hash_graceful_reverse_destroy(&EG(symbol_table));
4427 zend_hash_init(&EG(symbol_table), 0, NULL, ZVAL_PTR_DTOR, 0);
4428
4429 #if ZEND_DEBUG
4430 if (gc_enabled() && !CG(unclean_shutdown)) {
4431 gc_collect_cycles();
4432 }
4433 #endif
4434
4435 zend_objects_store_free_object_storage(&EG(objects_store), 1);
4436
4437 /* Cleanup static variables of preloaded functions */
4438 ZEND_HASH_REVERSE_FOREACH_VAL(EG(function_table), zv) {
4439 zend_op_array *op_array = Z_PTR_P(zv);
4440 if (op_array->type == ZEND_INTERNAL_FUNCTION) {
4441 break;
4442 }
4443 ZEND_ASSERT(op_array->fn_flags & ZEND_ACC_PRELOADED);
4444 if (op_array->static_variables) {
4445 HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr);
4446 if (ht) {
4447 ZEND_ASSERT(GC_REFCOUNT(ht) == 1);
4448 zend_array_destroy(ht);
4449 ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL);
4450 }
4451 }
4452 } ZEND_HASH_FOREACH_END();
4453
4454 /* Cleanup static properties and variables of preloaded classes */
4455 ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) {
4456 zend_class_entry *ce = Z_PTR_P(zv);
4457 if (ce->type == ZEND_INTERNAL_CLASS) {
4458 break;
4459 }
4460 ZEND_ASSERT(ce->ce_flags & ZEND_ACC_PRELOADED);
4461 if (ce->default_static_members_count) {
4462 zend_cleanup_internal_class_data(ce);
4463 if (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) {
4464 int i;
4465
4466 for (i = 0; i < ce->default_static_members_count; i++) {
4467 if (Z_TYPE(ce->default_static_members_table[i]) == IS_CONSTANT_AST) {
4468 ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
4469 break;
4470 }
4471 }
4472 }
4473 }
4474 if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) {
4475 zend_op_array *op_array;
4476
4477 ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
4478 if (op_array->type == ZEND_USER_FUNCTION) {
4479 if (op_array->static_variables) {
4480 HashTable *ht = ZEND_MAP_PTR_GET(op_array->static_variables_ptr);
4481 if (ht) {
4482 if (GC_DELREF(ht) == 0) {
4483 zend_array_destroy(ht);
4484 }
4485 ZEND_MAP_PTR_SET(op_array->static_variables_ptr, NULL);
4486 }
4487 }
4488 }
4489 } ZEND_HASH_FOREACH_END();
4490 }
4491 } ZEND_HASH_FOREACH_END();
4492
4493 if (Z_TYPE(EG(user_error_handler)) != IS_UNDEF) {
4494 zval_ptr_dtor(&EG(user_error_handler));
4495 ZVAL_UNDEF(&EG(user_error_handler));
4496 }
4497
4498 if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) {
4499 zval_ptr_dtor(&EG(user_exception_handler));
4500 ZVAL_UNDEF(&EG(user_exception_handler));
4501 }
4502
4503 zend_stack_clean(&EG(user_error_handlers_error_reporting), NULL, 1);
4504 zend_stack_clean(&EG(user_error_handlers), (void (*)(void *))ZVAL_PTR_DTOR, 1);
4505 zend_stack_clean(&EG(user_exception_handlers), (void (*)(void *))ZVAL_PTR_DTOR, 1);
4506
4507 CG(map_ptr_last) = orig_map_ptr_last;
4508
4509 if (EG(full_tables_cleanup)) {
4510 zend_accel_error(ACCEL_LOG_FATAL, "Preloading is not compatible with dl() function.");
4511 ret = FAILURE;
4512 goto finish;
4513 }
4514
4515 /* Inheritance errors may be thrown during linking */
4516 zend_try {
4517 preload_link();
4518 } zend_catch {
4519 CG(map_ptr_last) = orig_map_ptr_last;
4520 ret = FAILURE;
4521 goto finish;
4522 } zend_end_try();
4523
4524 preload_remove_empty_includes();
4525
4526 /* Don't preload constants */
4527 if (EG(zend_constants)) {
4528 zend_string *key;
4529 zval *zv;
4530
4531 /* Remember __COMPILER_HALT_OFFSET__(s) */
4532 ZEND_HASH_FOREACH_PTR(preload_scripts, script) {
4533 zend_execute_data *orig_execute_data = EG(current_execute_data);
4534 zend_execute_data fake_execute_data;
4535 zval *offset;
4536
4537 memset(&fake_execute_data, 0, sizeof(fake_execute_data));
4538 fake_execute_data.func = (zend_function*)&script->script.main_op_array;
4539 EG(current_execute_data) = &fake_execute_data;
4540 if ((offset = zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1)) != NULL) {
4541 script->compiler_halt_offset = Z_LVAL_P(offset);
4542 }
4543 EG(current_execute_data) = orig_execute_data;
4544 } ZEND_HASH_FOREACH_END();
4545
4546 ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(EG(zend_constants), key, zv) {
4547 zend_constant *c = Z_PTR_P(zv);
4548 if (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT) {
4549 break;
4550 }
4551 EG(zend_constants)->pDestructor(zv);
4552 zend_string_release(key);
4553 } ZEND_HASH_FOREACH_END_DEL();
4554 }
4555
4556 script = create_persistent_script();
4557 script->ping_auto_globals_mask = ping_auto_globals_mask;
4558
4559 /* Store all functions and classes in a single pseudo-file */
4560 filename = zend_string_init("$PRELOAD$", sizeof("$PRELOAD$") - 1, 0);
4561 #if ZEND_USE_ABS_CONST_ADDR
4562 init_op_array(&script->script.main_op_array, ZEND_USER_FUNCTION, 1);
4563 #else
4564 init_op_array(&script->script.main_op_array, ZEND_USER_FUNCTION, 2);
4565 #endif
4566 script->script.main_op_array.last = 1;
4567 script->script.main_op_array.last_literal = 1;
4568 #if ZEND_USE_ABS_CONST_ADDR
4569 script->script.main_op_array.literals = (zval*)emalloc(sizeof(zval));
4570 #else
4571 script->script.main_op_array.literals = (zval*)(script->script.main_op_array.opcodes + 1);
4572 #endif
4573 ZVAL_NULL(script->script.main_op_array.literals);
4574 memset(script->script.main_op_array.opcodes, 0, sizeof(zend_op));
4575 script->script.main_op_array.opcodes[0].opcode = ZEND_RETURN;
4576 script->script.main_op_array.opcodes[0].op1_type = IS_CONST;
4577 script->script.main_op_array.opcodes[0].op1.constant = 0;
4578 ZEND_PASS_TWO_UPDATE_CONSTANT(&script->script.main_op_array, script->script.main_op_array.opcodes, script->script.main_op_array.opcodes[0].op1);
4579
4580 script->script.main_op_array.filename = filename;
4581 script->script.filename = zend_string_copy(filename);
4582
4583 script->script.first_early_binding_opline = (uint32_t)-1;
4584
4585 preload_move_user_functions(CG(function_table), &script->script.function_table);
4586 preload_move_user_classes(CG(class_table), &script->script.class_table);
4587
4588 zend_hash_sort_ex(&script->script.class_table, preload_sort_classes, NULL, 0);
4589
4590 if (preload_optimize(script) != SUCCESS) {
4591 zend_accel_error(ACCEL_LOG_FATAL, "Optimization error during preloading!");
4592 return FAILURE;
4593 }
4594
4595 zend_shared_alloc_init_xlat_table();
4596
4597 HANDLE_BLOCK_INTERRUPTIONS();
4598 SHM_UNPROTECT();
4599
4600 /* Store method names first, because they may be shared between preloaded and non-preloaded classes */
4601 ZEND_HASH_FOREACH_PTR(&script->script.class_table, ce) {
4602 ZEND_HASH_FOREACH_PTR(&ce->function_table, op_array) {
4603 zend_string *new_name = zend_shared_alloc_get_xlat_entry(op_array->function_name);
4604
4605 if (!new_name) {
4606 new_name = accel_new_interned_string(op_array->function_name);
4607 zend_shared_alloc_register_xlat_entry(op_array->function_name, new_name);
4608 }
4609 op_array->function_name = new_name;
4610 } ZEND_HASH_FOREACH_END();
4611 } ZEND_HASH_FOREACH_END();
4612
4613 ZCSG(preload_script) = preload_script_in_shared_memory(script);
4614
4615 SHM_PROTECT();
4616 HANDLE_UNBLOCK_INTERRUPTIONS();
4617
4618 zend_string_release(filename);
4619
4620 ZEND_ASSERT(ZCSG(preload_script)->arena_size == 0);
4621
4622 preload_load();
4623
4624 /* Store individual scripts with unlinked classes */
4625 HANDLE_BLOCK_INTERRUPTIONS();
4626 SHM_UNPROTECT();
4627
4628 i = 0;
4629 ZCSG(saved_scripts) = zend_shared_alloc((zend_hash_num_elements(preload_scripts) + 1) * sizeof(void*));
4630 ZEND_HASH_FOREACH_PTR(preload_scripts, script) {
4631 if (zend_hash_num_elements(&script->script.class_table) > 1) {
4632 zend_hash_sort_ex(&script->script.class_table, preload_sort_classes, NULL, 0);
4633 }
4634 ZCSG(saved_scripts)[i++] = preload_script_in_shared_memory(script);
4635 } ZEND_HASH_FOREACH_END();
4636 ZCSG(saved_scripts)[i] = NULL;
4637
4638 zend_shared_alloc_save_state();
4639 accel_interned_strings_save_state();
4640
4641 SHM_PROTECT();
4642 HANDLE_UNBLOCK_INTERRUPTIONS();
4643
4644 zend_shared_alloc_destroy_xlat_table();
4645
4646 zend_preload_autoload = preload_autoload;
4647 } else {
4648 CG(map_ptr_last) = orig_map_ptr_last;
4649 }
4650
4651 finish:
4652 zend_hash_destroy(preload_scripts);
4653 efree(preload_scripts);
4654 preload_scripts = NULL;
4655
4656 return ret;
4657 }
4658
preload_ub_write(const char * str,size_t str_length)4659 static size_t preload_ub_write(const char *str, size_t str_length)
4660 {
4661 return fwrite(str, 1, str_length, stdout);
4662 }
4663
preload_flush(void * server_context)4664 static void preload_flush(void *server_context)
4665 {
4666 fflush(stdout);
4667 }
4668
preload_header_handler(sapi_header_struct * h,sapi_header_op_enum op,sapi_headers_struct * s)4669 static int preload_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s)
4670 {
4671 return 0;
4672 }
4673
preload_send_headers(sapi_headers_struct * sapi_headers)4674 static int preload_send_headers(sapi_headers_struct *sapi_headers)
4675 {
4676 return SAPI_HEADER_SENT_SUCCESSFULLY;
4677 }
4678
preload_send_header(sapi_header_struct * sapi_header,void * server_context)4679 static void preload_send_header(sapi_header_struct *sapi_header, void *server_context)
4680 {
4681 }
4682
accel_finish_startup(void)4683 static int accel_finish_startup(void)
4684 {
4685 if (!ZCG(enabled) || !accel_startup_ok) {
4686 return SUCCESS;
4687 }
4688
4689 if (ZCG(accel_directives).preload && *ZCG(accel_directives).preload) {
4690 #ifdef ZEND_WIN32
4691 zend_accel_error(ACCEL_LOG_ERROR, "Preloading is not supported on Windows");
4692 return FAILURE;
4693 #else
4694 int in_child = 0;
4695 int ret = SUCCESS;
4696 int rc;
4697 int orig_error_reporting;
4698
4699 int (*orig_activate)(TSRMLS_D) = sapi_module.activate;
4700 int (*orig_deactivate)(TSRMLS_D) = sapi_module.deactivate;
4701 void (*orig_register_server_variables)(zval *track_vars_array TSRMLS_DC) = sapi_module.register_server_variables;
4702 int (*orig_header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC) = sapi_module.header_handler;
4703 int (*orig_send_headers)(sapi_headers_struct *sapi_headers TSRMLS_DC) = sapi_module.send_headers;
4704 void (*orig_send_header)(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC)= sapi_module.send_header;
4705 char *(*orig_getenv)(char *name, size_t name_len TSRMLS_DC) = sapi_module.getenv;
4706 size_t (*orig_ub_write)(const char *str, size_t str_length) = sapi_module.ub_write;
4707 void (*orig_flush)(void *server_context) = sapi_module.flush;
4708 uint32_t orig_compiler_options = CG(compiler_options);
4709 #ifdef ZEND_SIGNALS
4710 zend_bool old_reset_signals = SIGG(reset);
4711 #endif
4712
4713 if (UNEXPECTED(file_cache_only)) {
4714 zend_accel_error(ACCEL_LOG_WARNING, "Preloading doesn't work in \"file_cache_only\" mode");
4715 return SUCCESS;
4716 }
4717
4718 /* exclusive lock */
4719 zend_shared_alloc_lock();
4720
4721 if (ZCSG(preload_script)) {
4722 /* Preloading was done in another process */
4723 preload_load();
4724 zend_shared_alloc_unlock();
4725 return SUCCESS;
4726 }
4727
4728 if (geteuid() == 0) {
4729 pid_t pid;
4730 struct passwd *pw;
4731
4732 if (!ZCG(accel_directives).preload_user
4733 || !*ZCG(accel_directives).preload_user) {
4734 zend_shared_alloc_unlock();
4735 zend_accel_error(ACCEL_LOG_FATAL, "\"opcache.preload_user\" has not been defined");
4736 return FAILURE;
4737 }
4738
4739 pw = getpwnam(ZCG(accel_directives).preload_user);
4740 if (pw == NULL) {
4741 zend_shared_alloc_unlock();
4742 zend_accel_error(ACCEL_LOG_FATAL, "Preloading failed to getpwnam(\"%s\")", ZCG(accel_directives).preload_user);
4743 return FAILURE;
4744 }
4745
4746 pid = fork();
4747 if (pid == -1) {
4748 zend_shared_alloc_unlock();
4749 zend_accel_error(ACCEL_LOG_FATAL, "Preloading failed to fork()");
4750 return FAILURE;
4751 } else if (pid == 0) { /* children */
4752 if (setgid(pw->pw_gid) < 0) {
4753 zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setgid(%d)", pw->pw_gid);
4754 exit(1);
4755 }
4756 if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
4757 zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to initgroups(\"%s\", %d)", pw->pw_name, pw->pw_uid);
4758 exit(1);
4759 }
4760 if (setuid(pw->pw_uid) < 0) {
4761 zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setuid(%d)", pw->pw_uid);
4762 exit(1);
4763 }
4764 in_child = 1;
4765 } else { /* parent */
4766 int status;
4767
4768 if (waitpid(pid, &status, 0) < 0) {
4769 zend_shared_alloc_unlock();
4770 zend_accel_error(ACCEL_LOG_FATAL, "Preloading failed to waitpid(%d)", pid);
4771 return FAILURE;
4772 }
4773
4774 if (ZCSG(preload_script)) {
4775 preload_load();
4776 }
4777
4778 zend_shared_alloc_unlock();
4779 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
4780 return SUCCESS;
4781 } else {
4782 return FAILURE;
4783 }
4784 }
4785 } else {
4786 if (ZCG(accel_directives).preload_user
4787 && *ZCG(accel_directives).preload_user) {
4788 zend_accel_error(ACCEL_LOG_WARNING, "\"opcache.preload_user\" is ignored");
4789 }
4790 }
4791
4792 sapi_module.activate = NULL;
4793 sapi_module.deactivate = NULL;
4794 sapi_module.register_server_variables = NULL;
4795 sapi_module.header_handler = preload_header_handler;
4796 sapi_module.send_headers = preload_send_headers;
4797 sapi_module.send_header = preload_send_header;
4798 sapi_module.getenv = NULL;
4799 sapi_module.ub_write = preload_ub_write;
4800 sapi_module.flush = preload_flush;
4801
4802 if (in_child) {
4803 CG(compiler_options) |= ZEND_COMPILE_PRELOAD_IN_CHILD;
4804 }
4805 CG(compiler_options) |= ZEND_COMPILE_PRELOAD;
4806 CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
4807 CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES;
4808 CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING;
4809 CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION;
4810 CG(compiler_options) |= ZEND_COMPILE_IGNORE_OTHER_FILES;
4811
4812 zend_interned_strings_switch_storage(1);
4813
4814 #ifdef ZEND_SIGNALS
4815 SIGG(reset) = 0;
4816 #endif
4817
4818 orig_error_reporting = EG(error_reporting);
4819 EG(error_reporting) = 0;
4820
4821 rc = php_request_startup();
4822
4823 EG(error_reporting) = orig_error_reporting;
4824
4825 if (rc == SUCCESS) {
4826 zend_bool orig_report_memleaks;
4827
4828 /* don't send headers */
4829 SG(headers_sent) = 1;
4830 SG(request_info).no_headers = 1;
4831 php_output_set_status(0);
4832
4833 ZCG(auto_globals_mask) = 0;
4834 ZCG(request_time) = (time_t)sapi_get_request_time();
4835 ZCG(cache_opline) = NULL;
4836 ZCG(cache_persistent_script) = NULL;
4837 ZCG(include_path_key_len) = 0;
4838 ZCG(include_path_check) = 1;
4839
4840 ZCG(cwd) = NULL;
4841 ZCG(cwd_key_len) = 0;
4842 ZCG(cwd_check) = 1;
4843
4844 if (accel_preload(ZCG(accel_directives).preload) != SUCCESS) {
4845 ret = FAILURE;
4846 }
4847
4848 orig_report_memleaks = PG(report_memleaks);
4849 PG(report_memleaks) = 0;
4850 #ifdef ZEND_SIGNALS
4851 /* We may not have registered signal handlers due to SIGG(reset)=0, so
4852 * also disable the check that they are registered. */
4853 SIGG(check) = 0;
4854 #endif
4855 php_request_shutdown(NULL); /* calls zend_shared_alloc_unlock(); */
4856 PG(report_memleaks) = orig_report_memleaks;
4857 } else {
4858 zend_shared_alloc_unlock();
4859 ret = FAILURE;
4860 }
4861 #ifdef ZEND_SIGNALS
4862 SIGG(reset) = old_reset_signals;
4863 #endif
4864
4865 CG(compiler_options) = orig_compiler_options;
4866
4867 sapi_module.activate = orig_activate;
4868 sapi_module.deactivate = orig_deactivate;
4869 sapi_module.register_server_variables = orig_register_server_variables;
4870 sapi_module.header_handler = orig_header_handler;
4871 sapi_module.send_headers = orig_send_headers;
4872 sapi_module.send_header = orig_send_header;
4873 sapi_module.getenv = orig_getenv;
4874 sapi_module.ub_write = orig_ub_write;
4875 sapi_module.flush = orig_flush;
4876
4877 sapi_activate();
4878
4879 if (in_child) {
4880 if (ret == SUCCESS) {
4881 exit(0);
4882 } else {
4883 exit(2);
4884 }
4885 }
4886
4887 return ret;
4888 #endif
4889 }
4890
4891 return SUCCESS;
4892 }
4893
4894 ZEND_EXT_API zend_extension zend_extension_entry = {
4895 ACCELERATOR_PRODUCT_NAME, /* name */
4896 PHP_VERSION, /* version */
4897 "Zend Technologies", /* author */
4898 "http://www.zend.com/", /* URL */
4899 "Copyright (c)", /* copyright */
4900 accel_startup, /* startup */
4901 NULL, /* shutdown */
4902 NULL, /* per-script activation */
4903 NULL, /* per-script deactivation */
4904 NULL, /* message handler */
4905 NULL, /* op_array handler */
4906 NULL, /* extended statement handler */
4907 NULL, /* extended fcall begin handler */
4908 NULL, /* extended fcall end handler */
4909 NULL, /* op_array ctor */
4910 NULL, /* op_array dtor */
4911 STANDARD_ZEND_EXTENSION_PROPERTIES
4912 };
4913