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 }
3286 }
3287 #endif
3288 zend_shared_alloc_save_state();
3289 zend_shared_alloc_unlock();
3290
3291 SHM_PROTECT();
3292 } else if (!ZCG(accel_directives).file_cache) {
3293 accel_startup_ok = false;
3294 zend_accel_error_noreturn(ACCEL_LOG_FATAL, "opcache.file_cache_only is set without a proper setting of opcache.file_cache");
3295 return SUCCESS;
3296 } else {
3297 #ifdef HAVE_JIT
3298 JIT_G(enabled) = false;
3299 JIT_G(on) = false;
3300 #endif
3301 accel_shared_globals = calloc(1, sizeof(zend_accel_shared_globals));
3302 }
3303 #if ENABLE_FILE_CACHE_FALLBACK
3304 file_cache_fallback:
3305 #endif
3306
3307 /* Override compiler */
3308 accelerator_orig_compile_file = zend_compile_file;
3309 zend_compile_file = persistent_compile_file;
3310
3311 /* Override stream opener function (to eliminate open() call caused by
3312 * include/require statements ) */
3313 accelerator_orig_zend_stream_open_function = zend_stream_open_function;
3314 zend_stream_open_function = persistent_stream_open_function;
3315
3316 /* Override path resolver function (to eliminate stat() calls caused by
3317 * include_once/require_once statements */
3318 accelerator_orig_zend_resolve_path = zend_resolve_path;
3319 zend_resolve_path = persistent_zend_resolve_path;
3320
3321 /* Override chdir() function */
3322 if ((func = zend_hash_str_find_ptr(CG(function_table), "chdir", sizeof("chdir")-1)) != NULL &&
3323 func->type == ZEND_INTERNAL_FUNCTION) {
3324 orig_chdir = func->internal_function.handler;
3325 func->internal_function.handler = ZEND_FN(accel_chdir);
3326 }
3327 ZCG(cwd) = NULL;
3328 ZCG(include_path) = NULL;
3329
3330 /* Override "include_path" modifier callback */
3331 if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) {
3332 ZCG(include_path) = ini_entry->value;
3333 orig_include_path_on_modify = ini_entry->on_modify;
3334 ini_entry->on_modify = accel_include_path_on_modify;
3335 }
3336
3337 accel_startup_ok = true;
3338
3339 /* Override file_exists(), is_file() and is_readable() */
3340 zend_accel_override_file_functions();
3341
3342 /* Load black list */
3343 accel_blacklist.entries = NULL;
3344 if (ZCG(enabled) && accel_startup_ok &&
3345 ZCG(accel_directives).user_blacklist_filename &&
3346 *ZCG(accel_directives.user_blacklist_filename)) {
3347 zend_accel_blacklist_init(&accel_blacklist);
3348 zend_accel_blacklist_load(&accel_blacklist, ZCG(accel_directives.user_blacklist_filename));
3349 }
3350
3351 if (!file_cache_only && ZCG(accel_directives).interned_strings_buffer) {
3352 accel_use_shm_interned_strings();
3353 }
3354
3355 if (accel_finish_startup() != SUCCESS) {
3356 return FAILURE;
3357 }
3358
3359 if (ZCG(enabled) && accel_startup_ok) {
3360 /* Override inheritance cache callbaks */
3361 accelerator_orig_inheritance_cache_get = zend_inheritance_cache_get;
3362 accelerator_orig_inheritance_cache_add = zend_inheritance_cache_add;
3363 zend_inheritance_cache_get = zend_accel_inheritance_cache_get;
3364 zend_inheritance_cache_add = zend_accel_inheritance_cache_add;
3365 }
3366
3367 return SUCCESS;
3368 }
3369
3370 static void (*orig_post_shutdown_cb)(void);
3371
3372 static void accel_post_shutdown(void)
3373 {
3374 zend_shared_alloc_shutdown();
3375 }
3376
3377 void accel_shutdown(void)
3378 {
3379 zend_ini_entry *ini_entry;
3380 bool _file_cache_only = false;
3381
3382 #ifdef HAVE_JIT
3383 zend_jit_shutdown();
3384 #endif
3385
3386 zend_accel_blacklist_shutdown(&accel_blacklist);
3387
3388 if (!ZCG(enabled) || !accel_startup_ok) {
3389 #ifdef ZTS
3390 ts_free_id(accel_globals_id);
3391 #endif
3392 return;
3393 }
3394
3395 if (ZCSG(preload_script)) {
3396 preload_shutdown();
3397 }
3398
3399 _file_cache_only = file_cache_only;
3400
3401 accel_reset_pcre_cache();
3402
3403 #ifdef ZTS
3404 ts_free_id(accel_globals_id);
3405 #endif
3406
3407 if (!_file_cache_only) {
3408 /* Delay SHM detach */
3409 orig_post_shutdown_cb = zend_post_shutdown_cb;
3410 zend_post_shutdown_cb = accel_post_shutdown;
3411 }
3412
3413 zend_compile_file = accelerator_orig_compile_file;
3414 zend_inheritance_cache_get = accelerator_orig_inheritance_cache_get;
3415 zend_inheritance_cache_add = accelerator_orig_inheritance_cache_add;
3416
3417 if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) {
3418 ini_entry->on_modify = orig_include_path_on_modify;
3419 }
3420 }
3421
3422 void zend_accel_schedule_restart(zend_accel_restart_reason reason)
3423 {
3424 const char *zend_accel_restart_reason_text[ACCEL_RESTART_USER + 1] = {
3425 "out of memory",
3426 "hash overflow",
3427 "user",
3428 };
3429
3430 if (ZCSG(restart_pending)) {
3431 /* don't schedule twice */
3432 return;
3433 }
3434
3435 if (UNEXPECTED(zend_accel_schedule_restart_hook)) {
3436 zend_accel_schedule_restart_hook(reason);
3437 }
3438
3439 zend_accel_error(ACCEL_LOG_DEBUG, "Restart Scheduled! Reason: %s",
3440 zend_accel_restart_reason_text[reason]);
3441
3442 HANDLE_BLOCK_INTERRUPTIONS();
3443 SHM_UNPROTECT();
3444 ZCSG(restart_pending) = true;
3445 ZCSG(restart_reason) = reason;
3446 ZCSG(cache_status_before_restart) = ZCSG(accelerator_enabled);
3447 ZCSG(accelerator_enabled) = false;
3448
3449 if (ZCG(accel_directives).force_restart_timeout) {
3450 ZCSG(force_restart_time) = zend_accel_get_time() + ZCG(accel_directives).force_restart_timeout;
3451 } else {
3452 ZCSG(force_restart_time) = 0;
3453 }
3454 SHM_PROTECT();
3455 HANDLE_UNBLOCK_INTERRUPTIONS();
3456 }
3457
3458 static void accel_deactivate_now(void)
3459 {
3460 /* this is needed because on WIN32 lock is not decreased unless ZCG(counted) is set */
3461 #ifdef ZEND_WIN32
3462 ZCG(counted) = true;
3463 #endif
3464 accel_deactivate_sub();
3465 }
3466
3467 /* ensures it is OK to read SHM
3468 if it's not OK (restart in progress) returns FAILURE
3469 if OK returns SUCCESS
3470 MUST call accelerator_shm_read_unlock after done lock operations
3471 */
3472 zend_result accelerator_shm_read_lock(void)
3473 {
3474 if (ZCG(counted)) {
3475 /* counted means we are holding read lock for SHM, so that nothing bad can happen */
3476 return SUCCESS;
3477 } else {
3478 /* here accelerator is active but we do not hold SHM lock. This means restart was scheduled
3479 or is in progress now */
3480 if (accel_activate_add() == FAILURE) { /* acquire usage lock */
3481 return FAILURE;
3482 }
3483 /* Now if we weren't inside restart, restart would not begin until we remove usage lock */
3484 if (ZCSG(restart_in_progress)) {
3485 /* we already were inside restart this means it's not safe to touch shm */
3486 accel_deactivate_now(); /* drop usage lock */
3487 return FAILURE;
3488 }
3489 ZCG(counted) = true;
3490 }
3491 return SUCCESS;
3492 }
3493
3494 /* must be called ONLY after SUCCESSFUL accelerator_shm_read_lock */
3495 void accelerator_shm_read_unlock(void)
3496 {
3497 if (!ZCG(counted)) {
3498 /* counted is false - meaning we had to readlock manually, release readlock now */
3499 accel_deactivate_now();
3500 }
3501 }
3502
3503 /* Preloading */
3504 static HashTable *preload_scripts = NULL;
3505 static zend_op_array *(*preload_orig_compile_file)(zend_file_handle *file_handle, int type);
3506
3507 static void preload_shutdown(void)
3508 {
3509 zval *zv;
3510
3511 #if 0
3512 if (EG(zend_constants)) {
3513 ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(zend_constants), zv) {
3514 zend_constant *c = Z_PTR_P(zv);
3515 if (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT) {
3516 break;
3517 }
3518 } ZEND_HASH_MAP_FOREACH_END_DEL();
3519 }
3520 #endif
3521
3522 if (EG(function_table)) {
3523 ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(function_table), zv) {
3524 zend_function *func = Z_PTR_P(zv);
3525 if (func->type == ZEND_INTERNAL_FUNCTION) {
3526 break;
3527 }
3528 } ZEND_HASH_MAP_FOREACH_END_DEL();
3529 }
3530
3531 if (EG(class_table)) {
3532 ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(class_table), zv) {
3533 zend_class_entry *ce = Z_PTR_P(zv);
3534 if (ce->type == ZEND_INTERNAL_CLASS) {
3535 break;
3536 }
3537 } ZEND_HASH_MAP_FOREACH_END_DEL();
3538 }
3539 }
3540
3541 static void preload_activate(void)
3542 {
3543 if (ZCSG(preload_script)->ping_auto_globals_mask & ~ZCG(auto_globals_mask)) {
3544 zend_accel_set_auto_globals(ZCSG(preload_script)->ping_auto_globals_mask & ~ZCG(auto_globals_mask));
3545 }
3546 }
3547
3548 static void preload_restart(void)
3549 {
3550 zend_accel_hash_update(&ZCSG(hash), ZCSG(preload_script)->script.filename, 0, ZCSG(preload_script));
3551 if (ZCSG(saved_scripts)) {
3552 zend_persistent_script **p = ZCSG(saved_scripts);
3553 while (*p) {
3554 zend_accel_hash_update(&ZCSG(hash), (*p)->script.filename, 0, *p);
3555 p++;
3556 }
3557 }
3558 }
3559
3560 static size_t preload_try_strip_filename(zend_string *filename) {
3561 /*FIXME: better way to handle eval()'d code? see COMPILED_STRING_DESCRIPTION_FORMAT */
3562 if (ZSTR_LEN(filename) > sizeof(" eval()'d code")
3563 && *(ZSTR_VAL(filename) + ZSTR_LEN(filename) - sizeof(" eval()'d code")) == ':') {
3564 const char *cfilename = ZSTR_VAL(filename);
3565 size_t cfilenamelen = ZSTR_LEN(filename) - sizeof(" eval()'d code") - 1 /*:*/;
3566 while (cfilenamelen && cfilename[--cfilenamelen] != '(');
3567 return cfilenamelen;
3568 }
3569 return 0;
3570 }
3571
3572 static void preload_move_user_functions(HashTable *src, HashTable *dst)
3573 {
3574 Bucket *p;
3575 dtor_func_t orig_dtor = src->pDestructor;
3576 zend_string *filename = NULL;
3577 bool copy = false;
3578
3579 src->pDestructor = NULL;
3580 zend_hash_extend(dst, dst->nNumUsed + src->nNumUsed, 0);
3581 ZEND_HASH_MAP_REVERSE_FOREACH_BUCKET(src, p) {
3582 zend_function *function = Z_PTR(p->val);
3583
3584 if (EXPECTED(function->type == ZEND_USER_FUNCTION)) {
3585 if (function->op_array.filename != filename) {
3586 filename = function->op_array.filename;
3587 if (filename) {
3588 if (!(copy = zend_hash_exists(preload_scripts, filename))) {
3589 size_t eval_len = preload_try_strip_filename(filename);
3590 if (eval_len) {
3591 copy = zend_hash_str_exists(preload_scripts, ZSTR_VAL(filename), eval_len);
3592 }
3593 }
3594 } else {
3595 copy = false;
3596 }
3597 }
3598 if (copy) {
3599 _zend_hash_append_ptr(dst, p->key, function);
3600 } else {
3601 orig_dtor(&p->val);
3602 }
3603 zend_hash_del_bucket(src, p);
3604 } else {
3605 break;
3606 }
3607 } ZEND_HASH_FOREACH_END();
3608 src->pDestructor = orig_dtor;
3609 }
3610
3611 static void preload_move_user_classes(HashTable *src, HashTable *dst)
3612 {
3613 Bucket *p;
3614 dtor_func_t orig_dtor = src->pDestructor;
3615 zend_string *filename = NULL;
3616 bool copy = false;
3617
3618 src->pDestructor = NULL;
3619 zend_hash_extend(dst, dst->nNumUsed + src->nNumUsed, 0);
3620 ZEND_HASH_MAP_FOREACH_BUCKET_FROM(src, p, EG(persistent_classes_count)) {
3621 zend_class_entry *ce = Z_PTR(p->val);
3622 ZEND_ASSERT(ce->type == ZEND_USER_CLASS);
3623 if (ce->info.user.filename != filename) {
3624 filename = ce->info.user.filename;
3625 if (filename) {
3626 if (!(copy = zend_hash_exists(preload_scripts, filename))) {
3627 size_t eval_len = preload_try_strip_filename(filename);
3628 if (eval_len) {
3629 copy = zend_hash_str_exists(preload_scripts, ZSTR_VAL(filename), eval_len);
3630 }
3631 }
3632 } else {
3633 copy = false;
3634 }
3635 }
3636 if (copy) {
3637 _zend_hash_append(dst, p->key, &p->val);
3638 } else {
3639 orig_dtor(&p->val);
3640 }
3641 zend_hash_del_bucket(src, p);
3642 } ZEND_HASH_FOREACH_END();
3643 src->pDestructor = orig_dtor;
3644 }
3645
3646 static zend_op_array *preload_compile_file(zend_file_handle *file_handle, int type)
3647 {
3648 zend_op_array *op_array = preload_orig_compile_file(file_handle, type);
3649
3650 if (op_array && op_array->refcount) {
3651 zend_persistent_script *script;
3652
3653 script = create_persistent_script();
3654 script->script.filename = zend_string_copy(op_array->filename);
3655 zend_string_hash_val(script->script.filename);
3656 script->script.main_op_array = *op_array;
3657
3658 //??? efree(op_array->refcount);
3659 op_array->refcount = NULL;
3660
3661 zend_hash_add_ptr(preload_scripts, script->script.filename, script);
3662 }
3663
3664 return op_array;
3665 }
3666
3667 static void preload_sort_classes(void *base, size_t count, size_t siz, compare_func_t compare, swap_func_t swp)
3668 {
3669 Bucket *b1 = base;
3670 Bucket *b2;
3671 Bucket *end = b1 + count;
3672 Bucket tmp;
3673 zend_class_entry *ce, *p;
3674
3675 while (b1 < end) {
3676 try_again:
3677 ce = (zend_class_entry*)Z_PTR(b1->val);
3678 if (ce->parent && (ce->ce_flags & ZEND_ACC_LINKED)) {
3679 p = ce->parent;
3680 if (p->type == ZEND_USER_CLASS) {
3681 b2 = b1 + 1;
3682 while (b2 < end) {
3683 if (p == Z_PTR(b2->val)) {
3684 tmp = *b1;
3685 *b1 = *b2;
3686 *b2 = tmp;
3687 goto try_again;
3688 }
3689 b2++;
3690 }
3691 }
3692 }
3693 if (ce->num_interfaces && (ce->ce_flags & ZEND_ACC_LINKED)) {
3694 uint32_t i = 0;
3695 for (i = 0; i < ce->num_interfaces; i++) {
3696 p = ce->interfaces[i];
3697 if (p->type == ZEND_USER_CLASS) {
3698 b2 = b1 + 1;
3699 while (b2 < end) {
3700 if (p == Z_PTR(b2->val)) {
3701 tmp = *b1;
3702 *b1 = *b2;
3703 *b2 = tmp;
3704 goto try_again;
3705 }
3706 b2++;
3707 }
3708 }
3709 }
3710 }
3711 b1++;
3712 }
3713 }
3714
3715 typedef struct {
3716 const char *kind;
3717 const char *name;
3718 } preload_error;
3719
3720 static zend_result preload_resolve_deps(preload_error *error, const zend_class_entry *ce)
3721 {
3722 memset(error, 0, sizeof(preload_error));
3723
3724 if (ce->parent_name) {
3725 zend_string *key = zend_string_tolower(ce->parent_name);
3726 zend_class_entry *parent = zend_hash_find_ptr(EG(class_table), key);
3727 zend_string_release(key);
3728 if (!parent) {
3729 error->kind = "Unknown parent ";
3730 error->name = ZSTR_VAL(ce->parent_name);
3731 return FAILURE;
3732 }
3733 }
3734
3735 if (ce->num_interfaces) {
3736 for (uint32_t i = 0; i < ce->num_interfaces; i++) {
3737 zend_class_entry *interface =
3738 zend_hash_find_ptr(EG(class_table), ce->interface_names[i].lc_name);
3739 if (!interface) {
3740 error->kind = "Unknown interface ";
3741 error->name = ZSTR_VAL(ce->interface_names[i].name);
3742 return FAILURE;
3743 }
3744 }
3745 }
3746
3747 if (ce->num_traits) {
3748 for (uint32_t i = 0; i < ce->num_traits; i++) {
3749 zend_class_entry *trait =
3750 zend_hash_find_ptr(EG(class_table), ce->trait_names[i].lc_name);
3751 if (!trait) {
3752 error->kind = "Unknown trait ";
3753 error->name = ZSTR_VAL(ce->trait_names[i].name);
3754 return FAILURE;
3755 }
3756 }
3757 }
3758
3759 return SUCCESS;
3760 }
3761
3762 static bool preload_try_resolve_constants(zend_class_entry *ce)
3763 {
3764 bool ok, changed, was_changed = false;
3765 zend_class_constant *c;
3766 zval *val;
3767 zend_string *key;
3768
3769 EG(exception) = (void*)(uintptr_t)-1; /* prevent error reporting */
3770 do {
3771 ok = true;
3772 changed = false;
3773 ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) {
3774 val = &c->value;
3775 if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
3776 if (EXPECTED(zend_update_class_constant(c, key, c->ce) == SUCCESS)) {
3777 was_changed = changed = true;
3778 } else {
3779 ok = false;
3780 }
3781 }
3782 } ZEND_HASH_FOREACH_END();
3783 if (ok) {
3784 ce->ce_flags &= ~ZEND_ACC_HAS_AST_CONSTANTS;
3785 }
3786 if (ce->default_properties_count) {
3787 uint32_t i;
3788 bool resolved = true;
3789
3790 for (i = 0; i < ce->default_properties_count; i++) {
3791 val = &ce->default_properties_table[i];
3792 if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
3793 zend_property_info *prop = ce->properties_info_table[i];
3794 if (UNEXPECTED(zval_update_constant_ex(val, prop->ce) != SUCCESS)) {
3795 resolved = ok = false;
3796 }
3797 }
3798 }
3799 if (resolved) {
3800 ce->ce_flags &= ~ZEND_ACC_HAS_AST_PROPERTIES;
3801 }
3802 }
3803 if (ce->default_static_members_count) {
3804 uint32_t count = ce->parent ? ce->default_static_members_count - ce->parent->default_static_members_count : ce->default_static_members_count;
3805 bool resolved = true;
3806
3807 val = ce->default_static_members_table + ce->default_static_members_count - 1;
3808 while (count) {
3809 if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
3810 if (UNEXPECTED(zval_update_constant_ex(val, ce) != SUCCESS)) {
3811 resolved = ok = false;
3812 }
3813 }
3814 val--;
3815 count--;
3816 }
3817 if (resolved) {
3818 ce->ce_flags &= ~ZEND_ACC_HAS_AST_STATICS;
3819 }
3820 }
3821 } while (changed && !ok);
3822 EG(exception) = NULL;
3823 CG(in_compilation) = false;
3824
3825 if (ok) {
3826 ce->ce_flags |= ZEND_ACC_CONSTANTS_UPDATED;
3827 }
3828
3829 return ok || was_changed;
3830 }
3831
3832 static void (*orig_error_cb)(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message);
3833
3834 static void preload_error_cb(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message)
3835 {
3836 /* Suppress printing of the error, only bail out for fatal errors. */
3837 if (type & E_FATAL_ERRORS) {
3838 zend_bailout();
3839 }
3840 }
3841
3842 /* Remove DECLARE opcodes and dynamic defs. */
3843 static void preload_remove_declares(zend_op_array *op_array)
3844 {
3845 zend_op *opline = op_array->opcodes;
3846 zend_op *end = opline + op_array->last;
3847 uint32_t skip_dynamic_func_count = 0;
3848 zend_string *key;
3849 zend_op_array *func;
3850
3851 while (opline != end) {
3852 switch (opline->opcode) {
3853 case ZEND_DECLARE_CLASS:
3854 case ZEND_DECLARE_CLASS_DELAYED:
3855 key = Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1);
3856 if (!zend_hash_exists(CG(class_table), key)) {
3857 MAKE_NOP(opline);
3858 }
3859 break;
3860 case ZEND_DECLARE_FUNCTION:
3861 opline->op2.num -= skip_dynamic_func_count;
3862 key = Z_STR_P(RT_CONSTANT(opline, opline->op1));
3863 func = zend_hash_find_ptr(EG(function_table), key);
3864 if (func && func == op_array->dynamic_func_defs[opline->op2.num]) {
3865 zend_op_array **dynamic_func_defs;
3866
3867 op_array->num_dynamic_func_defs--;
3868 if (op_array->num_dynamic_func_defs == 0) {
3869 dynamic_func_defs = NULL;
3870 } else {
3871 dynamic_func_defs = emalloc(sizeof(zend_op_array*) * op_array->num_dynamic_func_defs);
3872 if (opline->op2.num > 0) {
3873 memcpy(
3874 dynamic_func_defs,
3875 op_array->dynamic_func_defs,
3876 sizeof(zend_op_array*) * opline->op2.num);
3877 }
3878 if (op_array->num_dynamic_func_defs - opline->op2.num > 0) {
3879 memcpy(
3880 dynamic_func_defs + opline->op2.num,
3881 op_array->dynamic_func_defs + (opline->op2.num + 1),
3882 sizeof(zend_op_array*) * (op_array->num_dynamic_func_defs - opline->op2.num));
3883 }
3884 }
3885 efree(op_array->dynamic_func_defs);
3886 op_array->dynamic_func_defs = dynamic_func_defs;
3887 skip_dynamic_func_count++;
3888 MAKE_NOP(opline);
3889 }
3890 break;
3891 case ZEND_DECLARE_LAMBDA_FUNCTION:
3892 opline->op2.num -= skip_dynamic_func_count;
3893 break;
3894 }
3895 opline++;
3896 }
3897 }
3898
3899 static void preload_link(void)
3900 {
3901 zval *zv;
3902 zend_persistent_script *script;
3903 zend_class_entry *ce;
3904 zend_string *key;
3905 bool changed;
3906
3907 HashTable errors;
3908 zend_hash_init(&errors, 0, NULL, NULL, 0);
3909
3910 /* Resolve class dependencies */
3911 do {
3912 changed = false;
3913
3914 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL_FROM(EG(class_table), key, zv, EG(persistent_classes_count)) {
3915 ce = Z_PTR_P(zv);
3916 ZEND_ASSERT(ce->type != ZEND_INTERNAL_CLASS);
3917
3918 if (!(ce->ce_flags & (ZEND_ACC_TOP_LEVEL|ZEND_ACC_ANON_CLASS))
3919 || (ce->ce_flags & ZEND_ACC_LINKED)) {
3920 continue;
3921 }
3922
3923 zend_string *lcname = zend_string_tolower(ce->name);
3924 if (!(ce->ce_flags & ZEND_ACC_ANON_CLASS)) {
3925 if (zend_hash_exists(EG(class_table), lcname)) {
3926 zend_string_release(lcname);
3927 continue;
3928 }
3929 }
3930
3931 preload_error error_info;
3932 if (preload_resolve_deps(&error_info, ce) == FAILURE) {
3933 zend_string_release(lcname);
3934 continue;
3935 }
3936
3937 zv = zend_hash_set_bucket_key(EG(class_table), (Bucket*)zv, lcname);
3938 ZEND_ASSERT(zv && "We already checked above that the class doesn't exist yet");
3939
3940 /* Set the FILE_CACHED flag to force a lazy load, and the CACHED flag to
3941 * prevent freeing of interface names. */
3942 void *checkpoint = zend_arena_checkpoint(CG(arena));
3943 zend_class_entry *orig_ce = ce;
3944 uint32_t temporary_flags = ZEND_ACC_FILE_CACHED|ZEND_ACC_CACHED;
3945 ce->ce_flags |= temporary_flags;
3946 if (ce->parent_name) {
3947 zend_string_addref(ce->parent_name);
3948 }
3949
3950 /* Record and suppress errors during inheritance. */
3951 orig_error_cb = zend_error_cb;
3952 zend_error_cb = preload_error_cb;
3953 zend_begin_record_errors();
3954
3955 /* Set filename & lineno information for inheritance errors */
3956 CG(in_compilation) = true;
3957 CG(compiled_filename) = ce->info.user.filename;
3958 CG(zend_lineno) = ce->info.user.line_start;
3959 zend_try {
3960 ce = zend_do_link_class(ce, NULL, lcname);
3961 if (!ce) {
3962 ZEND_ASSERT(0 && "Class linking failed?");
3963 }
3964 ce->ce_flags &= ~temporary_flags;
3965 changed = true;
3966
3967 /* Inheritance successful, print out any warnings. */
3968 zend_error_cb = orig_error_cb;
3969 zend_emit_recorded_errors();
3970 } zend_catch {
3971 /* Clear variance obligations that were left behind on bailout. */
3972 if (CG(delayed_variance_obligations)) {
3973 zend_hash_index_del(
3974 CG(delayed_variance_obligations), (uintptr_t) Z_CE_P(zv));
3975 }
3976
3977 /* Restore the original class. */
3978 zv = zend_hash_set_bucket_key(EG(class_table), (Bucket*)zv, key);
3979 Z_CE_P(zv) = orig_ce;
3980 orig_ce->ce_flags &= ~temporary_flags;
3981 zend_arena_release(&CG(arena), checkpoint);
3982
3983 /* Remember the last error. */
3984 zend_error_cb = orig_error_cb;
3985 EG(record_errors) = false;
3986 ZEND_ASSERT(EG(num_errors) > 0);
3987 zend_hash_update_ptr(&errors, key, EG(errors)[EG(num_errors)-1]);
3988 EG(num_errors)--;
3989 } zend_end_try();
3990 CG(in_compilation) = false;
3991 CG(compiled_filename) = NULL;
3992 zend_free_recorded_errors();
3993 zend_string_release(lcname);
3994 } ZEND_HASH_FOREACH_END();
3995 } while (changed);
3996
3997 do {
3998 changed = false;
3999
4000 ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(class_table), zv) {
4001 ce = Z_PTR_P(zv);
4002 if (ce->type == ZEND_INTERNAL_CLASS) {
4003 break;
4004 }
4005 if ((ce->ce_flags & ZEND_ACC_LINKED) && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
4006 if (!(ce->ce_flags & ZEND_ACC_TRAIT)) { /* don't update traits */
4007 CG(in_compilation) = true; /* prevent autoloading */
4008 if (preload_try_resolve_constants(ce)) {
4009 changed = true;
4010 }
4011 CG(in_compilation) = false;
4012 }
4013 }
4014 } ZEND_HASH_FOREACH_END();
4015 } while (changed);
4016
4017 /* Warn for classes that could not be linked. */
4018 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL_FROM(
4019 EG(class_table), key, zv, EG(persistent_classes_count)) {
4020 ce = Z_PTR_P(zv);
4021 ZEND_ASSERT(ce->type != ZEND_INTERNAL_CLASS);
4022 if ((ce->ce_flags & (ZEND_ACC_TOP_LEVEL|ZEND_ACC_ANON_CLASS))
4023 && !(ce->ce_flags & ZEND_ACC_LINKED)) {
4024 zend_string *lcname = zend_string_tolower(ce->name);
4025 preload_error error;
4026 if (!(ce->ce_flags & ZEND_ACC_ANON_CLASS)
4027 && zend_hash_exists(EG(class_table), lcname)) {
4028 zend_error_at(
4029 E_WARNING, ce->info.user.filename, ce->info.user.line_start,
4030 "Can't preload already declared class %s", ZSTR_VAL(ce->name));
4031 } else if (preload_resolve_deps(&error, ce) == FAILURE) {
4032 zend_error_at(
4033 E_WARNING, ce->info.user.filename, ce->info.user.line_start,
4034 "Can't preload unlinked class %s: %s%s",
4035 ZSTR_VAL(ce->name), error.kind, error.name);
4036 } else {
4037 zend_error_info *error = zend_hash_find_ptr(&errors, key);
4038 zend_error_at(
4039 E_WARNING, error->filename, error->lineno,
4040 "Can't preload unlinked class %s: %s",
4041 ZSTR_VAL(ce->name), ZSTR_VAL(error->message));
4042 }
4043 zend_string_release(lcname);
4044 }
4045 } ZEND_HASH_FOREACH_END();
4046
4047 zend_hash_destroy(&errors);
4048
4049 ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4050 zend_op_array *op_array = &script->script.main_op_array;
4051 preload_remove_declares(op_array);
4052
4053 if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) {
4054 zend_accel_free_delayed_early_binding_list(script);
4055 zend_accel_build_delayed_early_binding_list(script);
4056 if (!script->num_early_bindings) {
4057 op_array->fn_flags &= ~ZEND_ACC_EARLY_BINDING;
4058 }
4059 }
4060 } ZEND_HASH_FOREACH_END();
4061
4062 /* Dynamic defs inside functions and methods need to be removed as well. */
4063 zend_op_array *op_array;
4064 ZEND_HASH_MAP_FOREACH_PTR_FROM(EG(function_table), op_array, EG(persistent_functions_count)) {
4065 ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION);
4066 preload_remove_declares(op_array);
4067 } ZEND_HASH_FOREACH_END();
4068 ZEND_HASH_MAP_FOREACH_PTR_FROM(EG(class_table), ce, EG(persistent_classes_count)) {
4069 ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
4070 if (op_array->type == ZEND_USER_FUNCTION) {
4071 preload_remove_declares(op_array);
4072 }
4073 } ZEND_HASH_FOREACH_END();
4074 } ZEND_HASH_FOREACH_END();
4075 }
4076
4077 static zend_string *preload_resolve_path(zend_string *filename)
4078 {
4079 if (php_is_stream_path(ZSTR_VAL(filename))) {
4080 return NULL;
4081 }
4082 return zend_resolve_path(filename);
4083 }
4084
4085 static void preload_remove_empty_includes(void)
4086 {
4087 zend_persistent_script *script;
4088 bool changed;
4089
4090 /* mark all as empty */
4091 ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4092 script->empty = true;
4093 } ZEND_HASH_FOREACH_END();
4094
4095 /* find non empty scripts */
4096 do {
4097 changed = false;
4098 ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4099 if (script->empty) {
4100 bool empty = true;
4101 zend_op *opline = script->script.main_op_array.opcodes;
4102 zend_op *end = opline + script->script.main_op_array.last;
4103
4104 while (opline < end) {
4105 if (opline->opcode == ZEND_INCLUDE_OR_EVAL &&
4106 opline->extended_value != ZEND_EVAL &&
4107 opline->op1_type == IS_CONST &&
4108 Z_TYPE_P(RT_CONSTANT(opline, opline->op1)) == IS_STRING &&
4109 opline->result_type == IS_UNUSED) {
4110
4111 zend_string *resolved_path = preload_resolve_path(Z_STR_P(RT_CONSTANT(opline, opline->op1)));
4112
4113 if (resolved_path) {
4114 zend_persistent_script *incl = zend_hash_find_ptr(preload_scripts, resolved_path);
4115 zend_string_release(resolved_path);
4116 if (!incl || !incl->empty) {
4117 empty = false;
4118 break;
4119 }
4120 } else {
4121 empty = false;
4122 break;
4123 }
4124 } else if (opline->opcode != ZEND_NOP &&
4125 opline->opcode != ZEND_RETURN &&
4126 opline->opcode != ZEND_HANDLE_EXCEPTION) {
4127 empty = false;
4128 break;
4129 }
4130 opline++;
4131 }
4132 if (!empty) {
4133 script->empty = false;
4134 changed = true;
4135 }
4136 }
4137 } ZEND_HASH_FOREACH_END();
4138 } while (changed);
4139
4140 /* remove empty includes */
4141 ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4142 zend_op *opline = script->script.main_op_array.opcodes;
4143 zend_op *end = opline + script->script.main_op_array.last;
4144
4145 while (opline < end) {
4146 if (opline->opcode == ZEND_INCLUDE_OR_EVAL &&
4147 opline->extended_value != ZEND_EVAL &&
4148 opline->op1_type == IS_CONST &&
4149 Z_TYPE_P(RT_CONSTANT(opline, opline->op1)) == IS_STRING) {
4150
4151 zend_string *resolved_path = preload_resolve_path(Z_STR_P(RT_CONSTANT(opline, opline->op1)));
4152
4153 if (resolved_path) {
4154 zend_persistent_script *incl = zend_hash_find_ptr(preload_scripts, resolved_path);
4155 if (incl && incl->empty && opline->result_type == IS_UNUSED) {
4156 MAKE_NOP(opline);
4157 } else {
4158 if (!IS_ABSOLUTE_PATH(Z_STRVAL_P(RT_CONSTANT(opline, opline->op1)), Z_STRLEN_P(RT_CONSTANT(opline, opline->op1)))) {
4159 /* replace relative patch with absolute one */
4160 zend_string_release(Z_STR_P(RT_CONSTANT(opline, opline->op1)));
4161 ZVAL_STR_COPY(RT_CONSTANT(opline, opline->op1), resolved_path);
4162 }
4163 }
4164 zend_string_release(resolved_path);
4165 }
4166 }
4167 opline++;
4168 }
4169 } ZEND_HASH_FOREACH_END();
4170 }
4171
4172 static void preload_register_trait_methods(zend_class_entry *ce) {
4173 zend_op_array *op_array;
4174 ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
4175 if (!(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
4176 ZEND_ASSERT(op_array->refcount && "Must have refcount pointer");
4177 zend_shared_alloc_register_xlat_entry(op_array->refcount, op_array);
4178 }
4179 } ZEND_HASH_FOREACH_END();
4180 }
4181
4182 static void preload_fix_trait_methods(zend_class_entry *ce)
4183 {
4184 zend_op_array *op_array;
4185
4186 ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
4187 if (op_array->fn_flags & ZEND_ACC_TRAIT_CLONE) {
4188 zend_op_array *orig_op_array = zend_shared_alloc_get_xlat_entry(op_array->refcount);
4189 ZEND_ASSERT(orig_op_array && "Must be in xlat table");
4190
4191 zend_string *function_name = op_array->function_name;
4192 zend_class_entry *scope = op_array->scope;
4193 uint32_t fn_flags = op_array->fn_flags;
4194 zend_function *prototype = op_array->prototype;
4195 HashTable *ht = op_array->static_variables;
4196 *op_array = *orig_op_array;
4197 op_array->function_name = function_name;
4198 op_array->scope = scope;
4199 op_array->fn_flags = fn_flags;
4200 op_array->prototype = prototype;
4201 op_array->static_variables = ht;
4202 }
4203 } ZEND_HASH_FOREACH_END();
4204 }
4205
4206 static void preload_optimize(zend_persistent_script *script)
4207 {
4208 zend_class_entry *ce;
4209 zend_persistent_script *tmp_script;
4210
4211 zend_shared_alloc_init_xlat_table();
4212
4213 ZEND_HASH_MAP_FOREACH_PTR(&script->script.class_table, ce) {
4214 if (ce->ce_flags & ZEND_ACC_TRAIT) {
4215 preload_register_trait_methods(ce);
4216 }
4217 } ZEND_HASH_FOREACH_END();
4218
4219 ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, tmp_script) {
4220 ZEND_HASH_MAP_FOREACH_PTR(&tmp_script->script.class_table, ce) {
4221 if (ce->ce_flags & ZEND_ACC_TRAIT) {
4222 preload_register_trait_methods(ce);
4223 }
4224 } ZEND_HASH_FOREACH_END();
4225 } ZEND_HASH_FOREACH_END();
4226
4227 zend_optimize_script(&script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level);
4228 zend_accel_finalize_delayed_early_binding_list(script);
4229
4230 ZEND_HASH_MAP_FOREACH_PTR(&script->script.class_table, ce) {
4231 preload_fix_trait_methods(ce);
4232 } ZEND_HASH_FOREACH_END();
4233
4234 ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4235 ZEND_HASH_MAP_FOREACH_PTR(&script->script.class_table, ce) {
4236 preload_fix_trait_methods(ce);
4237 } ZEND_HASH_FOREACH_END();
4238 } ZEND_HASH_FOREACH_END();
4239
4240 zend_shared_alloc_destroy_xlat_table();
4241
4242 ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4243 zend_optimize_script(&script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level);
4244 zend_accel_finalize_delayed_early_binding_list(script);
4245 } ZEND_HASH_FOREACH_END();
4246 }
4247
4248 static zend_persistent_script* preload_script_in_shared_memory(zend_persistent_script *new_persistent_script)
4249 {
4250 zend_accel_hash_entry *bucket;
4251 uint32_t memory_used;
4252 uint32_t checkpoint;
4253
4254 if (zend_accel_hash_is_full(&ZCSG(hash))) {
4255 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.");
4256 return NULL;
4257 }
4258
4259 checkpoint = zend_shared_alloc_checkpoint_xlat_table();
4260
4261 /* Calculate the required memory size */
4262 memory_used = zend_accel_script_persist_calc(new_persistent_script, 1);
4263
4264 /* Allocate shared memory */
4265 ZCG(mem) = zend_shared_alloc_aligned(memory_used);
4266 if (!ZCG(mem)) {
4267 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.");
4268 return NULL;
4269 }
4270
4271 bzero_aligned(ZCG(mem), memory_used);
4272
4273 zend_shared_alloc_restore_xlat_table(checkpoint);
4274
4275 /* Copy into shared memory */
4276 new_persistent_script = zend_accel_script_persist(new_persistent_script, 1);
4277
4278 new_persistent_script->is_phar = is_phar_file(new_persistent_script->script.filename);
4279
4280 /* Consistency check */
4281 if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
4282 zend_accel_error(
4283 ((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
4284 "Internal error: wrong size calculation: %s start=" ZEND_ADDR_FMT ", end=" ZEND_ADDR_FMT ", real=" ZEND_ADDR_FMT "\n",
4285 ZSTR_VAL(new_persistent_script->script.filename),
4286 (size_t)new_persistent_script->mem,
4287 (size_t)((char *)new_persistent_script->mem + new_persistent_script->size),
4288 (size_t)ZCG(mem));
4289 }
4290
4291 /* store script structure in the hash table */
4292 bucket = zend_accel_hash_update(&ZCSG(hash), new_persistent_script->script.filename, 0, new_persistent_script);
4293 if (bucket) {
4294 zend_accel_error(ACCEL_LOG_INFO, "Cached script '%s'", ZSTR_VAL(new_persistent_script->script.filename));
4295 }
4296
4297 new_persistent_script->dynamic_members.memory_consumption = ZEND_ALIGNED_SIZE(new_persistent_script->size);
4298
4299 return new_persistent_script;
4300 }
4301
4302 static void preload_load(void)
4303 {
4304 /* Load into process tables */
4305 zend_script *script = &ZCSG(preload_script)->script;
4306 if (zend_hash_num_elements(&script->function_table)) {
4307 Bucket *p = script->function_table.arData;
4308 Bucket *end = p + script->function_table.nNumUsed;
4309
4310 zend_hash_extend(CG(function_table),
4311 CG(function_table)->nNumUsed + script->function_table.nNumUsed, 0);
4312 for (; p != end; p++) {
4313 _zend_hash_append_ptr_ex(CG(function_table), p->key, Z_PTR(p->val), 1);
4314 }
4315 }
4316
4317 if (zend_hash_num_elements(&script->class_table)) {
4318 Bucket *p = script->class_table.arData;
4319 Bucket *end = p + script->class_table.nNumUsed;
4320
4321 zend_hash_extend(CG(class_table),
4322 CG(class_table)->nNumUsed + script->class_table.nNumUsed, 0);
4323 for (; p != end; p++) {
4324 _zend_hash_append_ex(CG(class_table), p->key, &p->val, 1);
4325 }
4326 }
4327
4328 if (EG(zend_constants)) {
4329 EG(persistent_constants_count) = EG(zend_constants)->nNumUsed;
4330 }
4331 if (EG(function_table)) {
4332 EG(persistent_functions_count) = EG(function_table)->nNumUsed;
4333 }
4334 if (EG(class_table)) {
4335 EG(persistent_classes_count) = EG(class_table)->nNumUsed;
4336 }
4337 if (CG(map_ptr_last) != ZCSG(map_ptr_last)) {
4338 size_t old_map_ptr_last = CG(map_ptr_last);
4339 CG(map_ptr_last) = ZCSG(map_ptr_last);
4340 CG(map_ptr_size) = ZEND_MM_ALIGNED_SIZE_EX(CG(map_ptr_last) + 1, 4096);
4341 CG(map_ptr_real_base) = perealloc(CG(map_ptr_real_base), CG(map_ptr_size) * sizeof(void*), 1);
4342 CG(map_ptr_base) = ZEND_MAP_PTR_BIASED_BASE(CG(map_ptr_real_base));
4343 memset((void **) CG(map_ptr_real_base) + old_map_ptr_last, 0,
4344 (CG(map_ptr_last) - old_map_ptr_last) * sizeof(void *));
4345 }
4346 }
4347
4348 static zend_result accel_preload(const char *config, bool in_child)
4349 {
4350 zend_file_handle file_handle;
4351 zend_result ret;
4352 char *orig_open_basedir;
4353 size_t orig_map_ptr_last;
4354 uint32_t orig_compiler_options;
4355
4356 ZCG(enabled) = false;
4357 ZCG(accelerator_enabled) = false;
4358 orig_open_basedir = PG(open_basedir);
4359 PG(open_basedir) = NULL;
4360 preload_orig_compile_file = accelerator_orig_compile_file;
4361 accelerator_orig_compile_file = preload_compile_file;
4362
4363 orig_map_ptr_last = CG(map_ptr_last);
4364
4365 /* Compile and execute preloading script */
4366 zend_stream_init_filename(&file_handle, (char *) config);
4367
4368 preload_scripts = emalloc(sizeof(HashTable));
4369 zend_hash_init(preload_scripts, 0, NULL, NULL, 0);
4370
4371 orig_compiler_options = CG(compiler_options);
4372 if (in_child) {
4373 CG(compiler_options) |= ZEND_COMPILE_PRELOAD_IN_CHILD;
4374 }
4375 CG(compiler_options) |= ZEND_COMPILE_PRELOAD;
4376 CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
4377 CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING;
4378 CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION;
4379 CG(compiler_options) |= ZEND_COMPILE_IGNORE_OTHER_FILES;
4380 CG(skip_shebang) = true;
4381
4382 zend_try {
4383 zend_op_array *op_array;
4384
4385 ret = SUCCESS;
4386 op_array = zend_compile_file(&file_handle, ZEND_REQUIRE);
4387 if (file_handle.opened_path) {
4388 zend_hash_add_empty_element(&EG(included_files), file_handle.opened_path);
4389 }
4390 zend_destroy_file_handle(&file_handle);
4391 if (op_array) {
4392 zend_execute(op_array, NULL);
4393 zend_exception_restore();
4394 if (UNEXPECTED(EG(exception))) {
4395 if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) {
4396 zend_user_exception_handler();
4397 }
4398 if (EG(exception)) {
4399 ret = zend_exception_error(EG(exception), E_ERROR);
4400 if (ret == FAILURE) {
4401 CG(unclean_shutdown) = true;
4402 }
4403 }
4404 }
4405 destroy_op_array(op_array);
4406 efree_size(op_array, sizeof(zend_op_array));
4407 } else {
4408 if (EG(exception)) {
4409 zend_exception_error(EG(exception), E_ERROR);
4410 }
4411
4412 CG(unclean_shutdown) = true;
4413 ret = FAILURE;
4414 }
4415 } zend_catch {
4416 ret = FAILURE;
4417 } zend_end_try();
4418
4419 PG(open_basedir) = orig_open_basedir;
4420 accelerator_orig_compile_file = preload_orig_compile_file;
4421 ZCG(enabled) = true;
4422
4423 zend_destroy_file_handle(&file_handle);
4424
4425 if (ret == SUCCESS) {
4426 zend_persistent_script *script;
4427 int ping_auto_globals_mask;
4428 int i;
4429
4430 if (PG(auto_globals_jit)) {
4431 ping_auto_globals_mask = zend_accel_get_auto_globals();
4432 } else {
4433 ping_auto_globals_mask = 0;
4434 }
4435
4436 if (EG(zend_constants)) {
4437 /* Remember __COMPILER_HALT_OFFSET__(s). Do this early,
4438 * as zend_shutdown_executor_values() destroys constants. */
4439 ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4440 zend_execute_data *orig_execute_data = EG(current_execute_data);
4441 zend_execute_data fake_execute_data;
4442 zval *offset;
4443
4444 memset(&fake_execute_data, 0, sizeof(fake_execute_data));
4445 fake_execute_data.func = (zend_function*)&script->script.main_op_array;
4446 EG(current_execute_data) = &fake_execute_data;
4447 if ((offset = zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1)) != NULL) {
4448 script->compiler_halt_offset = Z_LVAL_P(offset);
4449 }
4450 EG(current_execute_data) = orig_execute_data;
4451 } ZEND_HASH_FOREACH_END();
4452 }
4453
4454 /* Cleanup executor */
4455 EG(flags) |= EG_FLAGS_IN_SHUTDOWN;
4456
4457 php_call_shutdown_functions();
4458 zend_call_destructors();
4459 php_output_end_all();
4460 php_free_shutdown_functions();
4461
4462 /* Release stored values to avoid dangling pointers */
4463 zend_shutdown_executor_values(/* fast_shutdown */ false);
4464
4465 /* We don't want to preload constants.
4466 * Check that zend_shutdown_executor_values() also destroys constants. */
4467 ZEND_ASSERT(zend_hash_num_elements(EG(zend_constants)) == EG(persistent_constants_count));
4468
4469 zend_hash_init(&EG(symbol_table), 0, NULL, ZVAL_PTR_DTOR, 0);
4470
4471 CG(map_ptr_last) = orig_map_ptr_last;
4472
4473 if (EG(full_tables_cleanup)) {
4474 zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading is not compatible with dl() function.");
4475 ret = FAILURE;
4476 goto finish;
4477 }
4478
4479 /* Inheritance errors may be thrown during linking */
4480 zend_try {
4481 preload_link();
4482 } zend_catch {
4483 CG(map_ptr_last) = orig_map_ptr_last;
4484 ret = FAILURE;
4485 goto finish;
4486 } zend_end_try();
4487
4488 preload_remove_empty_includes();
4489
4490 script = create_persistent_script();
4491 script->ping_auto_globals_mask = ping_auto_globals_mask;
4492
4493 /* Store all functions and classes in a single pseudo-file */
4494 CG(compiled_filename) = ZSTR_INIT_LITERAL("$PRELOAD$", 0);
4495 #if ZEND_USE_ABS_CONST_ADDR
4496 init_op_array(&script->script.main_op_array, ZEND_USER_FUNCTION, 1);
4497 #else
4498 init_op_array(&script->script.main_op_array, ZEND_USER_FUNCTION, 2);
4499 #endif
4500 script->script.main_op_array.fn_flags |= ZEND_ACC_DONE_PASS_TWO;
4501 script->script.main_op_array.last = 1;
4502 script->script.main_op_array.last_literal = 1;
4503 script->script.main_op_array.T = ZEND_OBSERVER_ENABLED;
4504 #if ZEND_USE_ABS_CONST_ADDR
4505 script->script.main_op_array.literals = (zval*)emalloc(sizeof(zval));
4506 #else
4507 script->script.main_op_array.literals = (zval*)(script->script.main_op_array.opcodes + 1);
4508 #endif
4509 ZVAL_NULL(script->script.main_op_array.literals);
4510 memset(script->script.main_op_array.opcodes, 0, sizeof(zend_op));
4511 script->script.main_op_array.opcodes[0].opcode = ZEND_RETURN;
4512 script->script.main_op_array.opcodes[0].op1_type = IS_CONST;
4513 script->script.main_op_array.opcodes[0].op1.constant = 0;
4514 ZEND_PASS_TWO_UPDATE_CONSTANT(&script->script.main_op_array, script->script.main_op_array.opcodes, script->script.main_op_array.opcodes[0].op1);
4515 zend_vm_set_opcode_handler(script->script.main_op_array.opcodes);
4516
4517 script->script.filename = CG(compiled_filename);
4518 CG(compiled_filename) = NULL;
4519
4520 preload_move_user_functions(CG(function_table), &script->script.function_table);
4521 preload_move_user_classes(CG(class_table), &script->script.class_table);
4522
4523 zend_hash_sort_ex(&script->script.class_table, preload_sort_classes, NULL, 0);
4524
4525 preload_optimize(script);
4526
4527 zend_shared_alloc_init_xlat_table();
4528
4529 HANDLE_BLOCK_INTERRUPTIONS();
4530 SHM_UNPROTECT();
4531
4532 ZCSG(preload_script) = preload_script_in_shared_memory(script);
4533
4534 SHM_PROTECT();
4535 HANDLE_UNBLOCK_INTERRUPTIONS();
4536
4537 preload_load();
4538
4539 /* Store individual scripts with unlinked classes */
4540 HANDLE_BLOCK_INTERRUPTIONS();
4541 SHM_UNPROTECT();
4542
4543 i = 0;
4544 ZCSG(saved_scripts) = zend_shared_alloc((zend_hash_num_elements(preload_scripts) + 1) * sizeof(void*));
4545 ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4546 if (zend_hash_num_elements(&script->script.class_table) > 1) {
4547 zend_hash_sort_ex(&script->script.class_table, preload_sort_classes, NULL, 0);
4548 }
4549 ZCSG(saved_scripts)[i++] = preload_script_in_shared_memory(script);
4550 } ZEND_HASH_FOREACH_END();
4551 ZCSG(saved_scripts)[i] = NULL;
4552
4553 zend_shared_alloc_save_state();
4554 accel_interned_strings_save_state();
4555
4556 SHM_PROTECT();
4557 HANDLE_UNBLOCK_INTERRUPTIONS();
4558
4559 zend_shared_alloc_destroy_xlat_table();
4560 } else {
4561 CG(map_ptr_last) = orig_map_ptr_last;
4562 }
4563
4564 finish:
4565 CG(compiler_options) = orig_compiler_options;
4566 zend_hash_destroy(preload_scripts);
4567 efree(preload_scripts);
4568 preload_scripts = NULL;
4569
4570 return ret;
4571 }
4572
4573 static size_t preload_ub_write(const char *str, size_t str_length)
4574 {
4575 return fwrite(str, 1, str_length, stdout);
4576 }
4577
4578 static void preload_flush(void *server_context)
4579 {
4580 fflush(stdout);
4581 }
4582
4583 static int preload_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s)
4584 {
4585 return 0;
4586 }
4587
4588 static int preload_send_headers(sapi_headers_struct *sapi_headers)
4589 {
4590 return SAPI_HEADER_SENT_SUCCESSFULLY;
4591 }
4592
4593 static void preload_send_header(sapi_header_struct *sapi_header, void *server_context)
4594 {
4595 }
4596
4597 #ifndef ZEND_WIN32
4598 static zend_result accel_finish_startup_preload(bool in_child)
4599 {
4600 zend_result ret = SUCCESS;
4601 int orig_error_reporting;
4602
4603 int (*orig_activate)(void) = sapi_module.activate;
4604 int (*orig_deactivate)(void) = sapi_module.deactivate;
4605 void (*orig_register_server_variables)(zval *track_vars_array) = sapi_module.register_server_variables;
4606 int (*orig_header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers) = sapi_module.header_handler;
4607 int (*orig_send_headers)(sapi_headers_struct *sapi_headers) = sapi_module.send_headers;
4608 void (*orig_send_header)(sapi_header_struct *sapi_header, void *server_context)= sapi_module.send_header;
4609 char *(*orig_getenv)(const char *name, size_t name_len) = sapi_module.getenv;
4610 size_t (*orig_ub_write)(const char *str, size_t str_length) = sapi_module.ub_write;
4611 void (*orig_flush)(void *server_context) = sapi_module.flush;
4612 #ifdef ZEND_SIGNALS
4613 bool old_reset_signals = SIGG(reset);
4614 #endif
4615
4616 sapi_module.activate = NULL;
4617 sapi_module.deactivate = NULL;
4618 sapi_module.register_server_variables = NULL;
4619 sapi_module.header_handler = preload_header_handler;
4620 sapi_module.send_headers = preload_send_headers;
4621 sapi_module.send_header = preload_send_header;
4622 sapi_module.getenv = NULL;
4623 sapi_module.ub_write = preload_ub_write;
4624 sapi_module.flush = preload_flush;
4625
4626 zend_interned_strings_switch_storage(1);
4627
4628 #ifdef ZEND_SIGNALS
4629 SIGG(reset) = false;
4630 #endif
4631
4632 orig_error_reporting = EG(error_reporting);
4633 EG(error_reporting) = 0;
4634
4635 const zend_result rc = php_request_startup();
4636
4637 EG(error_reporting) = orig_error_reporting;
4638
4639 if (rc == SUCCESS) {
4640 bool orig_report_memleaks;
4641
4642 /* don't send headers */
4643 SG(headers_sent) = true;
4644 SG(request_info).no_headers = true;
4645 php_output_set_status(0);
4646
4647 ZCG(auto_globals_mask) = 0;
4648 ZCG(request_time) = (time_t)sapi_get_request_time();
4649 ZCG(cache_opline) = NULL;
4650 ZCG(cache_persistent_script) = NULL;
4651 ZCG(include_path_key_len) = 0;
4652 ZCG(include_path_check) = true;
4653
4654 ZCG(cwd) = NULL;
4655 ZCG(cwd_key_len) = 0;
4656 ZCG(cwd_check) = true;
4657
4658 if (accel_preload(ZCG(accel_directives).preload, in_child) != SUCCESS) {
4659 ret = FAILURE;
4660 }
4661 preload_flush(NULL);
4662
4663 orig_report_memleaks = PG(report_memleaks);
4664 PG(report_memleaks) = false;
4665 #ifdef ZEND_SIGNALS
4666 /* We may not have registered signal handlers due to SIGG(reset)=0, so
4667 * also disable the check that they are registered. */
4668 SIGG(check) = false;
4669 #endif
4670 php_request_shutdown(NULL); /* calls zend_shared_alloc_unlock(); */
4671 EG(class_table) = NULL;
4672 EG(function_table) = NULL;
4673 PG(report_memleaks) = orig_report_memleaks;
4674 } else {
4675 zend_shared_alloc_unlock();
4676 ret = FAILURE;
4677 }
4678 #ifdef ZEND_SIGNALS
4679 SIGG(reset) = old_reset_signals;
4680 #endif
4681
4682 sapi_module.activate = orig_activate;
4683 sapi_module.deactivate = orig_deactivate;
4684 sapi_module.register_server_variables = orig_register_server_variables;
4685 sapi_module.header_handler = orig_header_handler;
4686 sapi_module.send_headers = orig_send_headers;
4687 sapi_module.send_header = orig_send_header;
4688 sapi_module.getenv = orig_getenv;
4689 sapi_module.ub_write = orig_ub_write;
4690 sapi_module.flush = orig_flush;
4691
4692 sapi_activate();
4693
4694 return ret;
4695 }
4696
4697 static zend_result accel_finish_startup_preload_subprocess(pid_t *pid)
4698 {
4699 uid_t euid = geteuid();
4700 if (euid != 0) {
4701 if (ZCG(accel_directives).preload_user
4702 && *ZCG(accel_directives).preload_user) {
4703 zend_accel_error(ACCEL_LOG_WARNING, "\"opcache.preload_user\" is ignored because the current user is not \"root\"");
4704 }
4705
4706 *pid = -1;
4707 return SUCCESS;
4708 }
4709
4710 if (!ZCG(accel_directives).preload_user
4711 || !*ZCG(accel_directives).preload_user) {
4712
4713 bool sapi_requires_preload_user = !(strcmp(sapi_module.name, "cli") == 0
4714 || strcmp(sapi_module.name, "phpdbg") == 0);
4715
4716 if (!sapi_requires_preload_user) {
4717 *pid = -1;
4718 return SUCCESS;
4719 }
4720
4721 zend_shared_alloc_unlock();
4722 zend_accel_error_noreturn(ACCEL_LOG_FATAL, "\"opcache.preload\" requires \"opcache.preload_user\" when running under uid 0");
4723 return FAILURE;
4724 }
4725
4726 struct passwd *pw = getpwnam(ZCG(accel_directives).preload_user);
4727 if (pw == NULL) {
4728 zend_shared_alloc_unlock();
4729 zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to getpwnam(\"%s\")", ZCG(accel_directives).preload_user);
4730 return FAILURE;
4731 }
4732
4733 if (pw->pw_uid == euid) {
4734 *pid = -1;
4735 return SUCCESS;
4736 }
4737
4738 *pid = fork();
4739 if (*pid == -1) {
4740 zend_shared_alloc_unlock();
4741 zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to fork()");
4742 return FAILURE;
4743 }
4744
4745 if (*pid == 0) { /* children */
4746 if (setgid(pw->pw_gid) < 0) {
4747 zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setgid(%d)", pw->pw_gid);
4748 exit(1);
4749 }
4750 if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
4751 zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to initgroups(\"%s\", %d)", pw->pw_name, pw->pw_uid);
4752 exit(1);
4753 }
4754 if (setuid(pw->pw_uid) < 0) {
4755 zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setuid(%d)", pw->pw_uid);
4756 exit(1);
4757 }
4758 }
4759
4760 return SUCCESS;
4761 }
4762 #endif /* ZEND_WIN32 */
4763
4764 static zend_result accel_finish_startup(void)
4765 {
4766 if (!ZCG(enabled) || !accel_startup_ok) {
4767 return SUCCESS;
4768 }
4769
4770 if (!(ZCG(accel_directives).preload && *ZCG(accel_directives).preload)) {
4771 return SUCCESS;
4772 }
4773
4774 #ifdef ZEND_WIN32
4775 zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Preloading is not supported on Windows");
4776 return FAILURE;
4777 #else /* ZEND_WIN32 */
4778
4779 if (UNEXPECTED(file_cache_only)) {
4780 zend_accel_error(ACCEL_LOG_WARNING, "Preloading doesn't work in \"file_cache_only\" mode");
4781 return SUCCESS;
4782 }
4783
4784 /* exclusive lock */
4785 zend_shared_alloc_lock();
4786
4787 if (ZCSG(preload_script)) {
4788 /* Preloading was done in another process */
4789 preload_load();
4790 zend_shared_alloc_unlock();
4791 return SUCCESS;
4792 }
4793
4794
4795 pid_t pid;
4796 if (accel_finish_startup_preload_subprocess(&pid) == FAILURE) {
4797 zend_shared_alloc_unlock();
4798 return FAILURE;
4799 }
4800
4801 if (pid == -1) { /* no subprocess was needed */
4802 /* The called function unlocks the shared alloc lock */
4803 return accel_finish_startup_preload(false);
4804 } else if (pid == 0) { /* subprocess */
4805 const zend_result ret = accel_finish_startup_preload(true);
4806
4807 exit(ret == SUCCESS ? 0 : 1);
4808 } else { /* parent */
4809 int status;
4810
4811 if (waitpid(pid, &status, 0) < 0) {
4812 zend_shared_alloc_unlock();
4813 zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to waitpid(%d)", pid);
4814 }
4815
4816 if (ZCSG(preload_script)) {
4817 preload_load();
4818 }
4819
4820 zend_shared_alloc_unlock();
4821
4822 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
4823 return SUCCESS;
4824 } else {
4825 return FAILURE;
4826 }
4827 }
4828 #endif /* ZEND_WIN32 */
4829 }
4830
4831 ZEND_EXT_API zend_extension zend_extension_entry = {
4832 ACCELERATOR_PRODUCT_NAME, /* name */
4833 PHP_VERSION, /* version */
4834 "Zend Technologies", /* author */
4835 "http://www.zend.com/", /* URL */
4836 "Copyright (c)", /* copyright */
4837 accel_startup, /* startup */
4838 NULL, /* shutdown */
4839 NULL, /* per-script activation */
4840 #ifdef HAVE_JIT
4841 accel_deactivate, /* per-script deactivation */
4842 #else
4843 NULL, /* per-script deactivation */
4844 #endif
4845 NULL, /* message handler */
4846 NULL, /* op_array handler */
4847 NULL, /* extended statement handler */
4848 NULL, /* extended fcall begin handler */
4849 NULL, /* extended fcall end handler */
4850 NULL, /* op_array ctor */
4851 NULL, /* op_array dtor */
4852 STANDARD_ZEND_EXTENSION_PROPERTIES
4853 };
4854