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