xref: /PHP-7.3/ext/opcache/ZendAccelerator.c (revision 4e198c00)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend OPcache                                                         |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1998-2018 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 "main/SAPI.h"
35 #include "main/php_streams.h"
36 #include "main/php_open_temporary_file.h"
37 #include "zend_API.h"
38 #include "zend_ini.h"
39 #include "zend_virtual_cwd.h"
40 #include "zend_accelerator_util_funcs.h"
41 #include "zend_accelerator_hash.h"
42 #include "ext/pcre/php_pcre.h"
43 #include "ext/standard/md5.h"
44 
45 #ifdef HAVE_OPCACHE_FILE_CACHE
46 # include "zend_file_cache.h"
47 #endif
48 
49 #ifndef ZEND_WIN32
50 #include  <netdb.h>
51 #endif
52 
53 #ifdef ZEND_WIN32
54 typedef int uid_t;
55 typedef int gid_t;
56 #include <io.h>
57 #endif
58 
59 #ifndef ZEND_WIN32
60 # include <sys/time.h>
61 #else
62 # include <process.h>
63 #endif
64 
65 #ifdef HAVE_UNISTD_H
66 # include <unistd.h>
67 #endif
68 #include <fcntl.h>
69 #include <signal.h>
70 #include <time.h>
71 
72 #ifndef ZEND_WIN32
73 # include <sys/types.h>
74 # include <sys/ipc.h>
75 #endif
76 
77 #include <sys/stat.h>
78 #include <errno.h>
79 
80 #ifdef __AVX__
81 #include <immintrin.h>
82 #endif
83 
84 #define SHM_PROTECT() \
85 	do { \
86 		if (ZCG(accel_directives).protect_memory) { \
87 			zend_accel_shared_protect(1); \
88 		} \
89 	} while (0)
90 #define SHM_UNPROTECT() \
91 	do { \
92 		if (ZCG(accel_directives).protect_memory) { \
93 			zend_accel_shared_protect(0); \
94 		} \
95 	} while (0)
96 
97 ZEND_EXTENSION();
98 
99 #ifndef ZTS
100 zend_accel_globals accel_globals;
101 #else
102 int accel_globals_id;
103 #if defined(COMPILE_DL_OPCACHE)
104 ZEND_TSRMLS_CACHE_DEFINE()
105 #endif
106 #endif
107 
108 /* Points to the structure shared across all PHP processes */
109 zend_accel_shared_globals *accel_shared_globals = NULL;
110 
111 /* true globals, no need for thread safety */
112 zend_bool accel_startup_ok = 0;
113 static char *zps_failure_reason = NULL;
114 char *zps_api_failure_reason = NULL;
115 #ifdef HAVE_OPCACHE_FILE_CACHE
116 zend_bool file_cache_only = 0;  /* process uses file cache only */
117 #endif
118 #if ENABLE_FILE_CACHE_FALLBACK
119 zend_bool fallback_process = 0; /* process uses file cache fallback */
120 #endif
121 
122 static zend_op_array *(*accelerator_orig_compile_file)(zend_file_handle *file_handle, int type);
123 static int (*accelerator_orig_zend_stream_open_function)(const char *filename, zend_file_handle *handle );
124 static zend_string *(*accelerator_orig_zend_resolve_path)(const char *filename, size_t filename_len);
125 static zif_handler orig_chdir = NULL;
126 static ZEND_INI_MH((*orig_include_path_on_modify)) = NULL;
127 static int (*orig_post_startup_cb)(void);
128 
129 static void accel_gen_system_id(void);
130 static int accel_post_startup(void);
131 
132 #ifdef ZEND_WIN32
133 # define INCREMENT(v) InterlockedIncrement64(&ZCSG(v))
134 # define DECREMENT(v) InterlockedDecrement64(&ZCSG(v))
135 # define LOCKVAL(v)   (ZCSG(v))
136 #endif
137 
138 #ifdef ZEND_WIN32
zend_accel_get_time(void)139 static time_t zend_accel_get_time(void)
140 {
141 	FILETIME now;
142 	GetSystemTimeAsFileTime(&now);
143 
144 	return (time_t) ((((((__int64)now.dwHighDateTime) << 32)|now.dwLowDateTime) - 116444736000000000L)/10000000);
145 }
146 #else
147 # define zend_accel_get_time() time(NULL)
148 #endif
149 
is_stream_path(const char * filename)150 static inline int is_stream_path(const char *filename)
151 {
152 	const char *p;
153 
154 	for (p = filename;
155 	     (*p >= 'a' && *p <= 'z') ||
156 	     (*p >= 'A' && *p <= 'Z') ||
157 	     (*p >= '0' && *p <= '9') ||
158 	     *p == '+' || *p == '-' || *p == '.';
159 	     p++);
160 	return ((p != filename) && (p[0] == ':') && (p[1] == '/') && (p[2] == '/'));
161 }
162 
is_cacheable_stream_path(const char * filename)163 static inline int is_cacheable_stream_path(const char *filename)
164 {
165 	return memcmp(filename, "file://", sizeof("file://") - 1) == 0 ||
166 	       memcmp(filename, "phar://", sizeof("phar://") - 1) == 0;
167 }
168 
169 /* O+ overrides PHP chdir() function and remembers the current working directory
170  * in ZCG(cwd) and ZCG(cwd_len). Later accel_getcwd() can use stored value and
171  * avoid getcwd() call.
172  */
ZEND_FUNCTION(accel_chdir)173 static ZEND_FUNCTION(accel_chdir)
174 {
175 	char cwd[MAXPATHLEN];
176 
177 	orig_chdir(INTERNAL_FUNCTION_PARAM_PASSTHRU);
178 	if (VCWD_GETCWD(cwd, MAXPATHLEN)) {
179 		if (ZCG(cwd)) {
180 			zend_string_release_ex(ZCG(cwd), 0);
181 		}
182 		ZCG(cwd) = zend_string_init(cwd, strlen(cwd), 0);
183 	} else {
184 		if (ZCG(cwd)) {
185 			zend_string_release_ex(ZCG(cwd), 0);
186 			ZCG(cwd) = NULL;
187 		}
188 	}
189 	ZCG(cwd_key_len) = 0;
190 	ZCG(cwd_check) = 1;
191 }
192 
accel_getcwd(void)193 static inline zend_string* accel_getcwd(void)
194 {
195 	if (ZCG(cwd)) {
196 		return ZCG(cwd);
197 	} else {
198 		char cwd[MAXPATHLEN + 1];
199 
200 		if (!VCWD_GETCWD(cwd, MAXPATHLEN)) {
201 			return NULL;
202 		}
203 		ZCG(cwd) = zend_string_init(cwd, strlen(cwd), 0);
204 		ZCG(cwd_key_len) = 0;
205 		ZCG(cwd_check) = 1;
206 		return ZCG(cwd);
207 	}
208 }
209 
zend_accel_schedule_restart_if_necessary(zend_accel_restart_reason reason)210 void zend_accel_schedule_restart_if_necessary(zend_accel_restart_reason reason)
211 {
212 	if ((((double) ZSMMG(wasted_shared_memory)) / ZCG(accel_directives).memory_consumption) >= ZCG(accel_directives).max_wasted_percentage) {
213  		zend_accel_schedule_restart(reason);
214 	}
215 }
216 
217 /* O+ tracks changes of "include_path" directive. It stores all the requested
218  * values in ZCG(include_paths) shared hash table, current value in
219  * ZCG(include_path)/ZCG(include_path_len) and one letter "path key" in
220  * ZCG(include_path_key).
221  */
ZEND_INI_MH(accel_include_path_on_modify)222 static ZEND_INI_MH(accel_include_path_on_modify)
223 {
224 	int ret = orig_include_path_on_modify(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
225 
226 	if (ret == SUCCESS) {
227 		ZCG(include_path) = new_value;
228 		ZCG(include_path_key_len) = 0;
229 		ZCG(include_path_check) = 1;
230 	}
231 	return ret;
232 }
233 
accel_restart_enter(void)234 static inline void accel_restart_enter(void)
235 {
236 #ifdef ZEND_WIN32
237 	INCREMENT(restart_in);
238 #else
239 	struct flock restart_in_progress;
240 
241 	restart_in_progress.l_type = F_WRLCK;
242 	restart_in_progress.l_whence = SEEK_SET;
243 	restart_in_progress.l_start = 2;
244 	restart_in_progress.l_len = 1;
245 
246 	if (fcntl(lock_file, F_SETLK, &restart_in_progress) == -1) {
247 		zend_accel_error(ACCEL_LOG_DEBUG, "RestartC(+1):  %s (%d)", strerror(errno), errno);
248 	}
249 #endif
250 	ZCSG(restart_in_progress) = 1;
251 }
252 
accel_restart_leave(void)253 static inline void accel_restart_leave(void)
254 {
255 #ifdef ZEND_WIN32
256 	ZCSG(restart_in_progress) = 0;
257 	DECREMENT(restart_in);
258 #else
259 	struct flock restart_finished;
260 
261 	restart_finished.l_type = F_UNLCK;
262 	restart_finished.l_whence = SEEK_SET;
263 	restart_finished.l_start = 2;
264 	restart_finished.l_len = 1;
265 
266 	ZCSG(restart_in_progress) = 0;
267 	if (fcntl(lock_file, F_SETLK, &restart_finished) == -1) {
268 		zend_accel_error(ACCEL_LOG_DEBUG, "RestartC(-1):  %s (%d)", strerror(errno), errno);
269 	}
270 #endif
271 }
272 
accel_restart_is_active(void)273 static inline int accel_restart_is_active(void)
274 {
275 	if (ZCSG(restart_in_progress)) {
276 #ifndef ZEND_WIN32
277 		struct flock restart_check;
278 
279 		restart_check.l_type = F_WRLCK;
280 		restart_check.l_whence = SEEK_SET;
281 		restart_check.l_start = 2;
282 		restart_check.l_len = 1;
283 
284 		if (fcntl(lock_file, F_GETLK, &restart_check) == -1) {
285 			zend_accel_error(ACCEL_LOG_DEBUG, "RestartC:  %s (%d)", strerror(errno), errno);
286 			return FAILURE;
287 		}
288 		if (restart_check.l_type == F_UNLCK) {
289 			ZCSG(restart_in_progress) = 0;
290 			return 0;
291 		} else {
292 			return 1;
293 		}
294 #else
295 		return LOCKVAL(restart_in) != 0;
296 #endif
297 	}
298 	return 0;
299 }
300 
301 /* Creates a read lock for SHM access */
accel_activate_add(void)302 static inline int accel_activate_add(void)
303 {
304 #ifdef ZEND_WIN32
305 	INCREMENT(mem_usage);
306 #else
307 	struct flock mem_usage_lock;
308 
309 	mem_usage_lock.l_type = F_RDLCK;
310 	mem_usage_lock.l_whence = SEEK_SET;
311 	mem_usage_lock.l_start = 1;
312 	mem_usage_lock.l_len = 1;
313 
314 	if (fcntl(lock_file, F_SETLK, &mem_usage_lock) == -1) {
315 		zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC(+1):  %s (%d)", strerror(errno), errno);
316 		return FAILURE;
317 	}
318 #endif
319 	return SUCCESS;
320 }
321 
322 /* Releases a lock for SHM access */
accel_deactivate_sub(void)323 static inline void accel_deactivate_sub(void)
324 {
325 #ifdef ZEND_WIN32
326 	if (ZCG(counted)) {
327 		DECREMENT(mem_usage);
328 		ZCG(counted) = 0;
329 	}
330 #else
331 	struct flock mem_usage_unlock;
332 
333 	mem_usage_unlock.l_type = F_UNLCK;
334 	mem_usage_unlock.l_whence = SEEK_SET;
335 	mem_usage_unlock.l_start = 1;
336 	mem_usage_unlock.l_len = 1;
337 
338 	if (fcntl(lock_file, F_SETLK, &mem_usage_unlock) == -1) {
339 		zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC(-1):  %s (%d)", strerror(errno), errno);
340 	}
341 #endif
342 }
343 
accel_unlock_all(void)344 static inline void accel_unlock_all(void)
345 {
346 #ifdef ZEND_WIN32
347 	accel_deactivate_sub();
348 #else
349 	struct flock mem_usage_unlock_all;
350 
351 	mem_usage_unlock_all.l_type = F_UNLCK;
352 	mem_usage_unlock_all.l_whence = SEEK_SET;
353 	mem_usage_unlock_all.l_start = 0;
354 	mem_usage_unlock_all.l_len = 0;
355 
356 	if (fcntl(lock_file, F_SETLK, &mem_usage_unlock_all) == -1) {
357 		zend_accel_error(ACCEL_LOG_DEBUG, "UnlockAll:  %s (%d)", strerror(errno), errno);
358 	}
359 #endif
360 }
361 
362 /* Interned strings support */
363 
364 /* O+ disables creation of interned strings by regular PHP compiler, instead,
365  * it creates interned strings in shared memory when saves a script.
366  * Such interned strings are shared across all PHP processes
367  */
368 
369 #define STRTAB_INVALID_POS 0
370 
371 #define STRTAB_HASH_TO_SLOT(tab, h) \
372 	((uint32_t*)((char*)(tab) + sizeof(*(tab)) + ((h) & (tab)->nTableMask)))
373 #define STRTAB_STR_TO_POS(tab, s) \
374 	((uint32_t)((char*)s - (char*)(tab)))
375 #define STRTAB_POS_TO_STR(tab, pos) \
376 	((zend_string*)((char*)(tab) + (pos)))
377 #define STRTAB_COLLISION(s) \
378 	(*((uint32_t*)((char*)s - sizeof(uint32_t))))
379 #define STRTAB_STR_SIZE(s) \
380 	ZEND_MM_ALIGNED_SIZE_EX(_ZSTR_HEADER_SIZE + ZSTR_LEN(s) + 5, 8)
381 #define STRTAB_NEXT(s) \
382 	((zend_string*)((char*)(s) + STRTAB_STR_SIZE(s)))
383 
accel_interned_strings_restore_state(void)384 static void accel_interned_strings_restore_state(void)
385 {
386 	zend_string *s, *top;
387 	uint32_t *hash_slot, n;
388 
389 	/* clear removed content */
390 	memset(ZCSG(interned_strings).saved_top,
391 			0, (char*)ZCSG(interned_strings).top - (char*)ZCSG(interned_strings).saved_top);
392 
393 	/* Reset "top" */
394 	ZCSG(interned_strings).top = ZCSG(interned_strings).saved_top;
395 
396 	/* rehash */
397 	memset((char*)&ZCSG(interned_strings) + sizeof(zend_string_table),
398 		STRTAB_INVALID_POS,
399 		(char*)ZCSG(interned_strings).start -
400 			((char*)&ZCSG(interned_strings) + sizeof(zend_string_table)));
401 	s = ZCSG(interned_strings).start;
402 	top = ZCSG(interned_strings).top;
403 	n = 0;
404 	if (EXPECTED(s < top)) {
405 		do {
406 			hash_slot = STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), ZSTR_H(s));
407 			STRTAB_COLLISION(s) = *hash_slot;
408 			*hash_slot = STRTAB_STR_TO_POS(&ZCSG(interned_strings), s);
409 			s = STRTAB_NEXT(s);
410 			n++;
411 		} while (s < top);
412 	}
413 	ZCSG(interned_strings).nNumOfElements = n;
414 }
415 
accel_interned_strings_save_state(void)416 static void accel_interned_strings_save_state(void)
417 {
418 	ZCSG(interned_strings).saved_top = ZCSG(interned_strings).top;
419 }
420 
accel_find_interned_string(zend_string * str)421 static zend_always_inline zend_string *accel_find_interned_string(zend_string *str)
422 {
423 /* for now interned strings are supported only for non-ZTS build */
424 	zend_ulong   h;
425 	uint32_t     pos;
426 	zend_string *s;
427 
428 	if (IS_ACCEL_INTERNED(str)) {
429 		/* this is already an interned string */
430 		return str;
431 	}
432 
433 	if (!ZCG(counted)) {
434 		if (!ZCG(accelerator_enabled) || accel_activate_add() == FAILURE) {
435 			return NULL;
436 		}
437 		ZCG(counted) = 1;
438 	}
439 
440 	h = zend_string_hash_val(str);
441 
442 	/* check for existing interned string */
443 	pos = *STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), h);
444 	if (EXPECTED(pos != STRTAB_INVALID_POS)) {
445 		do {
446 			s = STRTAB_POS_TO_STR(&ZCSG(interned_strings), pos);
447 			if (EXPECTED(ZSTR_H(s) == h) && zend_string_equal_content(s, str)) {
448 				return s;
449 			}
450 			pos = STRTAB_COLLISION(s);
451 		} while (pos != STRTAB_INVALID_POS);
452 	}
453 
454 	return NULL;
455 }
456 
accel_new_interned_string(zend_string * str)457 zend_string* ZEND_FASTCALL accel_new_interned_string(zend_string *str)
458 {
459 	zend_ulong   h;
460 	uint32_t     pos, *hash_slot;
461 	zend_string *s;
462 
463 #ifdef HAVE_OPCACHE_FILE_CACHE
464 	if (UNEXPECTED(file_cache_only)) {
465 		return str;
466 	}
467 #endif
468 
469 	if (IS_ACCEL_INTERNED(str)) {
470 		/* this is already an interned string */
471 		return str;
472 	}
473 
474 	h = zend_string_hash_val(str);
475 
476 	/* check for existing interned string */
477 	hash_slot = STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), h);
478 	pos = *hash_slot;
479 	if (EXPECTED(pos != STRTAB_INVALID_POS)) {
480 		do {
481 			s = STRTAB_POS_TO_STR(&ZCSG(interned_strings), pos);
482 			if (EXPECTED(ZSTR_H(s) == h) && zend_string_equal_content(s, str)) {
483 				zend_string_release(str);
484 				return s;
485 			}
486 			pos = STRTAB_COLLISION(s);
487 		} while (pos != STRTAB_INVALID_POS);
488 	}
489 
490 	if (UNEXPECTED((char*)ZCSG(interned_strings).end - (char*)ZCSG(interned_strings).top < STRTAB_STR_SIZE(str))) {
491 	    /* no memory, return the same non-interned string */
492 		zend_accel_error(ACCEL_LOG_WARNING, "Interned string buffer overflow");
493 		return str;
494 	}
495 
496 	/* create new interning string in shared interned strings buffer */
497 	ZCSG(interned_strings).nNumOfElements++;
498 	s = ZCSG(interned_strings).top;
499 	hash_slot = STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), h);
500 	STRTAB_COLLISION(s) = *hash_slot;
501 	*hash_slot = STRTAB_STR_TO_POS(&ZCSG(interned_strings), s);
502 	GC_SET_REFCOUNT(s, 1);
503 	GC_TYPE_INFO(s) = IS_STRING | ((IS_STR_INTERNED | IS_STR_PERMANENT) << GC_FLAGS_SHIFT);
504 	ZSTR_H(s) = h;
505 	ZSTR_LEN(s) = ZSTR_LEN(str);
506 	memcpy(ZSTR_VAL(s), ZSTR_VAL(str), ZSTR_LEN(s) + 1);
507 	ZCSG(interned_strings).top = STRTAB_NEXT(s);
508 
509 	zend_string_release(str);
510 	return s;
511 }
512 
accel_new_interned_string_for_php(zend_string * str)513 static zend_string* ZEND_FASTCALL accel_new_interned_string_for_php(zend_string *str)
514 {
515 	zend_string_hash_val(str);
516 	if (ZCG(counted)) {
517 		zend_string *ret = accel_find_interned_string(str);
518 
519 		if (ret) {
520 			zend_string_release(str);
521 			return ret;
522 		}
523 	}
524 	return str;
525 }
526 
accel_find_interned_string_ex(zend_ulong h,const char * str,size_t size)527 static zend_always_inline zend_string *accel_find_interned_string_ex(zend_ulong h, const char *str, size_t size)
528 {
529 	uint32_t     pos;
530 	zend_string *s;
531 
532 	/* check for existing interned string */
533 	pos = *STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), h);
534 	if (EXPECTED(pos != STRTAB_INVALID_POS)) {
535 		do {
536 			s = STRTAB_POS_TO_STR(&ZCSG(interned_strings), pos);
537 			if (EXPECTED(ZSTR_H(s) == h) && EXPECTED(ZSTR_LEN(s) == size)) {
538 				if (!memcmp(ZSTR_VAL(s), str, size)) {
539 					return s;
540 				}
541 			}
542 			pos = STRTAB_COLLISION(s);
543 		} while (pos != STRTAB_INVALID_POS);
544 	}
545 	return NULL;
546 }
547 
accel_init_interned_string_for_php(const char * str,size_t size,int permanent)548 static zend_string* ZEND_FASTCALL accel_init_interned_string_for_php(const char *str, size_t size, int permanent)
549 {
550 	if (ZCG(counted)) {
551 	    zend_ulong h = zend_inline_hash_func(str, size);
552 		zend_string *ret = accel_find_interned_string_ex(h, str, size);
553 
554 		if (!ret) {
555 			ret = zend_string_init(str, size, permanent);
556 			ZSTR_H(ret) = h;
557 		}
558 
559 		return ret;
560 	}
561 
562 	return zend_string_init(str, size, permanent);
563 }
564 
565 /* 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)566 static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_interned_string)
567 {
568 	uint32_t j;
569 	Bucket *p, *q;
570 	HashTable *ht;
571 
572 	/* empty string */
573 	zend_empty_string = new_interned_string(zend_empty_string);
574 	for (j = 0; j < 256; j++) {
575 		zend_one_char_string[j] = new_interned_string(ZSTR_CHAR(j));
576 	}
577 	for (j = 0; j < ZEND_STR_LAST_KNOWN; j++) {
578 		zend_known_strings[j] = new_interned_string(zend_known_strings[j]);
579 	}
580 
581 	/* function table hash keys */
582 	ZEND_HASH_FOREACH_BUCKET(CG(function_table), p) {
583 		if (p->key) {
584 			p->key = new_interned_string(p->key);
585 		}
586 		if (Z_FUNC(p->val)->common.function_name) {
587 			Z_FUNC(p->val)->common.function_name = new_interned_string(Z_FUNC(p->val)->common.function_name);
588 		}
589 		if (Z_FUNC(p->val)->common.arg_info &&
590 		    (Z_FUNC(p->val)->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS))) {
591 			uint32_t i;
592 			uint32_t num_args = Z_FUNC(p->val)->common.num_args + 1;
593 			zend_arg_info *arg_info = Z_FUNC(p->val)->common.arg_info - 1;
594 
595 			if (Z_FUNC(p->val)->common.fn_flags & ZEND_ACC_VARIADIC) {
596 				num_args++;
597 			}
598 			for (i = 0 ; i < num_args; i++) {
599 				if (ZEND_TYPE_IS_CLASS(arg_info[i].type)) {
600 					zend_bool allow_null = ZEND_TYPE_ALLOW_NULL(arg_info[i].type);
601 					arg_info[i].type = ZEND_TYPE_ENCODE_CLASS(new_interned_string(ZEND_TYPE_NAME(arg_info[i].type)), allow_null);
602 				}
603 			}
604 		}
605 	} ZEND_HASH_FOREACH_END();
606 
607 	/* class table hash keys, class names, properties, methods, constants, etc */
608 	ZEND_HASH_FOREACH_BUCKET(CG(class_table), p) {
609 		zend_class_entry *ce;
610 
611 		ce = (zend_class_entry*)Z_PTR(p->val);
612 
613 		if (p->key) {
614 			p->key = new_interned_string(p->key);
615 		}
616 
617 		if (ce->name) {
618 			ce->name = new_interned_string(ce->name);
619 		}
620 
621 		ZEND_HASH_FOREACH_BUCKET(&ce->properties_info, q) {
622 			zend_property_info *info;
623 
624 			info = (zend_property_info*)Z_PTR(q->val);
625 
626 			if (q->key) {
627 				q->key = new_interned_string(q->key);
628 			}
629 
630 			if (info->name) {
631 				info->name = new_interned_string(info->name);
632 			}
633 		} ZEND_HASH_FOREACH_END();
634 
635 		ZEND_HASH_FOREACH_BUCKET(&ce->function_table, q) {
636 			if (q->key) {
637 				q->key = new_interned_string(q->key);
638 			}
639 			if (Z_FUNC(q->val)->common.function_name) {
640 				Z_FUNC(q->val)->common.function_name = new_interned_string(Z_FUNC(q->val)->common.function_name);
641 			}
642 		} ZEND_HASH_FOREACH_END();
643 
644 		ZEND_HASH_FOREACH_BUCKET(&ce->constants_table, q) {
645 			if (q->key) {
646 				q->key = new_interned_string(q->key);
647 			}
648 		} ZEND_HASH_FOREACH_END();
649 	} ZEND_HASH_FOREACH_END();
650 
651 	/* constant hash keys */
652 	ZEND_HASH_FOREACH_BUCKET(EG(zend_constants), p) {
653 		zend_constant *c;
654 
655 		if (p->key) {
656 			p->key = new_interned_string(p->key);
657 		}
658 		c = (zend_constant*)Z_PTR(p->val);
659 		if (c->name) {
660 			c->name = new_interned_string(c->name);
661 		}
662 		if (Z_TYPE(c->value) == IS_STRING) {
663 			ZVAL_STR(&c->value, new_interned_string(Z_STR(c->value)));
664 		}
665 	} ZEND_HASH_FOREACH_END();
666 
667 	/* auto globals hash keys and names */
668 	ZEND_HASH_FOREACH_BUCKET(CG(auto_globals), p) {
669 		zend_auto_global *auto_global;
670 
671 		auto_global = (zend_auto_global*)Z_PTR(p->val);
672 
673 		zend_string_addref(auto_global->name);
674 		auto_global->name = new_interned_string(auto_global->name);
675 		if (p->key) {
676 			p->key = new_interned_string(p->key);
677 		}
678 	} ZEND_HASH_FOREACH_END();
679 
680 	ZEND_HASH_FOREACH_BUCKET(&module_registry, p) {
681 		if (p->key) {
682 			p->key = new_interned_string(p->key);
683 		}
684 	} ZEND_HASH_FOREACH_END();
685 
686 	ZEND_HASH_FOREACH_BUCKET(EG(ini_directives), p) {
687 		zend_ini_entry *entry = (zend_ini_entry*)Z_PTR(p->val);
688 
689 		if (p->key) {
690 			p->key = new_interned_string(p->key);
691 		}
692 		if (entry->name) {
693 			entry->name = new_interned_string(entry->name);
694 		}
695 		if (entry->value) {
696 			entry->value = new_interned_string(entry->value);
697 		}
698 		if (entry->orig_value) {
699 			entry->orig_value = new_interned_string(entry->orig_value);
700 		}
701 	} ZEND_HASH_FOREACH_END();
702 
703 	ht = php_get_stream_filters_hash_global();
704 	ZEND_HASH_FOREACH_BUCKET(ht, p) {
705 		if (p->key) {
706 			p->key = new_interned_string(p->key);
707 		}
708 	} ZEND_HASH_FOREACH_END();
709 
710 	ht = php_stream_get_url_stream_wrappers_hash_global();
711 	ZEND_HASH_FOREACH_BUCKET(ht, p) {
712 		if (p->key) {
713 			p->key = new_interned_string(p->key);
714 		}
715 	} ZEND_HASH_FOREACH_END();
716 
717 	ht = php_stream_xport_get_hash();
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 
accel_replace_string_by_shm_permanent(zend_string * str)725 static zend_string* ZEND_FASTCALL accel_replace_string_by_shm_permanent(zend_string *str)
726 {
727 	zend_string *ret = accel_find_interned_string(str);
728 
729 	if (ret) {
730 		zend_string_release(str);
731 		return ret;
732 	}
733 	return str;
734 }
735 
accel_replace_string_by_process_permanent(zend_string * str)736 static zend_string* ZEND_FASTCALL accel_replace_string_by_process_permanent(zend_string *str)
737 {
738 	zend_string *ret = zend_interned_string_find_permanent(str);
739 	if (ret) {
740 		zend_string_release(str);
741 		return ret;
742 	}
743 	ZEND_ASSERT(!IS_ACCEL_INTERNED(str));
744 	return str;
745 }
746 
747 
accel_use_shm_interned_strings(void)748 static void accel_use_shm_interned_strings(void)
749 {
750 	HANDLE_BLOCK_INTERRUPTIONS();
751 	SHM_UNPROTECT();
752 	zend_shared_alloc_lock();
753 
754 	if (ZCSG(interned_strings).saved_top == NULL) {
755 		accel_copy_permanent_strings(accel_new_interned_string);
756 	} else {
757 		ZCG(counted) = 1;
758 		accel_copy_permanent_strings(accel_replace_string_by_shm_permanent);
759 		ZCG(counted) = 0;
760 	}
761 	accel_interned_strings_save_state();
762 
763 	zend_shared_alloc_unlock();
764 	SHM_PROTECT();
765 	HANDLE_UNBLOCK_INTERRUPTIONS();
766 }
767 
accel_use_permanent_interned_strings(void)768 static void accel_use_permanent_interned_strings(void)
769 {
770 	accel_copy_permanent_strings(accel_replace_string_by_process_permanent);
771 }
772 
773 #ifndef ZEND_WIN32
kill_all_lockers(struct flock * mem_usage_check)774 static inline void kill_all_lockers(struct flock *mem_usage_check)
775 {
776 	int success, tries;
777 	/* so that other process won't try to force while we are busy cleaning up */
778 	ZCSG(force_restart_time) = 0;
779 	while (mem_usage_check->l_pid > 0) {
780 		/* Clear previous errno, reset success and tries */
781 		errno = 0;
782 		success = 0;
783 		tries = 10;
784 
785 		while (tries--) {
786 			zend_accel_error(ACCEL_LOG_WARNING, "Attempting to kill locker %d", mem_usage_check->l_pid);
787 			if (kill(mem_usage_check->l_pid, SIGKILL)) {
788 				if (errno == ESRCH) {
789 					/* Process died before the signal was sent */
790 					success = 1;
791 					zend_accel_error(ACCEL_LOG_WARNING, "Process %d died before SIGKILL was sent", mem_usage_check->l_pid);
792 				}
793 				break;
794 			}
795 			/* give it a chance to die */
796 			usleep(20000);
797 			if (kill(mem_usage_check->l_pid, 0)) {
798 				if (errno == ESRCH) {
799 					/* successfully killed locker, process no longer exists  */
800 					success = 1;
801 					zend_accel_error(ACCEL_LOG_WARNING, "Killed locker %d", mem_usage_check->l_pid);
802 				}
803 				break;
804 			}
805 			usleep(10000);
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_FD:
977 			if (zend_fstat(file_handle->handle.fd, &statbuf) == -1) {
978 				return 0;
979 			}
980 			break;
981 		case ZEND_HANDLE_FP:
982 			if (zend_fstat(fileno(file_handle->handle.fp), &statbuf) == -1) {
983 				if (zend_get_stream_timestamp(file_handle->filename, &statbuf) != SUCCESS) {
984 					return 0;
985 				}
986 			}
987 			break;
988 		case ZEND_HANDLE_FILENAME:
989 		case ZEND_HANDLE_MAPPED:
990 			if (file_handle->opened_path) {
991 				char *file_path = ZSTR_VAL(file_handle->opened_path);
992 
993 				if (is_stream_path(file_path)) {
994 					if (zend_get_stream_timestamp(file_path, &statbuf) == SUCCESS) {
995 						break;
996 					}
997 				}
998 				if (VCWD_STAT(file_path, &statbuf) != -1) {
999 					break;
1000 				}
1001 			}
1002 
1003 			if (zend_get_stream_timestamp(file_handle->filename, &statbuf) != SUCCESS) {
1004 				return 0;
1005 			}
1006 			break;
1007 		case ZEND_HANDLE_STREAM:
1008 			{
1009 				php_stream *stream = (php_stream *)file_handle->handle.stream.handle;
1010 				php_stream_statbuf sb;
1011 				int ret, er;
1012 
1013 				if (!stream ||
1014 				    !stream->ops ||
1015 				    !stream->ops->stat) {
1016 					return 0;
1017 				}
1018 
1019 				er = EG(error_reporting);
1020 				EG(error_reporting) = 0;
1021 				zend_try {
1022 					ret = stream->ops->stat(stream, &sb);
1023 				} zend_catch {
1024 					ret = -1;
1025 				} zend_end_try();
1026 				EG(error_reporting) = er;
1027 				if (ret != 0) {
1028 					return 0;
1029 				}
1030 
1031 				statbuf = sb.sb;
1032 			}
1033 			break;
1034 
1035 		default:
1036 			return 0;
1037 	}
1038 
1039 	if (size) {
1040 		*size = statbuf.st_size;
1041 	}
1042 	return statbuf.st_mtime;
1043 }
1044 
do_validate_timestamps(zend_persistent_script * persistent_script,zend_file_handle * file_handle)1045 static inline int do_validate_timestamps(zend_persistent_script *persistent_script, zend_file_handle *file_handle)
1046 {
1047 	zend_file_handle ps_handle;
1048 	zend_string *full_path_ptr = NULL;
1049 
1050 	/** check that the persistent script is indeed the same file we cached
1051 	 * (if part of the path is a symlink than it possible that the user will change it)
1052 	 * See bug #15140
1053 	 */
1054 	if (file_handle->opened_path) {
1055 		if (persistent_script->script.filename != file_handle->opened_path &&
1056 		    !zend_string_equal_content(persistent_script->script.filename, file_handle->opened_path)) {
1057 			return FAILURE;
1058 		}
1059 	} else {
1060 		full_path_ptr = accelerator_orig_zend_resolve_path(file_handle->filename, strlen(file_handle->filename));
1061 		if (full_path_ptr &&
1062 		    persistent_script->script.filename != full_path_ptr &&
1063 		    !zend_string_equal_content(persistent_script->script.filename, full_path_ptr)) {
1064 			zend_string_release_ex(full_path_ptr, 0);
1065 			return FAILURE;
1066 		}
1067 		file_handle->opened_path = full_path_ptr;
1068 	}
1069 
1070 	if (persistent_script->timestamp == 0) {
1071 		if (full_path_ptr) {
1072 			zend_string_release_ex(full_path_ptr, 0);
1073 			file_handle->opened_path = NULL;
1074 		}
1075 		return FAILURE;
1076 	}
1077 
1078 	if (zend_get_file_handle_timestamp(file_handle, NULL) == persistent_script->timestamp) {
1079 		if (full_path_ptr) {
1080 			zend_string_release_ex(full_path_ptr, 0);
1081 			file_handle->opened_path = NULL;
1082 		}
1083 		return SUCCESS;
1084 	}
1085 	if (full_path_ptr) {
1086 		zend_string_release_ex(full_path_ptr, 0);
1087 		file_handle->opened_path = NULL;
1088 	}
1089 
1090 	ps_handle.type = ZEND_HANDLE_FILENAME;
1091 	ps_handle.filename = ZSTR_VAL(persistent_script->script.filename);
1092 	ps_handle.opened_path = persistent_script->script.filename;
1093 
1094 	if (zend_get_file_handle_timestamp(&ps_handle, NULL) == persistent_script->timestamp) {
1095 		return SUCCESS;
1096 	}
1097 
1098 	return FAILURE;
1099 }
1100 
validate_timestamp_and_record(zend_persistent_script * persistent_script,zend_file_handle * file_handle)1101 int validate_timestamp_and_record(zend_persistent_script *persistent_script, zend_file_handle *file_handle)
1102 {
1103 	if (ZCG(accel_directives).revalidate_freq &&
1104 	    persistent_script->dynamic_members.revalidate >= ZCG(request_time)) {
1105 		return SUCCESS;
1106 	} else if (do_validate_timestamps(persistent_script, file_handle) == FAILURE) {
1107 		return FAILURE;
1108 	} else {
1109 		persistent_script->dynamic_members.revalidate = ZCG(request_time) + ZCG(accel_directives).revalidate_freq;
1110 		return SUCCESS;
1111 	}
1112 }
1113 
validate_timestamp_and_record_ex(zend_persistent_script * persistent_script,zend_file_handle * file_handle)1114 int validate_timestamp_and_record_ex(zend_persistent_script *persistent_script, zend_file_handle *file_handle)
1115 {
1116 	int ret;
1117 
1118 	SHM_UNPROTECT();
1119 	ret = validate_timestamp_and_record(persistent_script, file_handle);
1120 	SHM_PROTECT();
1121 
1122 	return ret;
1123 }
1124 
1125 /* Instead of resolving full real path name each time we need to identify file,
1126  * we create a key that consist from requested file name, current working
1127  * directory, current include_path, etc */
accel_make_persistent_key(const char * path,size_t path_length,int * key_len)1128 char *accel_make_persistent_key(const char *path, size_t path_length, int *key_len)
1129 {
1130 	int key_length;
1131 
1132 	/* CWD and include_path don't matter for absolute file names and streams */
1133     if (IS_ABSOLUTE_PATH(path, path_length)) {
1134 		/* pass */
1135 		ZCG(key_len) = 0;
1136     } else if (UNEXPECTED(is_stream_path(path))) {
1137 		if (!is_cacheable_stream_path(path)) {
1138 			return NULL;
1139 		}
1140 		/* pass */
1141 		ZCG(key_len) = 0;
1142     } else if (UNEXPECTED(!ZCG(accel_directives).use_cwd)) {
1143 		/* pass */
1144 		ZCG(key_len) = 0;
1145     } else {
1146 		const char *include_path = NULL, *cwd = NULL;
1147 		int include_path_len = 0, cwd_len = 0;
1148 		zend_string *parent_script = NULL;
1149 		size_t parent_script_len = 0;
1150 
1151 		if (EXPECTED(ZCG(cwd_key_len))) {
1152 			cwd = ZCG(cwd_key);
1153 			cwd_len = ZCG(cwd_key_len);
1154 		} else {
1155 			zend_string *cwd_str = accel_getcwd();
1156 
1157 			if (UNEXPECTED(!cwd_str)) {
1158 				/* we don't handle this well for now. */
1159 				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);
1160 				return NULL;
1161 			}
1162 			cwd = ZSTR_VAL(cwd_str);
1163 			cwd_len = ZSTR_LEN(cwd_str);
1164 			if (ZCG(cwd_check)) {
1165 				ZCG(cwd_check) = 0;
1166 				if (ZCG(accelerator_enabled)) {
1167 
1168 					zend_string *str = accel_find_interned_string(cwd_str);
1169 					if (!str) {
1170 						HANDLE_BLOCK_INTERRUPTIONS();
1171 						SHM_UNPROTECT();
1172 						zend_shared_alloc_lock();
1173 						str = accel_new_interned_string(zend_string_copy(cwd_str));
1174 						if (str == cwd_str) {
1175 							zend_string_release_ex(str, 0);
1176 							str = NULL;
1177 						}
1178 						zend_shared_alloc_unlock();
1179 						SHM_PROTECT();
1180 						HANDLE_UNBLOCK_INTERRUPTIONS();
1181 					}
1182 					if (str) {
1183 						char buf[32];
1184 						char *res = zend_print_long_to_buf(buf + sizeof(buf) - 1, STRTAB_STR_TO_POS(&ZCSG(interned_strings), str));
1185 
1186 						cwd_len = ZCG(cwd_key_len) = buf + sizeof(buf) - 1 - res;
1187 						cwd = ZCG(cwd_key);
1188 						memcpy(ZCG(cwd_key), res, cwd_len + 1);
1189 					} else {
1190 						return NULL;
1191 					}
1192 				} else {
1193 					return NULL;
1194 				}
1195 			}
1196 		}
1197 
1198 		if (EXPECTED(ZCG(include_path_key_len))) {
1199 			include_path = ZCG(include_path_key);
1200 			include_path_len = ZCG(include_path_key_len);
1201 		} else if (!ZCG(include_path) || ZSTR_LEN(ZCG(include_path)) == 0) {
1202 			include_path = "";
1203 			include_path_len = 0;
1204 		} else {
1205 			include_path = ZSTR_VAL(ZCG(include_path));
1206 			include_path_len = ZSTR_LEN(ZCG(include_path));
1207 
1208 			if (ZCG(include_path_check)) {
1209 				ZCG(include_path_check) = 0;
1210 				if (ZCG(accelerator_enabled)) {
1211 
1212 					zend_string *str = accel_find_interned_string(ZCG(include_path));
1213 					if (!str) {
1214 						HANDLE_BLOCK_INTERRUPTIONS();
1215 						SHM_UNPROTECT();
1216 						zend_shared_alloc_lock();
1217 						str = accel_new_interned_string(zend_string_copy(ZCG(include_path)));
1218 						if (str == ZCG(include_path)) {
1219 							str = NULL;
1220 						}
1221 						zend_shared_alloc_unlock();
1222 						SHM_PROTECT();
1223 						HANDLE_UNBLOCK_INTERRUPTIONS();
1224 					}
1225 					if (str) {
1226 						char buf[32];
1227 						char *res = zend_print_long_to_buf(buf + sizeof(buf) - 1, STRTAB_STR_TO_POS(&ZCSG(interned_strings), str));
1228 
1229 						include_path_len = ZCG(include_path_key_len) = buf + sizeof(buf) - 1 - res;
1230 						include_path = ZCG(include_path_key);
1231 						memcpy(ZCG(include_path_key), res, include_path_len + 1);
1232 					} else {
1233 						return NULL;
1234 					}
1235 				} else {
1236 					return NULL;
1237 				}
1238 			}
1239 		}
1240 
1241 		/* Calculate key length */
1242 		if (UNEXPECTED((size_t)(cwd_len + path_length + include_path_len + 2) >= sizeof(ZCG(key)))) {
1243 			return NULL;
1244 		}
1245 
1246 		/* Generate key
1247 		 * Note - the include_path must be the last element in the key,
1248 		 * since in itself, it may include colons (which we use to separate
1249 		 * different components of the key)
1250 		 */
1251 		memcpy(ZCG(key), path, path_length);
1252 		ZCG(key)[path_length] = ':';
1253 		key_length = path_length + 1;
1254 		memcpy(ZCG(key) + key_length, cwd, cwd_len);
1255 		key_length += cwd_len;
1256 
1257 		if (include_path_len) {
1258 			ZCG(key)[key_length] = ':';
1259 			key_length += 1;
1260 			memcpy(ZCG(key) + key_length, include_path, include_path_len);
1261 			key_length += include_path_len;
1262 		}
1263 
1264 		/* Here we add to the key the parent script directory,
1265 		 * since fopen_wrappers from version 4.0.7 use current script's path
1266 		 * in include path too.
1267 		 */
1268 		if (EXPECTED(EG(current_execute_data)) &&
1269 		    EXPECTED((parent_script = zend_get_executed_filename_ex()) != NULL)) {
1270 
1271 			parent_script_len = ZSTR_LEN(parent_script);
1272 			while ((--parent_script_len > 0) && !IS_SLASH(ZSTR_VAL(parent_script)[parent_script_len]));
1273 
1274 			if (UNEXPECTED((size_t)(key_length + parent_script_len + 1) >= sizeof(ZCG(key)))) {
1275 				return NULL;
1276 			}
1277 			ZCG(key)[key_length] = ':';
1278 			key_length += 1;
1279 			memcpy(ZCG(key) + key_length, ZSTR_VAL(parent_script), parent_script_len);
1280 			key_length += parent_script_len;
1281 		}
1282 		ZCG(key)[key_length] = '\0';
1283 		*key_len = ZCG(key_len) = key_length;
1284 		return ZCG(key);
1285 	}
1286 
1287 	/* not use_cwd */
1288 	*key_len = path_length;
1289 	return (char*)path;
1290 }
1291 
zend_accel_invalidate(const char * filename,size_t filename_len,zend_bool force)1292 int zend_accel_invalidate(const char *filename, size_t filename_len, zend_bool force)
1293 {
1294 	zend_string *realpath;
1295 	zend_persistent_script *persistent_script;
1296 
1297 	if (!ZCG(accelerator_enabled) || accelerator_shm_read_lock() != SUCCESS) {
1298 		return FAILURE;
1299 	}
1300 
1301 	realpath = accelerator_orig_zend_resolve_path(filename, filename_len);
1302 
1303 	if (!realpath) {
1304 		return FAILURE;
1305 	}
1306 
1307 #ifdef HAVE_OPCACHE_FILE_CACHE
1308 	if (ZCG(accel_directives).file_cache) {
1309 		zend_file_cache_invalidate(realpath);
1310 	}
1311 #endif
1312 
1313 	persistent_script = zend_accel_hash_find(&ZCSG(hash), realpath);
1314 	if (persistent_script && !persistent_script->corrupted) {
1315 		zend_file_handle file_handle;
1316 
1317 		file_handle.type = ZEND_HANDLE_FILENAME;
1318 		file_handle.filename = ZSTR_VAL(realpath);
1319 		file_handle.opened_path = realpath;
1320 
1321 		if (force ||
1322 			!ZCG(accel_directives).validate_timestamps ||
1323 			do_validate_timestamps(persistent_script, &file_handle) == FAILURE) {
1324 			HANDLE_BLOCK_INTERRUPTIONS();
1325 			SHM_UNPROTECT();
1326 			zend_shared_alloc_lock();
1327 			if (!persistent_script->corrupted) {
1328 				persistent_script->corrupted = 1;
1329 				persistent_script->timestamp = 0;
1330 				ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
1331 				if (ZSMMG(memory_exhausted)) {
1332 					zend_accel_restart_reason reason =
1333 						zend_accel_hash_is_full(&ZCSG(hash)) ? ACCEL_RESTART_HASH : ACCEL_RESTART_OOM;
1334 					zend_accel_schedule_restart_if_necessary(reason);
1335 				}
1336 			}
1337 			zend_shared_alloc_unlock();
1338 			SHM_PROTECT();
1339 			HANDLE_UNBLOCK_INTERRUPTIONS();
1340 		}
1341 	}
1342 
1343 	accelerator_shm_read_unlock();
1344 	zend_string_release_ex(realpath, 0);
1345 
1346 	return SUCCESS;
1347 }
1348 
1349 /* Adds another key for existing cached script */
zend_accel_add_key(const char * key,unsigned int key_length,zend_accel_hash_entry * bucket)1350 static void zend_accel_add_key(const char *key, unsigned int key_length, zend_accel_hash_entry *bucket)
1351 {
1352 	if (!zend_accel_hash_str_find(&ZCSG(hash), key, key_length)) {
1353 		if (zend_accel_hash_is_full(&ZCSG(hash))) {
1354 			zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1355 			ZSMMG(memory_exhausted) = 1;
1356 			zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
1357 		} else {
1358 			char *new_key = zend_shared_alloc(key_length + 1);
1359 			if (new_key) {
1360 				memcpy(new_key, key, key_length + 1);
1361 				if (zend_accel_hash_update(&ZCSG(hash), new_key, key_length, 1, bucket)) {
1362 					zend_accel_error(ACCEL_LOG_INFO, "Added key '%s'", new_key);
1363 				}
1364 			} else {
1365 				zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
1366 			}
1367 		}
1368 	}
1369 }
1370 
is_phar_file(zend_string * filename)1371 static zend_always_inline zend_bool is_phar_file(zend_string *filename)
1372 {
1373 	return filename && ZSTR_LEN(filename) >= sizeof(".phar") &&
1374 		!memcmp(ZSTR_VAL(filename) + ZSTR_LEN(filename) - (sizeof(".phar")-1), ".phar", sizeof(".phar")-1) &&
1375 		!strstr(ZSTR_VAL(filename), "://");
1376 }
1377 
1378 #ifdef HAVE_OPCACHE_FILE_CACHE
store_script_in_file_cache(zend_persistent_script * new_persistent_script)1379 static zend_persistent_script *store_script_in_file_cache(zend_persistent_script *new_persistent_script)
1380 {
1381 	uint32_t memory_used;
1382 
1383 	zend_shared_alloc_init_xlat_table();
1384 
1385 	/* Calculate the required memory size */
1386 	memory_used = zend_accel_script_persist_calc(new_persistent_script, NULL, 0, 0);
1387 
1388 	/* Allocate memory block */
1389 #if defined(__AVX__) || defined(__SSE2__)
1390 	/* Align to 64-byte boundary */
1391 	ZCG(mem) = zend_arena_alloc(&CG(arena), memory_used + 64);
1392 	ZCG(mem) = (void*)(((zend_uintptr_t)ZCG(mem) + 63L) & ~63L);
1393 #elif ZEND_MM_ALIGNMENT < 8
1394 	/* Align to 8-byte boundary */
1395 	ZCG(mem) = zend_arena_alloc(&CG(arena), memory_used + 8);
1396 	ZCG(mem) = (void*)(((zend_uintptr_t)ZCG(mem) + 7L) & ~7L);
1397 #else
1398 	ZCG(mem) = zend_arena_alloc(&CG(arena), memory_used);
1399 #endif
1400 
1401 	/* Copy into memory block */
1402 	new_persistent_script = zend_accel_script_persist(new_persistent_script, NULL, 0, 0);
1403 
1404 	zend_shared_alloc_destroy_xlat_table();
1405 
1406 	new_persistent_script->is_phar = is_phar_file(new_persistent_script->script.filename);
1407 
1408 	/* Consistency check */
1409 	if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
1410 		zend_accel_error(
1411 			((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
1412 			"Internal error: wrong size calculation: %s start=" ZEND_ADDR_FMT ", end=" ZEND_ADDR_FMT ", real=" ZEND_ADDR_FMT "\n",
1413 			ZSTR_VAL(new_persistent_script->script.filename),
1414 			(size_t)new_persistent_script->mem,
1415 			(size_t)((char *)new_persistent_script->mem + new_persistent_script->size),
1416 			(size_t)ZCG(mem));
1417 	}
1418 
1419 	new_persistent_script->dynamic_members.checksum = zend_accel_script_checksum(new_persistent_script);
1420 
1421 	zend_file_cache_script_store(new_persistent_script, 0);
1422 
1423 	return new_persistent_script;
1424 }
1425 
cache_script_in_file_cache(zend_persistent_script * new_persistent_script,int * from_shared_memory)1426 static zend_persistent_script *cache_script_in_file_cache(zend_persistent_script *new_persistent_script, int *from_shared_memory)
1427 {
1428 	uint32_t orig_compiler_options;
1429 
1430 	/* Check if script may be stored in shared memory */
1431 	if (!zend_accel_script_persistable(new_persistent_script)) {
1432 		return new_persistent_script;
1433 	}
1434 
1435 	orig_compiler_options = CG(compiler_options);
1436 	CG(compiler_options) |= ZEND_COMPILE_WITH_FILE_CACHE;
1437 	if (!zend_optimize_script(&new_persistent_script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level)) {
1438 		CG(compiler_options) = orig_compiler_options;
1439 		return new_persistent_script;
1440 	}
1441 	CG(compiler_options) = orig_compiler_options;
1442 
1443 	*from_shared_memory = 1;
1444 	return store_script_in_file_cache(new_persistent_script);
1445 }
1446 #endif
1447 
cache_script_in_shared_memory(zend_persistent_script * new_persistent_script,const char * key,unsigned int key_length,int * from_shared_memory)1448 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)
1449 {
1450 	zend_accel_hash_entry *bucket;
1451 	uint32_t memory_used;
1452 	uint32_t orig_compiler_options;
1453 
1454 	/* Check if script may be stored in shared memory */
1455 	if (!zend_accel_script_persistable(new_persistent_script)) {
1456 		return new_persistent_script;
1457 	}
1458 
1459 	orig_compiler_options = CG(compiler_options);
1460 #ifdef HAVE_OPCACHE_FILE_CACHE
1461 	if (ZCG(accel_directives).file_cache) {
1462 		CG(compiler_options) |= ZEND_COMPILE_WITH_FILE_CACHE;
1463 	}
1464 #endif
1465 	if (!zend_optimize_script(&new_persistent_script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level)) {
1466 		CG(compiler_options) = orig_compiler_options;
1467 		return new_persistent_script;
1468 	}
1469 	CG(compiler_options) = orig_compiler_options;
1470 
1471 	/* exclusive lock */
1472 	zend_shared_alloc_lock();
1473 
1474 	/* Check if we still need to put the file into the cache (may be it was
1475 	 * already stored by another process. This final check is done under
1476 	 * exclusive lock) */
1477 	bucket = zend_accel_hash_find_entry(&ZCSG(hash), new_persistent_script->script.filename);
1478 	if (bucket) {
1479 		zend_persistent_script *existing_persistent_script = (zend_persistent_script *)bucket->data;
1480 
1481 		if (!existing_persistent_script->corrupted) {
1482 			if (key &&
1483 			    (!ZCG(accel_directives).validate_timestamps ||
1484 			     (new_persistent_script->timestamp == existing_persistent_script->timestamp))) {
1485 				zend_accel_add_key(key, key_length, bucket);
1486 			}
1487 			zend_shared_alloc_unlock();
1488 			return new_persistent_script;
1489 		}
1490 	}
1491 
1492 	if (zend_accel_hash_is_full(&ZCSG(hash))) {
1493 		zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1494 		ZSMMG(memory_exhausted) = 1;
1495 		zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
1496 		zend_shared_alloc_unlock();
1497 #ifdef HAVE_OPCACHE_FILE_CACHE
1498 		if (ZCG(accel_directives).file_cache) {
1499 			new_persistent_script = store_script_in_file_cache(new_persistent_script);
1500 			*from_shared_memory = 1;
1501 		}
1502 #endif
1503 		return new_persistent_script;
1504 	}
1505 
1506 	zend_shared_alloc_init_xlat_table();
1507 
1508 	/* Calculate the required memory size */
1509 	memory_used = zend_accel_script_persist_calc(new_persistent_script, key, key_length, 1);
1510 
1511 	/* Allocate shared memory */
1512 #if defined(__AVX__) || defined(__SSE2__)
1513 	/* Align to 64-byte boundary */
1514 	ZCG(mem) = zend_shared_alloc(memory_used + 64);
1515 	if (ZCG(mem)) {
1516 		ZCG(mem) = (void*)(((zend_uintptr_t)ZCG(mem) + 63L) & ~63L);
1517 #if defined(__x86_64__)
1518 		memset(ZCG(mem), 0, memory_used);
1519 #elif defined(__AVX__)
1520 		{
1521 			char *p = (char*)ZCG(mem);
1522 			char *end = p + memory_used;
1523 			__m256i ymm0 = _mm256_setzero_si256();
1524 
1525 			while (p < end) {
1526 				_mm256_store_si256((__m256i*)p, ymm0);
1527 				_mm256_store_si256((__m256i*)(p+32), ymm0);
1528 				p += 64;
1529 			}
1530 		}
1531 #else
1532 		{
1533 			char *p = (char*)ZCG(mem);
1534 			char *end = p + memory_used;
1535 			__m128i xmm0 = _mm_setzero_si128();
1536 
1537 			while (p < end) {
1538 				_mm_store_si128((__m128i*)p, xmm0);
1539 				_mm_store_si128((__m128i*)(p+16), xmm0);
1540 				_mm_store_si128((__m128i*)(p+32), xmm0);
1541 				_mm_store_si128((__m128i*)(p+48), xmm0);
1542 				p += 64;
1543 			}
1544 		}
1545 #endif
1546 	}
1547 #else
1548 	ZCG(mem) = zend_shared_alloc(memory_used);
1549 	if (ZCG(mem)) {
1550 		memset(ZCG(mem), 0, memory_used);
1551 	}
1552 #endif
1553 	if (!ZCG(mem)) {
1554 		zend_shared_alloc_destroy_xlat_table();
1555 		zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
1556 		zend_shared_alloc_unlock();
1557 #ifdef HAVE_OPCACHE_FILE_CACHE
1558 		if (ZCG(accel_directives).file_cache) {
1559 			new_persistent_script = store_script_in_file_cache(new_persistent_script);
1560 			*from_shared_memory = 1;
1561 		}
1562 #endif
1563 		return new_persistent_script;
1564 	}
1565 
1566 	/* Copy into shared memory */
1567 	new_persistent_script = zend_accel_script_persist(new_persistent_script, &key, key_length, 1);
1568 
1569 	zend_shared_alloc_destroy_xlat_table();
1570 
1571 	new_persistent_script->is_phar = is_phar_file(new_persistent_script->script.filename);
1572 
1573 	/* Consistency check */
1574 	if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
1575 		zend_accel_error(
1576 			((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
1577 			"Internal error: wrong size calculation: %s start=" ZEND_ADDR_FMT ", end=" ZEND_ADDR_FMT ", real=" ZEND_ADDR_FMT "\n",
1578 			ZSTR_VAL(new_persistent_script->script.filename),
1579 			(size_t)new_persistent_script->mem,
1580 			(size_t)((char *)new_persistent_script->mem + new_persistent_script->size),
1581 			(size_t)ZCG(mem));
1582 	}
1583 
1584 	new_persistent_script->dynamic_members.checksum = zend_accel_script_checksum(new_persistent_script);
1585 
1586 	/* store script structure in the hash table */
1587 	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);
1588 	if (bucket) {
1589 		zend_accel_error(ACCEL_LOG_INFO, "Cached script '%s'", ZSTR_VAL(new_persistent_script->script.filename));
1590 		if (key &&
1591 		    /* key may contain non-persistent PHAR aliases (see issues #115 and #149) */
1592 		    memcmp(key, "phar://", sizeof("phar://") - 1) != 0 &&
1593 		    (ZSTR_LEN(new_persistent_script->script.filename) != key_length ||
1594 		     memcmp(ZSTR_VAL(new_persistent_script->script.filename), key, key_length) != 0)) {
1595 			/* link key to the same persistent script in hash table */
1596 			if (zend_accel_hash_update(&ZCSG(hash), key, key_length, 1, bucket)) {
1597 				zend_accel_error(ACCEL_LOG_INFO, "Added key '%s'", key);
1598 			} else {
1599 				zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1600 				ZSMMG(memory_exhausted) = 1;
1601 				zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
1602 			}
1603 		}
1604 	}
1605 
1606 	new_persistent_script->dynamic_members.memory_consumption = ZEND_ALIGNED_SIZE(new_persistent_script->size);
1607 
1608 	zend_shared_alloc_unlock();
1609 
1610 #ifdef HAVE_OPCACHE_FILE_CACHE
1611 	if (ZCG(accel_directives).file_cache) {
1612 		SHM_PROTECT();
1613 		zend_file_cache_script_store(new_persistent_script, 1);
1614 		SHM_UNPROTECT();
1615 	}
1616 #endif
1617 
1618 	*from_shared_memory = 1;
1619 	return new_persistent_script;
1620 }
1621 
1622 static const struct jit_auto_global_info
1623 {
1624     const char *name;
1625     size_t len;
1626 } jit_auto_globals_info[] = {
1627     { "_SERVER",  sizeof("_SERVER")-1},
1628     { "_ENV",     sizeof("_ENV")-1},
1629     { "_REQUEST", sizeof("_REQUEST")-1},
1630     { "GLOBALS",  sizeof("GLOBALS")-1},
1631 };
1632 
1633 static zend_string *jit_auto_globals_str[4];
1634 
zend_accel_get_auto_globals(void)1635 static int zend_accel_get_auto_globals(void)
1636 {
1637 	int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0]));
1638 	int n = 1;
1639 	int mask = 0;
1640 
1641 	for (i = 0; i < ag_size ; i++) {
1642 		if (zend_hash_exists(&EG(symbol_table), jit_auto_globals_str[i])) {
1643 			mask |= n;
1644 		}
1645 		n += n;
1646 	}
1647 	return mask;
1648 }
1649 
zend_accel_get_auto_globals_no_jit(void)1650 static int zend_accel_get_auto_globals_no_jit(void)
1651 {
1652 	if (zend_hash_exists(&EG(symbol_table), jit_auto_globals_str[3])) {
1653 		return 8;
1654 	}
1655 	return 0;
1656 }
1657 
zend_accel_set_auto_globals(int mask)1658 static void zend_accel_set_auto_globals(int mask)
1659 {
1660 	int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0]));
1661 	int n = 1;
1662 
1663 	for (i = 0; i < ag_size ; i++) {
1664 		if ((mask & n) && !(ZCG(auto_globals_mask) & n)) {
1665 			ZCG(auto_globals_mask) |= n;
1666 			zend_is_auto_global(jit_auto_globals_str[i]);
1667 		}
1668 		n += n;
1669 	}
1670 }
1671 
zend_accel_init_auto_globals(void)1672 static void zend_accel_init_auto_globals(void)
1673 {
1674 	int i, ag_size = (sizeof(jit_auto_globals_info) / sizeof(jit_auto_globals_info[0]));
1675 
1676 	for (i = 0; i < ag_size ; i++) {
1677 		jit_auto_globals_str[i] = zend_string_init(jit_auto_globals_info[i].name, jit_auto_globals_info[i].len, 1);
1678 		zend_string_hash_val(jit_auto_globals_str[i]);
1679 		jit_auto_globals_str[i] = accel_new_interned_string(jit_auto_globals_str[i]);
1680 	}
1681 }
1682 
opcache_compile_file(zend_file_handle * file_handle,int type,const char * key,zend_op_array ** op_array_p)1683 static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handle, int type, const char *key, zend_op_array **op_array_p)
1684 {
1685 	zend_persistent_script *new_persistent_script;
1686 	zend_op_array *orig_active_op_array;
1687 	HashTable *orig_function_table, *orig_class_table;
1688 	zval orig_user_error_handler;
1689 	zend_op_array *op_array;
1690 	int do_bailout = 0;
1691 	accel_time_t timestamp = 0;
1692 	uint32_t orig_compiler_options = 0;
1693 
1694     /* Try to open file */
1695     if (file_handle->type == ZEND_HANDLE_FILENAME) {
1696         if (accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle) != SUCCESS) {
1697 			*op_array_p = NULL;
1698 			if (type == ZEND_REQUIRE) {
1699 				zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
1700 				zend_bailout();
1701 			} else {
1702 				zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
1703 			}
1704 			return NULL;
1705     	}
1706     }
1707 
1708 	/* check blacklist right after ensuring that file was opened */
1709 	if (file_handle->opened_path && zend_accel_blacklist_is_blacklisted(&accel_blacklist, ZSTR_VAL(file_handle->opened_path), ZSTR_LEN(file_handle->opened_path))) {
1710 		SHM_UNPROTECT();
1711 		ZCSG(blacklist_misses)++;
1712 		SHM_PROTECT();
1713 		*op_array_p = accelerator_orig_compile_file(file_handle, type);
1714 		return NULL;
1715 	}
1716 
1717 	if (ZCG(accel_directives).validate_timestamps ||
1718 	    ZCG(accel_directives).file_update_protection ||
1719 	    ZCG(accel_directives).max_file_size > 0) {
1720 		size_t size = 0;
1721 
1722 		/* Obtain the file timestamps, *before* actually compiling them,
1723 		 * otherwise we have a race-condition.
1724 		 */
1725 		timestamp = zend_get_file_handle_timestamp(file_handle, ZCG(accel_directives).max_file_size > 0 ? &size : NULL);
1726 
1727 		/* If we can't obtain a timestamp (that means file is possibly socket)
1728 		 *  we won't cache it
1729 		 */
1730 		if (timestamp == 0) {
1731 			*op_array_p = accelerator_orig_compile_file(file_handle, type);
1732 			return NULL;
1733 		}
1734 
1735 		/* check if file is too new (may be it's not written completely yet) */
1736 		if (ZCG(accel_directives).file_update_protection &&
1737 		    ((accel_time_t)(ZCG(request_time) - ZCG(accel_directives).file_update_protection) < timestamp)) {
1738 			*op_array_p = accelerator_orig_compile_file(file_handle, type);
1739 			return NULL;
1740 		}
1741 
1742 		if (ZCG(accel_directives).max_file_size > 0 && size > (size_t)ZCG(accel_directives).max_file_size) {
1743 			SHM_UNPROTECT();
1744 			ZCSG(blacklist_misses)++;
1745 			SHM_PROTECT();
1746 			*op_array_p = accelerator_orig_compile_file(file_handle, type);
1747 			return NULL;
1748 		}
1749 	}
1750 
1751 	new_persistent_script = create_persistent_script();
1752 
1753 	/* Save the original values for the op_array, function table and class table */
1754 	orig_active_op_array = CG(active_op_array);
1755 	orig_function_table = CG(function_table);
1756 	orig_class_table = CG(class_table);
1757 	ZVAL_COPY_VALUE(&orig_user_error_handler, &EG(user_error_handler));
1758 
1759 	/* Override them with ours */
1760 	CG(function_table) = &ZCG(function_table);
1761 	EG(class_table) = CG(class_table) = &new_persistent_script->script.class_table;
1762 	ZVAL_UNDEF(&EG(user_error_handler));
1763 
1764 	zend_try {
1765 		orig_compiler_options = CG(compiler_options);
1766 		CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
1767 		CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES;
1768 		CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING;
1769 		CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION;
1770 #ifdef HAVE_OPCACHE_FILE_CACHE
1771 		if (ZCG(accel_directives).file_cache) {
1772 			CG(compiler_options) |= ZEND_COMPILE_WITH_FILE_CACHE;
1773 		}
1774 #endif
1775 		op_array = *op_array_p = accelerator_orig_compile_file(file_handle, type);
1776 		CG(compiler_options) = orig_compiler_options;
1777 	} zend_catch {
1778 		op_array = NULL;
1779 		do_bailout = 1;
1780 		CG(compiler_options) = orig_compiler_options;
1781 	} zend_end_try();
1782 
1783 	/* Restore originals */
1784 	CG(active_op_array) = orig_active_op_array;
1785 	CG(function_table) = orig_function_table;
1786 	EG(class_table) = CG(class_table) = orig_class_table;
1787 	EG(user_error_handler) = orig_user_error_handler;
1788 
1789 	if (!op_array) {
1790 		/* compilation failed */
1791 		free_persistent_script(new_persistent_script, 1);
1792 		zend_accel_free_user_functions(&ZCG(function_table));
1793 		if (do_bailout) {
1794 			zend_bailout();
1795 		}
1796 		return NULL;
1797 	}
1798 
1799 	/* Build the persistent_script structure.
1800 	   Here we aren't sure we would store it, but we will need it
1801 	   further anyway.
1802 	*/
1803 	zend_accel_move_user_functions(&ZCG(function_table), &new_persistent_script->script.function_table);
1804 	new_persistent_script->script.first_early_binding_opline =
1805 		(op_array->fn_flags & ZEND_ACC_EARLY_BINDING) ?
1806 			zend_build_delayed_early_binding_list(op_array) :
1807 			(uint32_t)-1;
1808 	new_persistent_script->script.main_op_array = *op_array;
1809 
1810 	efree(op_array); /* we have valid persistent_script, so it's safe to free op_array */
1811 
1812     /* Fill in the ping_auto_globals_mask for the new script. If jit for auto globals is enabled we
1813        will have to ping the used auto global variables before execution */
1814 	if (PG(auto_globals_jit)) {
1815 		new_persistent_script->ping_auto_globals_mask = zend_accel_get_auto_globals();
1816 	} else {
1817 		new_persistent_script->ping_auto_globals_mask = zend_accel_get_auto_globals_no_jit();
1818 	}
1819 
1820 	if (ZCG(accel_directives).validate_timestamps) {
1821 		/* Obtain the file timestamps, *before* actually compiling them,
1822 		 * otherwise we have a race-condition.
1823 		 */
1824 		new_persistent_script->timestamp = timestamp;
1825 		new_persistent_script->dynamic_members.revalidate = ZCG(request_time) + ZCG(accel_directives).revalidate_freq;
1826 	}
1827 
1828 	if (file_handle->opened_path) {
1829 		new_persistent_script->script.filename = zend_string_copy(file_handle->opened_path);
1830 	} else {
1831 		new_persistent_script->script.filename = zend_string_init(file_handle->filename, strlen(file_handle->filename), 0);
1832 	}
1833 	zend_string_hash_val(new_persistent_script->script.filename);
1834 
1835 	/* Now persistent_script structure is ready in process memory */
1836 	return new_persistent_script;
1837 }
1838 
1839 #ifdef HAVE_OPCACHE_FILE_CACHE
file_cache_compile_file(zend_file_handle * file_handle,int type)1840 zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int type)
1841 {
1842 	zend_persistent_script *persistent_script;
1843 	zend_op_array *op_array = NULL;
1844 	int from_memory; /* if the script we've got is stored in SHM */
1845 
1846 	if (is_stream_path(file_handle->filename) &&
1847 	    !is_cacheable_stream_path(file_handle->filename)) {
1848 		return accelerator_orig_compile_file(file_handle, type);
1849 	}
1850 
1851 	if (!file_handle->opened_path) {
1852 		if (file_handle->type == ZEND_HANDLE_FILENAME &&
1853 		    accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle) == FAILURE) {
1854 			if (type == ZEND_REQUIRE) {
1855 				zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
1856 				zend_bailout();
1857 			} else {
1858 				zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
1859 			}
1860 			return NULL;
1861 	    }
1862 	}
1863 
1864 	HANDLE_BLOCK_INTERRUPTIONS();
1865 	SHM_UNPROTECT();
1866 	persistent_script = zend_file_cache_script_load(file_handle);
1867 	SHM_PROTECT();
1868 	HANDLE_UNBLOCK_INTERRUPTIONS();
1869 	if (persistent_script) {
1870 		/* see bug #15471 (old BTS) */
1871 		if (persistent_script->script.filename) {
1872 			if (!EG(current_execute_data) || !EG(current_execute_data)->opline ||
1873 			    !EG(current_execute_data)->func ||
1874 			    !ZEND_USER_CODE(EG(current_execute_data)->func->common.type) ||
1875 			    EG(current_execute_data)->opline->opcode != ZEND_INCLUDE_OR_EVAL ||
1876 			    (EG(current_execute_data)->opline->extended_value != ZEND_INCLUDE_ONCE &&
1877 			     EG(current_execute_data)->opline->extended_value != ZEND_REQUIRE_ONCE)) {
1878 				if (zend_hash_add_empty_element(&EG(included_files), persistent_script->script.filename) != NULL) {
1879 					/* ext/phar has to load phar's metadata into memory */
1880 					if (persistent_script->is_phar) {
1881 						php_stream_statbuf ssb;
1882 						char *fname = emalloc(sizeof("phar://") + ZSTR_LEN(persistent_script->script.filename));
1883 
1884 						memcpy(fname, "phar://", sizeof("phar://") - 1);
1885 						memcpy(fname + sizeof("phar://") - 1, ZSTR_VAL(persistent_script->script.filename), ZSTR_LEN(persistent_script->script.filename) + 1);
1886 						php_stream_stat_path(fname, &ssb);
1887 						efree(fname);
1888 					}
1889 				}
1890 			}
1891 		}
1892 		zend_file_handle_dtor(file_handle);
1893 
1894 	    if (persistent_script->ping_auto_globals_mask) {
1895 			zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask);
1896 		}
1897 
1898 		return zend_accel_load_script(persistent_script, 1);
1899 	}
1900 
1901 	persistent_script = opcache_compile_file(file_handle, type, NULL, &op_array);
1902 
1903 	if (persistent_script) {
1904 		from_memory = 0;
1905 		persistent_script = cache_script_in_file_cache(persistent_script, &from_memory);
1906 		return zend_accel_load_script(persistent_script, from_memory);
1907 	}
1908 
1909 	return op_array;
1910 }
1911 #endif
1912 
check_persistent_script_access(zend_persistent_script * persistent_script)1913 int check_persistent_script_access(zend_persistent_script *persistent_script)
1914 {
1915     char *phar_path, *ptr;
1916     int ret;
1917     if ((ZSTR_LEN(persistent_script->script.filename)<sizeof("phar://.phar")) ||
1918          memcmp(ZSTR_VAL(persistent_script->script.filename), "phar://", sizeof("phar://")-1)) {
1919 
1920         return access(ZSTR_VAL(persistent_script->script.filename), R_OK) != 0;
1921 
1922     } else {
1923         /* we got a cached file from .phar, so we have to strip prefix and path inside .phar to check access() */
1924         phar_path = estrdup(ZSTR_VAL(persistent_script->script.filename)+sizeof("phar://")-1);
1925         if ((ptr = strstr(phar_path, ".phar/")) != NULL)
1926         {
1927             *(ptr+sizeof(".phar/")-2) = 0; /* strip path inside .phar file */
1928         }
1929         ret = access(phar_path, R_OK) != 0;
1930         efree(phar_path);
1931         return ret;
1932     }
1933 }
1934 
1935 
1936 /* zend_compile() replacement */
persistent_compile_file(zend_file_handle * file_handle,int type)1937 zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
1938 {
1939 	zend_persistent_script *persistent_script = NULL;
1940 	char *key = NULL;
1941 	int key_length;
1942 	int from_shared_memory; /* if the script we've got is stored in SHM */
1943 
1944 	if (!file_handle->filename || !ZCG(accelerator_enabled)) {
1945 		/* The Accelerator is disabled, act as if without the Accelerator */
1946 		ZCG(cache_opline) = NULL;
1947 		ZCG(cache_persistent_script) = NULL;
1948 #ifdef HAVE_OPCACHE_FILE_CACHE
1949 		if (file_handle->filename
1950 		 && ZCG(accel_directives).file_cache
1951 		 && ZCG(enabled) && accel_startup_ok) {
1952 			return file_cache_compile_file(file_handle, type);
1953 		}
1954 #endif
1955 		return accelerator_orig_compile_file(file_handle, type);
1956 #ifdef HAVE_OPCACHE_FILE_CACHE
1957 	} else if (file_cache_only) {
1958 		ZCG(cache_opline) = NULL;
1959 		ZCG(cache_persistent_script) = NULL;
1960 		return file_cache_compile_file(file_handle, type);
1961 #endif
1962 	} else if (!ZCG(accelerator_enabled) ||
1963 	           (ZCSG(restart_in_progress) && accel_restart_is_active())) {
1964 #ifdef HAVE_OPCACHE_FILE_CACHE
1965 		if (ZCG(accel_directives).file_cache) {
1966 			return file_cache_compile_file(file_handle, type);
1967 		}
1968 #endif
1969 		ZCG(cache_opline) = NULL;
1970 		ZCG(cache_persistent_script) = NULL;
1971 		return accelerator_orig_compile_file(file_handle, type);
1972 	}
1973 
1974 	/* In case this callback is called from include_once, require_once or it's
1975 	 * a main FastCGI request, the key must be already calculated, and cached
1976 	 * persistent script already found */
1977 	if (ZCG(cache_persistent_script) &&
1978 	    ((!EG(current_execute_data) &&
1979 	      file_handle->filename == SG(request_info).path_translated &&
1980 	      ZCG(cache_opline) == NULL) ||
1981 	     (EG(current_execute_data) &&
1982 	      EG(current_execute_data)->func &&
1983 	      ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
1984 	      ZCG(cache_opline) == EG(current_execute_data)->opline))) {
1985 
1986 		persistent_script = ZCG(cache_persistent_script);
1987 		if (ZCG(key_len)) {
1988 			key = ZCG(key);
1989 			key_length = ZCG(key_len);
1990 		}
1991 
1992 	} else {
1993 		if (!ZCG(accel_directives).revalidate_path) {
1994 			/* try to find cached script by key */
1995 			key = accel_make_persistent_key(file_handle->filename, strlen(file_handle->filename), &key_length);
1996 			if (!key) {
1997 				ZCG(cache_opline) = NULL;
1998 				ZCG(cache_persistent_script) = NULL;
1999 				return accelerator_orig_compile_file(file_handle, type);
2000 			}
2001 			persistent_script = zend_accel_hash_str_find(&ZCSG(hash), key, key_length);
2002 		} else if (UNEXPECTED(is_stream_path(file_handle->filename) && !is_cacheable_stream_path(file_handle->filename))) {
2003 			ZCG(cache_opline) = NULL;
2004 			ZCG(cache_persistent_script) = NULL;
2005 			return accelerator_orig_compile_file(file_handle, type);
2006 		}
2007 
2008 		if (!persistent_script) {
2009 			/* try to find cached script by full real path */
2010 			zend_accel_hash_entry *bucket;
2011 
2012 			/* open file to resolve the path */
2013 		    if (file_handle->type == ZEND_HANDLE_FILENAME &&
2014         		accelerator_orig_zend_stream_open_function(file_handle->filename, file_handle) == FAILURE) {
2015 				if (type == ZEND_REQUIRE) {
2016 					zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
2017 					zend_bailout();
2018 				} else {
2019 					zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
2020 				}
2021 				return NULL;
2022 		    }
2023 
2024 			if (file_handle->opened_path) {
2025 				bucket = zend_accel_hash_find_entry(&ZCSG(hash), file_handle->opened_path);
2026 
2027 				if (bucket) {
2028 					persistent_script = (zend_persistent_script *)bucket->data;
2029 
2030 					if (key && !persistent_script->corrupted) {
2031 						HANDLE_BLOCK_INTERRUPTIONS();
2032 						SHM_UNPROTECT();
2033 						zend_shared_alloc_lock();
2034 						zend_accel_add_key(key, key_length, bucket);
2035 						zend_shared_alloc_unlock();
2036 						SHM_PROTECT();
2037 						HANDLE_UNBLOCK_INTERRUPTIONS();
2038 					}
2039 				}
2040 			}
2041 		}
2042 	}
2043 
2044 	/* clear cache */
2045 	ZCG(cache_opline) = NULL;
2046 	ZCG(cache_persistent_script) = NULL;
2047 
2048 	if (persistent_script && persistent_script->corrupted) {
2049 		persistent_script = NULL;
2050 	}
2051 
2052 	/* Make sure we only increase the currently running processes semaphore
2053      * once each execution (this function can be called more than once on
2054      * each execution)
2055      */
2056 	if (!ZCG(counted)) {
2057 		if (accel_activate_add() == FAILURE) {
2058 #ifdef HAVE_OPCACHE_FILE_CACHE
2059 			if (ZCG(accel_directives).file_cache) {
2060 				return file_cache_compile_file(file_handle, type);
2061 			}
2062 #endif
2063 			return accelerator_orig_compile_file(file_handle, type);
2064 		}
2065 		ZCG(counted) = 1;
2066 	}
2067 
2068 	/* Revalidate acessibility of cached file */
2069 	if (EXPECTED(persistent_script != NULL) &&
2070 	    UNEXPECTED(ZCG(accel_directives).validate_permission) &&
2071 	    file_handle->type == ZEND_HANDLE_FILENAME &&
2072 	    UNEXPECTED(check_persistent_script_access(persistent_script))) {
2073 		if (type == ZEND_REQUIRE) {
2074 			zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, file_handle->filename);
2075 			zend_bailout();
2076 		} else {
2077 			zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, file_handle->filename);
2078 		}
2079 		return NULL;
2080 	}
2081 
2082 	HANDLE_BLOCK_INTERRUPTIONS();
2083 	SHM_UNPROTECT();
2084 
2085 	/* If script is found then validate_timestamps if option is enabled */
2086 	if (persistent_script && ZCG(accel_directives).validate_timestamps) {
2087 		if (validate_timestamp_and_record(persistent_script, file_handle) == FAILURE) {
2088 			zend_shared_alloc_lock();
2089 			if (!persistent_script->corrupted) {
2090 				persistent_script->corrupted = 1;
2091 				persistent_script->timestamp = 0;
2092 				ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
2093 				if (ZSMMG(memory_exhausted)) {
2094 					zend_accel_restart_reason reason =
2095 						zend_accel_hash_is_full(&ZCSG(hash)) ? ACCEL_RESTART_HASH : ACCEL_RESTART_OOM;
2096 					zend_accel_schedule_restart_if_necessary(reason);
2097 				}
2098 			}
2099 			zend_shared_alloc_unlock();
2100 			persistent_script = NULL;
2101 		}
2102 	}
2103 
2104 	/* if turned on - check the compiled script ADLER32 checksum */
2105 	if (persistent_script && ZCG(accel_directives).consistency_checks
2106 		&& persistent_script->dynamic_members.hits % ZCG(accel_directives).consistency_checks == 0) {
2107 
2108 		unsigned int checksum = zend_accel_script_checksum(persistent_script);
2109 		if (checksum != persistent_script->dynamic_members.checksum ) {
2110 			/* The checksum is wrong */
2111 			zend_accel_error(ACCEL_LOG_INFO, "Checksum failed for '%s':  expected=0x%08x, found=0x%08x",
2112 							 ZSTR_VAL(persistent_script->script.filename), persistent_script->dynamic_members.checksum, checksum);
2113 			zend_shared_alloc_lock();
2114 			if (!persistent_script->corrupted) {
2115 				persistent_script->corrupted = 1;
2116 				persistent_script->timestamp = 0;
2117 				ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
2118 				if (ZSMMG(memory_exhausted)) {
2119 					zend_accel_restart_reason reason =
2120 						zend_accel_hash_is_full(&ZCSG(hash)) ? ACCEL_RESTART_HASH : ACCEL_RESTART_OOM;
2121 					zend_accel_schedule_restart_if_necessary(reason);
2122 				}
2123 			}
2124 			zend_shared_alloc_unlock();
2125 			persistent_script = NULL;
2126 		}
2127 	}
2128 
2129 #ifdef HAVE_OPCACHE_FILE_CACHE
2130 	/* Check the second level cache */
2131 	if (!persistent_script && ZCG(accel_directives).file_cache) {
2132 		persistent_script = zend_file_cache_script_load(file_handle);
2133 	}
2134 #endif
2135 
2136 	/* If script was not found or invalidated by validate_timestamps */
2137 	if (!persistent_script) {
2138 		uint32_t old_const_num = zend_hash_next_free_element(EG(zend_constants));
2139 		zend_op_array *op_array;
2140 
2141 		/* Cache miss.. */
2142 		ZCSG(misses)++;
2143 
2144 		/* No memory left. Behave like without the Accelerator */
2145 		if (ZSMMG(memory_exhausted) || ZCSG(restart_pending)) {
2146 			SHM_PROTECT();
2147 			HANDLE_UNBLOCK_INTERRUPTIONS();
2148 #ifdef HAVE_OPCACHE_FILE_CACHE
2149 			if (ZCG(accel_directives).file_cache) {
2150 				return file_cache_compile_file(file_handle, type);
2151 			}
2152 #endif
2153 			return accelerator_orig_compile_file(file_handle, type);
2154 		}
2155 
2156 		SHM_PROTECT();
2157 		HANDLE_UNBLOCK_INTERRUPTIONS();
2158 		persistent_script = opcache_compile_file(file_handle, type, key, &op_array);
2159 		HANDLE_BLOCK_INTERRUPTIONS();
2160 		SHM_UNPROTECT();
2161 
2162 		/* Try and cache the script and assume that it is returned from_shared_memory.
2163          * If it isn't compile_and_cache_file() changes the flag to 0
2164          */
2165        	from_shared_memory = 0;
2166 		if (persistent_script) {
2167 			persistent_script = cache_script_in_shared_memory(persistent_script, key, key ? key_length : 0, &from_shared_memory);
2168 		}
2169 
2170 		/* Caching is disabled, returning op_array;
2171 		 * or something went wrong during compilation, returning NULL
2172 		 */
2173 		if (!persistent_script) {
2174 			SHM_PROTECT();
2175 			HANDLE_UNBLOCK_INTERRUPTIONS();
2176 			return op_array;
2177 		}
2178 		if (from_shared_memory) {
2179 			/* Delete immutable arrays moved into SHM */
2180 			uint32_t new_const_num = zend_hash_next_free_element(EG(zend_constants));
2181 			while (new_const_num > old_const_num) {
2182 				new_const_num--;
2183 				zend_hash_index_del(EG(zend_constants), new_const_num);
2184 			}
2185 		}
2186 	} else {
2187 
2188 #if !ZEND_WIN32
2189 		ZCSG(hits)++; /* TBFixed: may lose one hit */
2190 		persistent_script->dynamic_members.hits++; /* see above */
2191 #else
2192 #ifdef _M_X64
2193 		InterlockedIncrement64(&ZCSG(hits));
2194 #else
2195 		InterlockedIncrement(&ZCSG(hits));
2196 #endif
2197 		InterlockedIncrement64(&persistent_script->dynamic_members.hits);
2198 #endif
2199 
2200 		/* see bug #15471 (old BTS) */
2201 		if (persistent_script->script.filename) {
2202 			if (!EG(current_execute_data) || !EG(current_execute_data)->opline ||
2203 			    !EG(current_execute_data)->func ||
2204 			    !ZEND_USER_CODE(EG(current_execute_data)->func->common.type) ||
2205 			    EG(current_execute_data)->opline->opcode != ZEND_INCLUDE_OR_EVAL ||
2206 			    (EG(current_execute_data)->opline->extended_value != ZEND_INCLUDE_ONCE &&
2207 			     EG(current_execute_data)->opline->extended_value != ZEND_REQUIRE_ONCE)) {
2208 				if (zend_hash_add_empty_element(&EG(included_files), persistent_script->script.filename) != NULL) {
2209 					/* ext/phar has to load phar's metadata into memory */
2210 					if (persistent_script->is_phar) {
2211 						php_stream_statbuf ssb;
2212 						char *fname = emalloc(sizeof("phar://") + ZSTR_LEN(persistent_script->script.filename));
2213 
2214 						memcpy(fname, "phar://", sizeof("phar://") - 1);
2215 						memcpy(fname + sizeof("phar://") - 1, ZSTR_VAL(persistent_script->script.filename), ZSTR_LEN(persistent_script->script.filename) + 1);
2216 						php_stream_stat_path(fname, &ssb);
2217 						efree(fname);
2218 					}
2219 				}
2220 			}
2221 		}
2222 		zend_file_handle_dtor(file_handle);
2223 		from_shared_memory = 1;
2224 	}
2225 
2226 	persistent_script->dynamic_members.last_used = ZCG(request_time);
2227 
2228 	SHM_PROTECT();
2229 	HANDLE_UNBLOCK_INTERRUPTIONS();
2230 
2231     /* Fetch jit auto globals used in the script before execution */
2232     if (persistent_script->ping_auto_globals_mask) {
2233 		zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask);
2234 	}
2235 
2236 	return zend_accel_load_script(persistent_script, from_shared_memory);
2237 }
2238 
2239 /* zend_stream_open_function() replacement for PHP 5.3 and above */
persistent_stream_open_function(const char * filename,zend_file_handle * handle)2240 static int persistent_stream_open_function(const char *filename, zend_file_handle *handle)
2241 {
2242 	if (ZCG(cache_persistent_script)) {
2243 		/* check if callback is called from include_once or it's a main request */
2244 		if ((!EG(current_execute_data) &&
2245 		     filename == SG(request_info).path_translated &&
2246 		     ZCG(cache_opline) == NULL) ||
2247 		    (EG(current_execute_data) &&
2248 		     EG(current_execute_data)->func &&
2249 		     ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
2250 		     ZCG(cache_opline) == EG(current_execute_data)->opline)) {
2251 
2252 			/* we are in include_once or FastCGI request */
2253 			handle->filename = (char*)filename;
2254 			handle->free_filename = 0;
2255 			handle->opened_path = zend_string_copy(ZCG(cache_persistent_script)->script.filename);
2256 			handle->type = ZEND_HANDLE_FILENAME;
2257 			return SUCCESS;
2258 		}
2259 		ZCG(cache_opline) = NULL;
2260 		ZCG(cache_persistent_script) = NULL;
2261 	}
2262 	return accelerator_orig_zend_stream_open_function(filename, handle);
2263 }
2264 
2265 /* zend_resolve_path() replacement for PHP 5.3 and above */
persistent_zend_resolve_path(const char * filename,size_t filename_len)2266 static zend_string* persistent_zend_resolve_path(const char *filename, size_t filename_len)
2267 {
2268 	if (
2269 #ifdef HAVE_OPCACHE_FILE_CACHE
2270 		!file_cache_only &&
2271 #endif
2272 	    ZCG(accelerator_enabled)) {
2273 
2274 		/* check if callback is called from include_once or it's a main request */
2275 		if ((!EG(current_execute_data) &&
2276 		     filename == SG(request_info).path_translated) ||
2277 		    (EG(current_execute_data) &&
2278 		     EG(current_execute_data)->func &&
2279 		     ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
2280 		     EG(current_execute_data)->opline->opcode == ZEND_INCLUDE_OR_EVAL &&
2281 		     (EG(current_execute_data)->opline->extended_value == ZEND_INCLUDE_ONCE ||
2282 		      EG(current_execute_data)->opline->extended_value == ZEND_REQUIRE_ONCE))) {
2283 
2284 			/* we are in include_once or FastCGI request */
2285 			zend_string *resolved_path;
2286 			int key_length;
2287 			char *key = NULL;
2288 
2289 			if (!ZCG(accel_directives).revalidate_path) {
2290 				/* lookup by "not-real" path */
2291 				key = accel_make_persistent_key(filename, filename_len, &key_length);
2292 				if (key) {
2293 					zend_accel_hash_entry *bucket = zend_accel_hash_str_find_entry(&ZCSG(hash), key, key_length);
2294 					if (bucket != NULL) {
2295 						zend_persistent_script *persistent_script = (zend_persistent_script *)bucket->data;
2296 						if (!persistent_script->corrupted) {
2297 							ZCG(cache_opline) = EG(current_execute_data) ? EG(current_execute_data)->opline : NULL;
2298 							ZCG(cache_persistent_script) = persistent_script;
2299 							return zend_string_copy(persistent_script->script.filename);
2300 						}
2301 					}
2302 				} else {
2303 					ZCG(cache_opline) = NULL;
2304 					ZCG(cache_persistent_script) = NULL;
2305 					return accelerator_orig_zend_resolve_path(filename, filename_len);
2306 				}
2307 			}
2308 
2309 			/* find the full real path */
2310 			resolved_path = accelerator_orig_zend_resolve_path(filename, filename_len);
2311 
2312 			if (resolved_path) {
2313 				/* lookup by real path */
2314 				zend_accel_hash_entry *bucket = zend_accel_hash_find_entry(&ZCSG(hash), resolved_path);
2315 				if (bucket) {
2316 					zend_persistent_script *persistent_script = (zend_persistent_script *)bucket->data;
2317 					if (!persistent_script->corrupted) {
2318 						if (key) {
2319 							/* add another "key" for the same bucket */
2320 							HANDLE_BLOCK_INTERRUPTIONS();
2321 							SHM_UNPROTECT();
2322 							zend_shared_alloc_lock();
2323 							zend_accel_add_key(key, key_length, bucket);
2324 							zend_shared_alloc_unlock();
2325 							SHM_PROTECT();
2326 							HANDLE_UNBLOCK_INTERRUPTIONS();
2327 						} else {
2328 							ZCG(key_len) = 0;
2329 						}
2330 						ZCG(cache_opline) = EG(current_execute_data) ? EG(current_execute_data)->opline : NULL;
2331 						ZCG(cache_persistent_script) = persistent_script;
2332 						return resolved_path;
2333 					}
2334 				}
2335 			}
2336 
2337 			ZCG(cache_opline) = NULL;
2338 			ZCG(cache_persistent_script) = NULL;
2339 			return resolved_path;
2340 		}
2341 	}
2342 	ZCG(cache_opline) = NULL;
2343 	ZCG(cache_persistent_script) = NULL;
2344 	return accelerator_orig_zend_resolve_path(filename, filename_len);
2345 }
2346 
zend_reset_cache_vars(void)2347 static void zend_reset_cache_vars(void)
2348 {
2349 	ZSMMG(memory_exhausted) = 0;
2350 	ZCSG(hits) = 0;
2351 	ZCSG(misses) = 0;
2352 	ZCSG(blacklist_misses) = 0;
2353 	ZSMMG(wasted_shared_memory) = 0;
2354 	ZCSG(restart_pending) = 0;
2355 	ZCSG(force_restart_time) = 0;
2356 }
2357 
accel_reset_pcre_cache(void)2358 static void accel_reset_pcre_cache(void)
2359 {
2360 	Bucket *p;
2361 
2362 	ZEND_HASH_FOREACH_BUCKET(&PCRE_G(pcre_cache), p) {
2363 		/* Remove PCRE cache entries with inconsistent keys */
2364 		if (zend_accel_in_shm(p->key)) {
2365 			p->key = NULL;
2366 			zend_hash_del_bucket(&PCRE_G(pcre_cache), p);
2367 		}
2368 	} ZEND_HASH_FOREACH_END();
2369 }
2370 
accel_activate(void)2371 static void accel_activate(void)
2372 {
2373 	if (!ZCG(enabled) || !accel_startup_ok) {
2374 		ZCG(accelerator_enabled) = 0;
2375 		return;
2376 	}
2377 
2378 	if (!ZCG(function_table).nTableSize) {
2379 		zend_hash_init(&ZCG(function_table), zend_hash_num_elements(CG(function_table)), NULL, ZEND_FUNCTION_DTOR, 1);
2380 		zend_accel_copy_internal_functions();
2381 	}
2382 
2383 	/* PHP-5.4 and above return "double", but we use 1 sec precision */
2384 	ZCG(auto_globals_mask) = 0;
2385 	ZCG(request_time) = (time_t)sapi_get_request_time();
2386 	ZCG(cache_opline) = NULL;
2387 	ZCG(cache_persistent_script) = NULL;
2388 	ZCG(include_path_key_len) = 0;
2389 	ZCG(include_path_check) = 1;
2390 
2391 	/* check if ZCG(function_table) wasn't somehow polluted on the way */
2392 	if (ZCG(internal_functions_count) != (zend_long)zend_hash_num_elements(&ZCG(function_table))) {
2393 		zend_accel_error(ACCEL_LOG_WARNING, "Internal functions count changed - was %d, now %d", ZCG(internal_functions_count), zend_hash_num_elements(&ZCG(function_table)));
2394 	}
2395 
2396 	ZCG(cwd) = NULL;
2397 	ZCG(cwd_key_len) = 0;
2398 	ZCG(cwd_check) = 1;
2399 
2400 #ifdef HAVE_OPCACHE_FILE_CACHE
2401 	if (file_cache_only) {
2402 		ZCG(accelerator_enabled) = 0;
2403 		return;
2404 	}
2405 #endif
2406 
2407 #ifndef ZEND_WIN32
2408 	if (ZCG(accel_directives).validate_root) {
2409 		struct stat buf;
2410 
2411 		if (stat("/", &buf) != 0) {
2412 			ZCG(root_hash) = 0;
2413 		} else {
2414 			ZCG(root_hash) = buf.st_ino;
2415 			if (sizeof(buf.st_ino) > sizeof(ZCG(root_hash))) {
2416 				if (ZCG(root_hash) != buf.st_ino) {
2417 					zend_string *key = zend_string_init("opcache.enable", sizeof("opcache.enable")-1, 0);
2418 					zend_alter_ini_entry_chars(key, "0", 1, ZEND_INI_SYSTEM, ZEND_INI_STAGE_RUNTIME);
2419 					zend_string_release_ex(key, 0);
2420 					zend_accel_error(ACCEL_LOG_WARNING, "Can't cache files in chroot() directory with too big inode");
2421 					return;
2422 				}
2423 			}
2424 		}
2425 	} else {
2426 		ZCG(root_hash) = 0;
2427 	}
2428 #endif
2429 
2430 	HANDLE_BLOCK_INTERRUPTIONS();
2431 	SHM_UNPROTECT();
2432 
2433 	if (ZCG(counted)) {
2434 #ifdef ZTS
2435 		zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for thread id %lu", (unsigned long) tsrm_thread_id());
2436 #else
2437 		zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for pid %d", getpid());
2438 #endif
2439 		accel_unlock_all();
2440 		ZCG(counted) = 0;
2441 	}
2442 
2443 	if (ZCSG(restart_pending)) {
2444 		zend_shared_alloc_lock();
2445 		if (ZCSG(restart_pending) != 0) { /* check again, to ensure that the cache wasn't already cleaned by another process */
2446 			if (accel_is_inactive() == SUCCESS) {
2447 				zend_accel_error(ACCEL_LOG_DEBUG, "Restarting!");
2448 				ZCSG(restart_pending) = 0;
2449 				switch ZCSG(restart_reason) {
2450 					case ACCEL_RESTART_OOM:
2451 						ZCSG(oom_restarts)++;
2452 						break;
2453 					case ACCEL_RESTART_HASH:
2454 						ZCSG(hash_restarts)++;
2455 						break;
2456 					case ACCEL_RESTART_USER:
2457 						ZCSG(manual_restarts)++;
2458 						break;
2459 				}
2460 				accel_restart_enter();
2461 
2462 				zend_reset_cache_vars();
2463 				zend_accel_hash_clean(&ZCSG(hash));
2464 
2465 				if (ZCG(accel_directives).interned_strings_buffer) {
2466 					accel_interned_strings_restore_state();
2467 				}
2468 
2469 				zend_shared_alloc_restore_state();
2470 				ZCSG(accelerator_enabled) = ZCSG(cache_status_before_restart);
2471 				if (ZCSG(last_restart_time) < ZCG(request_time)) {
2472 					ZCSG(last_restart_time) = ZCG(request_time);
2473 				} else {
2474 					ZCSG(last_restart_time)++;
2475 				}
2476 				accel_restart_leave();
2477 			}
2478 		}
2479 		zend_shared_alloc_unlock();
2480 	}
2481 
2482 	ZCG(accelerator_enabled) = ZCSG(accelerator_enabled);
2483 
2484 	SHM_PROTECT();
2485 	HANDLE_UNBLOCK_INTERRUPTIONS();
2486 
2487 	if (ZCG(accelerator_enabled) && ZCSG(last_restart_time) != ZCG(last_restart_time)) {
2488 		/* SHM was reinitialized. */
2489 		ZCG(last_restart_time) = ZCSG(last_restart_time);
2490 
2491 		/* Reset in-process realpath cache */
2492 		realpath_cache_clean();
2493 
2494 		accel_reset_pcre_cache();
2495 		ZCG(pcre_reseted) = 0;
2496 	} else if (!ZCG(accelerator_enabled) && !ZCG(pcre_reseted)) {
2497 		accel_reset_pcre_cache();
2498 		ZCG(pcre_reseted) = 1;
2499 	}
2500 }
2501 
accel_post_deactivate(void)2502 int accel_post_deactivate(void)
2503 {
2504 	if (!ZCG(enabled) || !accel_startup_ok) {
2505 		return SUCCESS;
2506 	}
2507 
2508 	zend_shared_alloc_safe_unlock(); /* be sure we didn't leave cache locked */
2509 	accel_unlock_all();
2510 	ZCG(counted) = 0;
2511 
2512 	return SUCCESS;
2513 }
2514 
accel_deactivate(void)2515 static void accel_deactivate(void)
2516 {
2517 	/* ensure that we restore function_table and class_table
2518 	 * In general, they're restored by persistent_compile_file(), but in case
2519 	 * the script is aborted abnormally, they may become messed up.
2520 	 */
2521 
2522 	if (ZCG(cwd)) {
2523 		zend_string_release_ex(ZCG(cwd), 0);
2524 		ZCG(cwd) = NULL;
2525 	}
2526 }
2527 
accelerator_remove_cb(zend_extension * element1,zend_extension * element2)2528 static int accelerator_remove_cb(zend_extension *element1, zend_extension *element2)
2529 {
2530 	(void)element2; /* keep the compiler happy */
2531 
2532 	if (!strcmp(element1->name, ACCELERATOR_PRODUCT_NAME )) {
2533 		element1->startup = NULL;
2534 #if 0
2535 		/* We have to call shutdown callback it to free TS resources */
2536 		element1->shutdown = NULL;
2537 #endif
2538 		element1->activate = NULL;
2539 		element1->deactivate = NULL;
2540 		element1->op_array_handler = NULL;
2541 
2542 #ifdef __DEBUG_MESSAGES__
2543         fprintf(stderr, ACCELERATOR_PRODUCT_NAME " is disabled: %s\n", (zps_failure_reason ? zps_failure_reason : "unknown error"));
2544         fflush(stderr);
2545 #endif
2546 	}
2547 
2548 	return 0;
2549 }
2550 
zps_startup_failure(char * reason,char * api_reason,int (* cb)(zend_extension *,zend_extension *))2551 static void zps_startup_failure(char *reason, char *api_reason, int (*cb)(zend_extension *, zend_extension *))
2552 {
2553 	accel_startup_ok = 0;
2554 	zps_failure_reason = reason;
2555 	zps_api_failure_reason = api_reason?api_reason:reason;
2556 	zend_llist_del_element(&zend_extensions, NULL, (int (*)(void *, void *))cb);
2557 }
2558 
accel_find_sapi(void)2559 static inline int accel_find_sapi(void)
2560 {
2561 	static const char *supported_sapis[] = {
2562 		"apache",
2563 		"fastcgi",
2564 		"cli-server",
2565 		"cgi-fcgi",
2566 		"fpm-fcgi",
2567 		"fpmi-fcgi",
2568 		"apache2handler",
2569 		"litespeed",
2570 		"uwsgi",
2571 		NULL
2572 	};
2573 	const char **sapi_name;
2574 
2575 	if (sapi_module.name) {
2576 		for (sapi_name = supported_sapis; *sapi_name; sapi_name++) {
2577 			if (strcmp(sapi_module.name, *sapi_name) == 0) {
2578 				return SUCCESS;
2579 			}
2580 		}
2581 		if (ZCG(accel_directives).enable_cli && (
2582 		    strcmp(sapi_module.name, "cli") == 0
2583 		  || strcmp(sapi_module.name, "phpdbg") == 0)) {
2584 			return SUCCESS;
2585 		}
2586 	}
2587 
2588 	return FAILURE;
2589 }
2590 
zend_accel_init_shm(void)2591 static int zend_accel_init_shm(void)
2592 {
2593 	int i;
2594 
2595 	zend_shared_alloc_lock();
2596 
2597 	if (ZCG(accel_directives).interned_strings_buffer) {
2598 		accel_shared_globals = zend_shared_alloc((ZCG(accel_directives).interned_strings_buffer * 1024 * 1024));
2599 	} else {
2600 		accel_shared_globals = zend_shared_alloc(sizeof(zend_accel_shared_globals));
2601 	}
2602 	if (!accel_shared_globals) {
2603 		zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
2604 		zend_shared_alloc_unlock();
2605 		return FAILURE;
2606 	}
2607 	memset(accel_shared_globals, 0, sizeof(zend_accel_shared_globals));
2608 	ZSMMG(app_shared_globals) = accel_shared_globals;
2609 
2610 	zend_accel_hash_init(&ZCSG(hash), ZCG(accel_directives).max_accelerated_files);
2611 
2612 	if (ZCG(accel_directives).interned_strings_buffer) {
2613 		uint32_t hash_size;
2614 
2615 		/* must be a power of two */
2616 		hash_size = ZCG(accel_directives).interned_strings_buffer * (32 * 1024);
2617 		hash_size |= (hash_size >> 1);
2618 		hash_size |= (hash_size >> 2);
2619 		hash_size |= (hash_size >> 4);
2620 		hash_size |= (hash_size >> 8);
2621 		hash_size |= (hash_size >> 16);
2622 
2623 		ZCSG(interned_strings).nTableMask = hash_size << 2;
2624 		ZCSG(interned_strings).nNumOfElements = 0;
2625 		ZCSG(interned_strings).start =
2626 			(zend_string*)((char*)&ZCSG(interned_strings) +
2627 				sizeof(zend_string_table) +
2628 				((hash_size + 1) * sizeof(uint32_t))) +
2629 				8;
2630 		ZCSG(interned_strings).top =
2631 			ZCSG(interned_strings).start;
2632 		ZCSG(interned_strings).end =
2633 			(zend_string*)((char*)accel_shared_globals +
2634 				ZCG(accel_directives).interned_strings_buffer * 1024 * 1024);
2635 		ZCSG(interned_strings).saved_top = NULL;
2636 
2637 		memset((char*)&ZCSG(interned_strings) + sizeof(zend_string_table),
2638 			STRTAB_INVALID_POS,
2639 			(char*)ZCSG(interned_strings).start -
2640 				((char*)&ZCSG(interned_strings) + sizeof(zend_string_table)));
2641 
2642 		zend_interned_strings_set_permanent_storage_copy_handlers(accel_use_shm_interned_strings, accel_use_permanent_interned_strings);
2643 	}
2644 
2645 	zend_interned_strings_set_request_storage_handlers(accel_new_interned_string_for_php, accel_init_interned_string_for_php);
2646 
2647 	zend_reset_cache_vars();
2648 
2649 	ZCSG(oom_restarts) = 0;
2650 	ZCSG(hash_restarts) = 0;
2651 	ZCSG(manual_restarts) = 0;
2652 
2653 	ZCSG(accelerator_enabled) = 1;
2654 	ZCSG(start_time) = zend_accel_get_time();
2655 	ZCSG(last_restart_time) = 0;
2656 	ZCSG(restart_in_progress) = 0;
2657 
2658 
2659 	for (i = 0; i < -HT_MIN_MASK; i++) {
2660 		ZCSG(uninitialized_bucket)[i] = HT_INVALID_IDX;
2661 	}
2662 
2663 	zend_shared_alloc_unlock();
2664 
2665 	return SUCCESS;
2666 }
2667 
accel_globals_ctor(zend_accel_globals * accel_globals)2668 static void accel_globals_ctor(zend_accel_globals *accel_globals)
2669 {
2670 #if defined(COMPILE_DL_OPCACHE) && defined(ZTS)
2671 	ZEND_TSRMLS_CACHE_UPDATE();
2672 #endif
2673 	memset(accel_globals, 0, sizeof(zend_accel_globals));
2674 
2675 	/* TODO refactor to init this just once. */
2676 	accel_gen_system_id();
2677 }
2678 
accel_globals_dtor(zend_accel_globals * accel_globals)2679 static void accel_globals_dtor(zend_accel_globals *accel_globals)
2680 {
2681 	if (accel_globals->function_table.nTableSize) {
2682 		accel_globals->function_table.pDestructor = NULL;
2683 		zend_hash_destroy(&accel_globals->function_table);
2684 	}
2685 }
2686 
2687 #define ZEND_BIN_ID "BIN_" ZEND_TOSTR(SIZEOF_CHAR) ZEND_TOSTR(SIZEOF_INT) ZEND_TOSTR(SIZEOF_LONG) ZEND_TOSTR(SIZEOF_SIZE_T) ZEND_TOSTR(SIZEOF_ZEND_LONG) ZEND_TOSTR(ZEND_MM_ALIGNMENT)
2688 
accel_gen_system_id(void)2689 static void accel_gen_system_id(void)
2690 {
2691 	PHP_MD5_CTX context;
2692 	unsigned char digest[16], c;
2693 	char *md5str = ZCG(system_id);
2694 	int i;
2695 	zend_module_entry *module;
2696 	zend_extension *extension;
2697 	zend_llist_position pos;
2698 
2699 	PHP_MD5Init(&context);
2700 	PHP_MD5Update(&context, PHP_VERSION, sizeof(PHP_VERSION)-1);
2701 	PHP_MD5Update(&context, ZEND_EXTENSION_BUILD_ID, sizeof(ZEND_EXTENSION_BUILD_ID)-1);
2702 	PHP_MD5Update(&context, ZEND_BIN_ID, sizeof(ZEND_BIN_ID)-1);
2703 	if (strstr(PHP_VERSION, "-dev") != 0) {
2704 		/* Development versions may be changed from build to build */
2705 		PHP_MD5Update(&context, __DATE__, sizeof(__DATE__)-1);
2706 		PHP_MD5Update(&context, __TIME__, sizeof(__TIME__)-1);
2707 	}
2708 	/* Modules may have changed after restart which can cause dangling pointers from
2709      * custom opcode handlers in the second-level cache files
2710      */
2711 	ZEND_HASH_FOREACH_PTR(&module_registry, module) {
2712 		PHP_MD5Update(&context, module->name, strlen(module->name));
2713 		if (module->version != NULL) {
2714 			PHP_MD5Update(&context, module->version, strlen(module->version));
2715 		}
2716 	} ZEND_HASH_FOREACH_END();
2717 	extension = (zend_extension *) zend_llist_get_first_ex(&zend_extensions, &pos);
2718 	while (extension) {
2719 		PHP_MD5Update(&context, extension->name, strlen(extension->name));
2720 		if (extension->version != NULL) {
2721 			PHP_MD5Update(&context, extension->version, strlen(extension->version));
2722 		}
2723 		extension = (zend_extension *) zend_llist_get_next_ex(&zend_extensions, &pos);
2724 	}
2725 	PHP_MD5Final(digest, &context);
2726 	for (i = 0; i < 16; i++) {
2727 		c = digest[i] >> 4;
2728 		c = (c <= 9) ? c + '0' : c - 10 + 'a';
2729 		md5str[i * 2] = c;
2730 		c = digest[i] &  0x0f;
2731 		c = (c <= 9) ? c + '0' : c - 10 + 'a';
2732 		md5str[(i * 2) + 1] = c;
2733 	}
2734 }
2735 
2736 #ifdef HAVE_HUGE_CODE_PAGES
2737 # ifndef _WIN32
2738 #  include <sys/mman.h>
2739 #  ifndef MAP_ANON
2740 #   ifdef MAP_ANONYMOUS
2741 #    define MAP_ANON MAP_ANONYMOUS
2742 #   endif
2743 #  endif
2744 #  ifndef MAP_FAILED
2745 #   define MAP_FAILED ((void*)-1)
2746 #  endif
2747 # endif
2748 
2749 # if defined(MAP_HUGETLB) || defined(MADV_HUGEPAGE)
accel_remap_huge_pages(void * start,size_t size,const char * name,size_t offset)2750 static int accel_remap_huge_pages(void *start, size_t size, const char *name, size_t offset)
2751 {
2752 	void *ret = MAP_FAILED;
2753 	void *mem;
2754 
2755 	mem = mmap(NULL, size,
2756 		PROT_READ | PROT_WRITE,
2757 		MAP_PRIVATE | MAP_ANONYMOUS,
2758 		-1, 0);
2759 	if (mem == MAP_FAILED) {
2760 		zend_error(E_WARNING,
2761 			ACCELERATOR_PRODUCT_NAME " huge_code_pages: mmap failed: %s (%d)",
2762 			strerror(errno), errno);
2763 		return -1;
2764 	}
2765 	memcpy(mem, start, size);
2766 
2767 #  ifdef MAP_HUGETLB
2768 	ret = mmap(start, size,
2769 		PROT_READ | PROT_WRITE | PROT_EXEC,
2770 		MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_HUGETLB,
2771 		-1, 0);
2772 #  endif
2773 	if (ret == MAP_FAILED) {
2774 		ret = mmap(start, size,
2775 			PROT_READ | PROT_WRITE | PROT_EXEC,
2776 			MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
2777 			-1, 0);
2778 		/* this should never happen? */
2779 		ZEND_ASSERT(ret != MAP_FAILED);
2780 #  ifdef MADV_HUGEPAGE
2781 		if (-1 == madvise(start, size, MADV_HUGEPAGE)) {
2782 			memcpy(start, mem, size);
2783 			mprotect(start, size, PROT_READ | PROT_EXEC);
2784 			munmap(mem, size);
2785 			zend_error(E_WARNING,
2786 				ACCELERATOR_PRODUCT_NAME " huge_code_pages: madvise(HUGEPAGE) failed: %s (%d)",
2787 				strerror(errno), errno);
2788 			return -1;
2789 		}
2790 #  else
2791 		memcpy(start, mem, size);
2792 		mprotect(start, size, PROT_READ | PROT_EXEC);
2793 		munmap(mem, size);
2794 		zend_error(E_WARNING,
2795 			ACCELERATOR_PRODUCT_NAME " huge_code_pages: mmap(HUGETLB) failed: %s (%d)",
2796 			strerror(errno), errno);
2797 		return -1;
2798 #  endif
2799 	}
2800 
2801 	if (ret == start) {
2802 		memcpy(start, mem, size);
2803 		mprotect(start, size, PROT_READ | PROT_EXEC);
2804 	}
2805 	munmap(mem, size);
2806 
2807 	return (ret == start) ? 0 : -1;
2808 }
2809 
accel_move_code_to_huge_pages(void)2810 static void accel_move_code_to_huge_pages(void)
2811 {
2812 	FILE *f;
2813 	long unsigned int huge_page_size = 2 * 1024 * 1024;
2814 
2815 	f = fopen("/proc/self/maps", "r");
2816 	if (f) {
2817 		long unsigned int  start, end, offset, inode;
2818 		char perm[5], dev[6], name[MAXPATHLEN];
2819 		int ret;
2820 
2821 		ret = fscanf(f, "%lx-%lx %4s %lx %5s %ld %s\n", &start, &end, perm, &offset, dev, &inode, name);
2822 		if (ret == 7 && perm[0] == 'r' && perm[1] == '-' && perm[2] == 'x' && name[0] == '/') {
2823 			long unsigned int  seg_start = ZEND_MM_ALIGNED_SIZE_EX(start, huge_page_size);
2824 			long unsigned int  seg_end = (end & ~(huge_page_size-1L));
2825 
2826 			if (seg_end > seg_start) {
2827 				zend_accel_error(ACCEL_LOG_DEBUG, "remap to huge page %lx-%lx %s \n", seg_start, seg_end, name);
2828 				accel_remap_huge_pages((void*)seg_start, seg_end - seg_start, name, offset + seg_start - start);
2829 			}
2830 		}
2831 		fclose(f);
2832 	}
2833 }
2834 # else
accel_move_code_to_huge_pages(void)2835 static void accel_move_code_to_huge_pages(void)
2836 {
2837 	zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages has no affect as huge page is not supported");
2838 	return;
2839 }
2840 # endif /* defined(MAP_HUGETLB) || defined(MADV_HUGEPAGE) */
2841 #endif /* HAVE_HUGE_CODE_PAGES */
2842 
accel_startup(zend_extension * extension)2843 static int accel_startup(zend_extension *extension)
2844 {
2845 #ifdef ZTS
2846 	accel_globals_id = ts_allocate_id(&accel_globals_id, sizeof(zend_accel_globals), (ts_allocate_ctor) accel_globals_ctor, (ts_allocate_dtor) accel_globals_dtor);
2847 #else
2848 	accel_globals_ctor(&accel_globals);
2849 #endif
2850 
2851 #ifdef ZEND_WIN32
2852 # if !defined(__has_feature) || !__has_feature(address_sanitizer)
2853 	_setmaxstdio(2048); /* The default configuration is limited to 512 stdio files */
2854 # endif
2855 #endif
2856 
2857 	if (start_accel_module() == FAILURE) {
2858 		accel_startup_ok = 0;
2859 		zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": module registration failed!");
2860 		return FAILURE;
2861 	}
2862 
2863 	accel_gen_system_id();
2864 
2865 #ifdef HAVE_HUGE_CODE_PAGES
2866 	if (ZCG(accel_directives).huge_code_pages &&
2867 	    (strcmp(sapi_module.name, "cli") == 0 ||
2868 	     strcmp(sapi_module.name, "cli-server") == 0 ||
2869 		 strcmp(sapi_module.name, "cgi-fcgi") == 0 ||
2870 		 strcmp(sapi_module.name, "fpm-fcgi") == 0)) {
2871 		accel_move_code_to_huge_pages();
2872 	}
2873 #endif
2874 
2875 	/* no supported SAPI found - disable acceleration and stop initialization */
2876 	if (accel_find_sapi() == FAILURE) {
2877 		accel_startup_ok = 0;
2878 		if (!ZCG(accel_directives).enable_cli &&
2879 		    strcmp(sapi_module.name, "cli") == 0) {
2880 			zps_startup_failure("Opcode Caching is disabled for CLI", NULL, accelerator_remove_cb);
2881 		} else {
2882 			zps_startup_failure("Opcode Caching is only supported in Apache, FPM, FastCGI and LiteSpeed SAPIs", NULL, accelerator_remove_cb);
2883 		}
2884 		return SUCCESS;
2885 	}
2886 
2887 	if (ZCG(enabled) == 0) {
2888 		return SUCCESS ;
2889 	}
2890 
2891 	orig_post_startup_cb = zend_post_startup_cb;
2892 	zend_post_startup_cb = accel_post_startup;
2893 
2894 	return SUCCESS;
2895 }
2896 
accel_post_startup(void)2897 static int accel_post_startup(void)
2898 {
2899 	zend_function *func;
2900 	zend_ini_entry *ini_entry;
2901 
2902 	if (orig_post_startup_cb) {
2903 		int (*cb)(void) = orig_post_startup_cb;
2904 
2905 		orig_post_startup_cb = NULL;
2906 		if (cb() != SUCCESS) {
2907 			return FAILURE;
2908 		}
2909 	}
2910 
2911 /********************************************/
2912 /* End of non-SHM dependent initializations */
2913 /********************************************/
2914 #ifdef HAVE_OPCACHE_FILE_CACHE
2915 	file_cache_only = ZCG(accel_directives).file_cache_only;
2916 	if (!file_cache_only) {
2917 #else
2918 	if (1) {
2919 #endif
2920 		switch (zend_shared_alloc_startup(ZCG(accel_directives).memory_consumption)) {
2921 			case ALLOC_SUCCESS:
2922 				if (zend_accel_init_shm() == FAILURE) {
2923 					accel_startup_ok = 0;
2924 					return FAILURE;
2925 				}
2926 				break;
2927 			case ALLOC_FAILURE:
2928 				accel_startup_ok = 0;
2929 				zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - probably not enough shared memory.");
2930 				return SUCCESS;
2931 			case SUCCESSFULLY_REATTACHED:
2932 				zend_shared_alloc_lock();
2933 				accel_shared_globals = (zend_accel_shared_globals *) ZSMMG(app_shared_globals);
2934 				if (ZCG(accel_directives).interned_strings_buffer) {
2935 					zend_interned_strings_set_permanent_storage_copy_handlers(accel_use_shm_interned_strings, accel_use_permanent_interned_strings);
2936 				}
2937 				zend_interned_strings_set_request_storage_handlers(accel_new_interned_string_for_php, accel_init_interned_string_for_php);
2938 				zend_shared_alloc_unlock();
2939 				break;
2940 			case FAILED_REATTACHED:
2941 				accel_startup_ok = 0;
2942 				zend_accel_error(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - can not reattach to exiting shared memory.");
2943 				return SUCCESS;
2944 				break;
2945 #if ENABLE_FILE_CACHE_FALLBACK
2946 			case ALLOC_FALLBACK:
2947 				zend_shared_alloc_lock();
2948 				file_cache_only = 1;
2949 				fallback_process = 1;
2950 				zend_accel_init_auto_globals();
2951 				zend_shared_alloc_unlock();
2952 				goto file_cache_fallback;
2953 				break;
2954 #endif
2955 		}
2956 
2957 		/* from this point further, shared memory is supposed to be OK */
2958 
2959 		/* remember the last restart time in the process memory */
2960 		ZCG(last_restart_time) = ZCSG(last_restart_time);
2961 
2962 		/* Init auto-global strings */
2963 		zend_accel_init_auto_globals();
2964 
2965 		zend_shared_alloc_lock();
2966 		zend_shared_alloc_save_state();
2967 		zend_shared_alloc_unlock();
2968 
2969 		SHM_PROTECT();
2970 #ifdef HAVE_OPCACHE_FILE_CACHE
2971 	} else if (!ZCG(accel_directives).file_cache) {
2972 		accel_startup_ok = 0;
2973 		zend_accel_error(ACCEL_LOG_FATAL, "opcache.file_cache_only is set without a proper setting of opcache.file_cache");
2974 		return SUCCESS;
2975 	} else {
2976 		accel_shared_globals = calloc(1, sizeof(zend_accel_shared_globals));
2977 
2978 		/* Init auto-global strings */
2979 		zend_accel_init_auto_globals();
2980 #endif
2981 	}
2982 #if ENABLE_FILE_CACHE_FALLBACK
2983 file_cache_fallback:
2984 #endif
2985 
2986 	/* Override compiler */
2987 	accelerator_orig_compile_file = zend_compile_file;
2988 	zend_compile_file = persistent_compile_file;
2989 
2990 	/* Override stream opener function (to eliminate open() call caused by
2991 	 * include/require statements ) */
2992 	accelerator_orig_zend_stream_open_function = zend_stream_open_function;
2993 	zend_stream_open_function = persistent_stream_open_function;
2994 
2995 	/* Override path resolver function (to eliminate stat() calls caused by
2996 	 * include_once/require_once statements */
2997 	accelerator_orig_zend_resolve_path = zend_resolve_path;
2998 	zend_resolve_path = persistent_zend_resolve_path;
2999 
3000 	/* Override chdir() function */
3001 	if ((func = zend_hash_str_find_ptr(CG(function_table), "chdir", sizeof("chdir")-1)) != NULL &&
3002 	    func->type == ZEND_INTERNAL_FUNCTION) {
3003 		orig_chdir = func->internal_function.handler;
3004 		func->internal_function.handler = ZEND_FN(accel_chdir);
3005 	}
3006 	ZCG(cwd) = NULL;
3007 	ZCG(include_path) = NULL;
3008 
3009 	/* Override "include_path" modifier callback */
3010 	if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) {
3011 		ZCG(include_path) = ini_entry->value;
3012 		orig_include_path_on_modify = ini_entry->on_modify;
3013 		ini_entry->on_modify = accel_include_path_on_modify;
3014 	}
3015 
3016 	accel_startup_ok = 1;
3017 
3018 	/* Override file_exists(), is_file() and is_readable() */
3019 	zend_accel_override_file_functions();
3020 
3021 	/* Load black list */
3022 	accel_blacklist.entries = NULL;
3023 	if (ZCG(enabled) && accel_startup_ok &&
3024 	    ZCG(accel_directives).user_blacklist_filename &&
3025 	    *ZCG(accel_directives.user_blacklist_filename)) {
3026 		zend_accel_blacklist_init(&accel_blacklist);
3027 		zend_accel_blacklist_load(&accel_blacklist, ZCG(accel_directives.user_blacklist_filename));
3028 	}
3029 
3030 	zend_optimizer_startup();
3031 
3032 	return SUCCESS;
3033 }
3034 
3035 static void accel_free_ts_resources()
3036 {
3037 #ifndef ZTS
3038 	accel_globals_dtor(&accel_globals);
3039 #else
3040 	ts_free_id(accel_globals_id);
3041 #endif
3042 }
3043 
3044 void accel_shutdown(void)
3045 {
3046 	zend_ini_entry *ini_entry;
3047 	zend_bool _file_cache_only = 0;
3048 
3049 	zend_optimizer_shutdown();
3050 
3051 	zend_accel_blacklist_shutdown(&accel_blacklist);
3052 
3053 	if (!ZCG(enabled) || !accel_startup_ok) {
3054 		accel_free_ts_resources();
3055 		return;
3056 	}
3057 
3058 #ifdef HAVE_OPCACHE_FILE_CACHE
3059 	_file_cache_only = file_cache_only;
3060 #endif
3061 
3062 	accel_reset_pcre_cache();
3063 
3064 	accel_free_ts_resources();
3065 
3066 	if (!_file_cache_only) {
3067 		zend_shared_alloc_shutdown();
3068 	}
3069 	zend_compile_file = accelerator_orig_compile_file;
3070 
3071 	if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) {
3072 		ini_entry->on_modify = orig_include_path_on_modify;
3073 	}
3074 }
3075 
3076 void zend_accel_schedule_restart(zend_accel_restart_reason reason)
3077 {
3078 	const char *zend_accel_restart_reason_text[ACCEL_RESTART_USER + 1] = {
3079 		"out of memory",
3080 		"hash overflow",
3081 		"user",
3082 	};
3083 
3084 	if (ZCSG(restart_pending)) {
3085 		/* don't schedule twice */
3086 		return;
3087 	}
3088 	zend_accel_error(ACCEL_LOG_DEBUG, "Restart Scheduled! Reason: %s",
3089 			zend_accel_restart_reason_text[reason]);
3090 
3091 	HANDLE_BLOCK_INTERRUPTIONS();
3092 	SHM_UNPROTECT();
3093 	ZCSG(restart_pending) = 1;
3094 	ZCSG(restart_reason) = reason;
3095 	ZCSG(cache_status_before_restart) = ZCSG(accelerator_enabled);
3096 	ZCSG(accelerator_enabled) = 0;
3097 
3098 	if (ZCG(accel_directives).force_restart_timeout) {
3099 		ZCSG(force_restart_time) = zend_accel_get_time() + ZCG(accel_directives).force_restart_timeout;
3100 	} else {
3101 		ZCSG(force_restart_time) = 0;
3102 	}
3103 	SHM_PROTECT();
3104 	HANDLE_UNBLOCK_INTERRUPTIONS();
3105 }
3106 
3107 /* this is needed because on WIN32 lock is not decreased unless ZCG(counted) is set */
3108 #ifdef ZEND_WIN32
3109 #define accel_deactivate_now() ZCG(counted) = 1; accel_deactivate_sub()
3110 #else
3111 #define accel_deactivate_now() accel_deactivate_sub()
3112 #endif
3113 
3114 /* ensures it is OK to read SHM
3115 	if it's not OK (restart in progress) returns FAILURE
3116 	if OK returns SUCCESS
3117 	MUST call accelerator_shm_read_unlock after done lock operations
3118 */
3119 int accelerator_shm_read_lock(void)
3120 {
3121 	if (ZCG(counted)) {
3122 		/* counted means we are holding read lock for SHM, so that nothing bad can happen */
3123 		return SUCCESS;
3124 	} else {
3125 		/* here accelerator is active but we do not hold SHM lock. This means restart was scheduled
3126 			or is in progress now */
3127 		if (accel_activate_add() == FAILURE) { /* acquire usage lock */
3128 			return FAILURE;
3129 		}
3130 		/* Now if we weren't inside restart, restart would not begin until we remove usage lock */
3131 		if (ZCSG(restart_in_progress)) {
3132 			/* we already were inside restart this means it's not safe to touch shm */
3133 			accel_deactivate_now(); /* drop usage lock */
3134 			return FAILURE;
3135 		}
3136 		ZCG(counted) = 1;
3137 	}
3138 	return SUCCESS;
3139 }
3140 
3141 /* must be called ONLY after SUCCESSFUL accelerator_shm_read_lock */
3142 void accelerator_shm_read_unlock(void)
3143 {
3144 	if (!ZCG(counted)) {
3145 		/* counted is 0 - meaning we had to readlock manually, release readlock now */
3146 		accel_deactivate_now();
3147 	}
3148 }
3149 
3150 ZEND_EXT_API zend_extension zend_extension_entry = {
3151 	ACCELERATOR_PRODUCT_NAME,               /* name */
3152 	PHP_VERSION,							/* version */
3153 	"Zend Technologies",					/* author */
3154 	"http://www.zend.com/",					/* URL */
3155 	"Copyright (c) 1999-2018",				/* copyright */
3156 	accel_startup,					   		/* startup */
3157 	NULL,									/* shutdown */
3158 	accel_activate,							/* per-script activation */
3159 	accel_deactivate,						/* per-script deactivation */
3160 	NULL,									/* message handler */
3161 	NULL,									/* op_array handler */
3162 	NULL,									/* extended statement handler */
3163 	NULL,									/* extended fcall begin handler */
3164 	NULL,									/* extended fcall end handler */
3165 	NULL,									/* op_array ctor */
3166 	NULL,									/* op_array dtor */
3167 	STANDARD_ZEND_EXTENSION_PROPERTIES
3168 };
3169