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