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