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