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