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