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