xref: /php-src/ext/opcache/ZendAccelerator.c (revision 1bae61a4)
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