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