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