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 | https://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: Dmitry Stogov <dmitry@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #include "zend.h"
20 #include "zend_virtual_cwd.h"
21 #include "zend_compile.h"
22 #include "zend_vm.h"
23 #include "zend_interfaces.h"
24 #include "zend_attributes.h"
25 #include "zend_system_id.h"
26 #include "zend_enum.h"
27
28 #include "php.h"
29 #ifdef ZEND_WIN32
30 #include "ext/standard/md5.h"
31 #endif
32 #include "ext/standard/php_filestat.h"
33
34 #include "ZendAccelerator.h"
35 #include "zend_file_cache.h"
36 #include "zend_shared_alloc.h"
37 #include "zend_accelerator_util_funcs.h"
38 #include "zend_accelerator_hash.h"
39
40 #ifdef HAVE_JIT
41 #include "jit/zend_jit.h"
42 #endif
43
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <fcntl.h>
47
48 #ifdef HAVE_UNISTD_H
49 #include <unistd.h>
50 #endif
51
52 #ifdef HAVE_SYS_UIO_H
53 # include <sys/uio.h>
54 #endif
55
56 #ifdef HAVE_SYS_FILE_H
57 # include <sys/file.h>
58 #endif
59
60 #if __has_feature(memory_sanitizer)
61 # include <sanitizer/msan_interface.h>
62 #endif
63
64 #ifndef ZEND_WIN32
65 #define zend_file_cache_unlink unlink
66 #define zend_file_cache_open open
67 #else
68 #define zend_file_cache_unlink php_win32_ioutil_unlink
69 #define zend_file_cache_open php_win32_ioutil_open
70 #endif
71
72 #ifdef ZEND_WIN32
73 # define LOCK_SH 0
74 # define LOCK_EX 1
75 # define LOCK_UN 2
zend_file_cache_flock(int fd,int op)76 static int zend_file_cache_flock(int fd, int op)
77 {
78 OVERLAPPED offset = {0,0,0,0,NULL};
79 if (op == LOCK_EX) {
80 if (LockFileEx((HANDLE)_get_osfhandle(fd),
81 LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &offset) == TRUE) {
82 return 0;
83 }
84 } else if (op == LOCK_SH) {
85 if (LockFileEx((HANDLE)_get_osfhandle(fd),
86 0, 0, 1, 0, &offset) == TRUE) {
87 return 0;
88 }
89 } else if (op == LOCK_UN) {
90 if (UnlockFileEx((HANDLE)_get_osfhandle(fd),
91 0, 1, 0, &offset) == TRUE) {
92 return 0;
93 }
94 }
95 return -1;
96 }
97 #elif defined(HAVE_FLOCK)
98 # define zend_file_cache_flock flock
99 #else
100 # define LOCK_SH 0
101 # define LOCK_EX 1
102 # define LOCK_UN 2
zend_file_cache_flock(int fd,int type)103 static int zend_file_cache_flock(int fd, int type)
104 {
105 return 0;
106 }
107 #endif
108
109 #ifndef O_BINARY
110 # define O_BINARY 0
111 #endif
112
113 #define SUFFIX ".bin"
114
115 #define IS_SERIALIZED_INTERNED(ptr) \
116 ((size_t)(ptr) & Z_UL(1))
117
118 /* Allowing == on the upper bound accounts for a potential empty allocation at the end of the
119 * memory region. This can also happen for a return-type-only arg_info, where &arg_info[1] is
120 * stored, which may point to the end of the region. */
121 #define IS_SERIALIZED(ptr) \
122 ((char*)(ptr) <= (char*)script->size)
123 #define IS_UNSERIALIZED(ptr) \
124 (((char*)(ptr) >= (char*)script->mem && (char*)(ptr) <= (char*)script->mem + script->size) || \
125 IS_ACCEL_INTERNED(ptr))
126 #define SERIALIZE_PTR(ptr) do { \
127 if (ptr) { \
128 ZEND_ASSERT(IS_UNSERIALIZED(ptr)); \
129 (ptr) = (void*)((char*)(ptr) - (char*)script->mem); \
130 } \
131 } while (0)
132 #define UNSERIALIZE_PTR(ptr) do { \
133 if (ptr) { \
134 ZEND_ASSERT(IS_SERIALIZED(ptr)); \
135 (ptr) = (void*)((char*)buf + (size_t)(ptr)); \
136 } \
137 } while (0)
138 #define SERIALIZE_STR(ptr) do { \
139 if (ptr) { \
140 if (IS_ACCEL_INTERNED(ptr)) { \
141 (ptr) = zend_file_cache_serialize_interned((zend_string*)(ptr), info); \
142 } else { \
143 ZEND_ASSERT(IS_UNSERIALIZED(ptr)); \
144 /* script->corrupted shows if the script in SHM or not */ \
145 if (EXPECTED(script->corrupted)) { \
146 GC_ADD_FLAGS(ptr, IS_STR_INTERNED); \
147 GC_DEL_FLAGS(ptr, IS_STR_PERMANENT); \
148 } \
149 (ptr) = (void*)((char*)(ptr) - (char*)script->mem); \
150 } \
151 } \
152 } while (0)
153 #define UNSERIALIZE_STR(ptr) do { \
154 if (ptr) { \
155 if (IS_SERIALIZED_INTERNED(ptr)) { \
156 (ptr) = (void*)zend_file_cache_unserialize_interned((zend_string*)(ptr), !script->corrupted); \
157 } else { \
158 ZEND_ASSERT(IS_SERIALIZED(ptr)); \
159 (ptr) = (void*)((char*)buf + (size_t)(ptr)); \
160 /* script->corrupted shows if the script in SHM or not */ \
161 if (EXPECTED(!script->corrupted)) { \
162 GC_ADD_FLAGS(ptr, IS_STR_INTERNED | IS_STR_PERMANENT); \
163 } else { \
164 GC_ADD_FLAGS(ptr, IS_STR_INTERNED); \
165 GC_DEL_FLAGS(ptr, IS_STR_PERMANENT); \
166 } \
167 } \
168 } \
169 } while (0)
170
171 #define SERIALIZE_ATTRIBUTES(attributes) do { \
172 if ((attributes) && !IS_SERIALIZED(attributes)) { \
173 HashTable *ht; \
174 SERIALIZE_PTR(attributes); \
175 ht = (attributes); \
176 UNSERIALIZE_PTR(ht); \
177 zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_attribute); \
178 } \
179 } while (0)
180
181 #define UNSERIALIZE_ATTRIBUTES(attributes) do { \
182 if ((attributes) && !IS_UNSERIALIZED(attributes)) { \
183 HashTable *ht; \
184 UNSERIALIZE_PTR(attributes); \
185 ht = (attributes); \
186 zend_file_cache_unserialize_hash(ht, script, buf, zend_file_cache_unserialize_attribute, NULL); \
187 } \
188 } while (0)
189
190 #define HOOKED_ITERATOR_PLACEHOLDER ((void*)1)
191
192 static const uint32_t uninitialized_bucket[-HT_MIN_MASK] =
193 {HT_INVALID_IDX, HT_INVALID_IDX};
194
195 typedef struct _zend_file_cache_metainfo {
196 char magic[8];
197 char system_id[32];
198 size_t mem_size;
199 size_t str_size;
200 size_t script_offset;
201 accel_time_t timestamp;
202 uint32_t checksum;
203 } zend_file_cache_metainfo;
204
zend_file_cache_mkdir(char * filename,size_t start)205 static int zend_file_cache_mkdir(char *filename, size_t start)
206 {
207 char *s = filename + start;
208
209 while (*s) {
210 if (IS_SLASH(*s)) {
211 char old = *s;
212 *s = '\000';
213 #ifndef ZEND_WIN32
214 if (mkdir(filename, S_IRWXU) < 0 && errno != EEXIST) {
215 #else
216 if (php_win32_ioutil_mkdir(filename, 0700) < 0 && errno != EEXIST) {
217 #endif
218 *s = old;
219 return FAILURE;
220 }
221 *s = old;
222 }
223 s++;
224 }
225 return SUCCESS;
226 }
227
228 typedef void (*serialize_callback_t)(zval *zv,
229 zend_persistent_script *script,
230 zend_file_cache_metainfo *info,
231 void *buf);
232
233 typedef void (*unserialize_callback_t)(zval *zv,
234 zend_persistent_script *script,
235 void *buf);
236
237 static void zend_file_cache_serialize_zval(zval *zv,
238 zend_persistent_script *script,
239 zend_file_cache_metainfo *info,
240 void *buf);
241 static void zend_file_cache_unserialize_zval(zval *zv,
242 zend_persistent_script *script,
243 void *buf);
244
245 static void *zend_file_cache_serialize_interned(zend_string *str,
246 zend_file_cache_metainfo *info)
247 {
248 size_t len;
249 void *ret;
250
251 /* check if the same interned string was already stored */
252 ret = zend_shared_alloc_get_xlat_entry(str);
253 if (ret) {
254 return ret;
255 }
256
257 len = ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(ZSTR_LEN(str)));
258 ret = (void*)(info->str_size | Z_UL(1));
259 zend_shared_alloc_register_xlat_entry(str, ret);
260
261 zend_string *s = (zend_string*)ZCG(mem);
262 if (info->str_size + len > ZSTR_LEN(s)) {
263 size_t new_len = info->str_size + len;
264 s = zend_string_realloc(
265 s,
266 ((_ZSTR_HEADER_SIZE + 1 + new_len + 4095) & ~0xfff) - (_ZSTR_HEADER_SIZE + 1),
267 0);
268 ZCG(mem) = (void*)s;
269 }
270
271 zend_string *new_str = (zend_string *) (ZSTR_VAL(s) + info->str_size);
272 memcpy(new_str, str, len);
273 GC_ADD_FLAGS(new_str, IS_STR_INTERNED);
274 GC_DEL_FLAGS(new_str, IS_STR_PERMANENT|IS_STR_CLASS_NAME_MAP_PTR);
275 info->str_size += len;
276 return ret;
277 }
278
279 static void *zend_file_cache_unserialize_interned(zend_string *str, bool in_shm)
280 {
281 str = (zend_string*)((char*)ZCG(mem) + ((size_t)(str) & ~Z_UL(1)));
282 if (!in_shm) {
283 return str;
284 }
285
286 zend_string *ret = accel_new_interned_string(str);
287 if (ret == str) {
288 /* We have to create new SHM allocated string */
289 size_t size = _ZSTR_STRUCT_SIZE(ZSTR_LEN(str));
290 ret = zend_shared_alloc(size);
291 if (!ret) {
292 zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
293 LONGJMP(*EG(bailout), FAILURE);
294 }
295 memcpy(ret, str, size);
296 /* String wasn't interned but we will use it as interned anyway */
297 GC_SET_REFCOUNT(ret, 1);
298 GC_TYPE_INFO(ret) = GC_STRING | ((IS_STR_INTERNED | IS_STR_PERSISTENT | IS_STR_PERMANENT) << GC_FLAGS_SHIFT);
299 }
300 return ret;
301 }
302
303 static void zend_file_cache_serialize_hash(HashTable *ht,
304 zend_persistent_script *script,
305 zend_file_cache_metainfo *info,
306 void *buf,
307 serialize_callback_t func)
308 {
309 if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) {
310 ht->arData = NULL;
311 return;
312 }
313 if (IS_SERIALIZED(ht->arData)) {
314 return;
315 }
316 if (HT_IS_PACKED(ht)) {
317 zval *p, *end;
318
319 SERIALIZE_PTR(ht->arPacked);
320 p = ht->arPacked;
321 UNSERIALIZE_PTR(p);
322 end = p + ht->nNumUsed;
323 while (p < end) {
324 if (Z_TYPE_P(p) != IS_UNDEF) {
325 func(p, script, info, buf);
326 }
327 p++;
328 }
329 } else {
330 Bucket *p, *end;
331
332 SERIALIZE_PTR(ht->arData);
333 p = ht->arData;
334 UNSERIALIZE_PTR(p);
335 end = p + ht->nNumUsed;
336 while (p < end) {
337 if (Z_TYPE(p->val) != IS_UNDEF) {
338 SERIALIZE_STR(p->key);
339 func(&p->val, script, info, buf);
340 }
341 p++;
342 }
343 }
344 }
345
346 static void zend_file_cache_serialize_ast(zend_ast *ast,
347 zend_persistent_script *script,
348 zend_file_cache_metainfo *info,
349 void *buf)
350 {
351 uint32_t i;
352 zend_ast *tmp;
353
354 if (ast->kind == ZEND_AST_ZVAL || ast->kind == ZEND_AST_CONSTANT) {
355 zend_file_cache_serialize_zval(&((zend_ast_zval*)ast)->val, script, info, buf);
356 } else if (zend_ast_is_list(ast)) {
357 zend_ast_list *list = zend_ast_get_list(ast);
358 for (i = 0; i < list->children; i++) {
359 if (list->child[i] && !IS_SERIALIZED(list->child[i])) {
360 SERIALIZE_PTR(list->child[i]);
361 tmp = list->child[i];
362 UNSERIALIZE_PTR(tmp);
363 zend_file_cache_serialize_ast(tmp, script, info, buf);
364 }
365 }
366 } else {
367 uint32_t children = zend_ast_get_num_children(ast);
368 for (i = 0; i < children; i++) {
369 if (ast->child[i] && !IS_SERIALIZED(ast->child[i])) {
370 SERIALIZE_PTR(ast->child[i]);
371 tmp = ast->child[i];
372 UNSERIALIZE_PTR(tmp);
373 zend_file_cache_serialize_ast(tmp, script, info, buf);
374 }
375 }
376 }
377 }
378
379 static void zend_file_cache_serialize_zval(zval *zv,
380 zend_persistent_script *script,
381 zend_file_cache_metainfo *info,
382 void *buf)
383 {
384 switch (Z_TYPE_P(zv)) {
385 case IS_STRING:
386 if (!IS_SERIALIZED(Z_STR_P(zv))) {
387 SERIALIZE_STR(Z_STR_P(zv));
388 }
389 break;
390 case IS_ARRAY:
391 if (!IS_SERIALIZED(Z_ARR_P(zv))) {
392 HashTable *ht;
393
394 SERIALIZE_PTR(Z_ARR_P(zv));
395 ht = Z_ARR_P(zv);
396 UNSERIALIZE_PTR(ht);
397 zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval);
398 }
399 break;
400 case IS_CONSTANT_AST:
401 if (!IS_SERIALIZED(Z_AST_P(zv))) {
402 zend_ast_ref *ast;
403
404 SERIALIZE_PTR(Z_AST_P(zv));
405 ast = Z_AST_P(zv);
406 UNSERIALIZE_PTR(ast);
407 zend_file_cache_serialize_ast(GC_AST(ast), script, info, buf);
408 }
409 break;
410 case IS_INDIRECT:
411 /* Used by static properties. */
412 SERIALIZE_PTR(Z_INDIRECT_P(zv));
413 break;
414 default:
415 ZEND_ASSERT(Z_TYPE_P(zv) < IS_STRING);
416 break;
417 }
418 }
419
420 static void zend_file_cache_serialize_attribute(zval *zv,
421 zend_persistent_script *script,
422 zend_file_cache_metainfo *info,
423 void *buf)
424 {
425 zend_attribute *attr = Z_PTR_P(zv);
426 uint32_t i;
427
428 SERIALIZE_PTR(Z_PTR_P(zv));
429 attr = Z_PTR_P(zv);
430 UNSERIALIZE_PTR(attr);
431
432 SERIALIZE_STR(attr->name);
433 SERIALIZE_STR(attr->lcname);
434
435 for (i = 0; i < attr->argc; i++) {
436 SERIALIZE_STR(attr->args[i].name);
437 zend_file_cache_serialize_zval(&attr->args[i].value, script, info, buf);
438 }
439 }
440
441 static void zend_file_cache_serialize_type(
442 zend_type *type, zend_persistent_script *script, zend_file_cache_metainfo *info, void *buf)
443 {
444 if (ZEND_TYPE_HAS_LIST(*type)) {
445 zend_type_list *list = ZEND_TYPE_LIST(*type);
446 SERIALIZE_PTR(list);
447 ZEND_TYPE_SET_PTR(*type, list);
448 UNSERIALIZE_PTR(list);
449
450 zend_type *list_type;
451 ZEND_TYPE_LIST_FOREACH(list, list_type) {
452 zend_file_cache_serialize_type(list_type, script, info, buf);
453 } ZEND_TYPE_LIST_FOREACH_END();
454 } else if (ZEND_TYPE_HAS_NAME(*type)) {
455 zend_string *type_name = ZEND_TYPE_NAME(*type);
456 SERIALIZE_STR(type_name);
457 ZEND_TYPE_SET_PTR(*type, type_name);
458 }
459 }
460
461 static void zend_file_cache_serialize_op_array(zend_op_array *op_array,
462 zend_persistent_script *script,
463 zend_file_cache_metainfo *info,
464 void *buf)
465 {
466 ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, NULL);
467 ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL);
468
469 /* Check whether this op_array has already been serialized. */
470 if (IS_SERIALIZED(op_array->opcodes)) {
471 ZEND_ASSERT(op_array->scope && "Only method op_arrays should be shared");
472 return;
473 }
474
475 if (op_array->scope) {
476 if (UNEXPECTED(zend_shared_alloc_get_xlat_entry(op_array->opcodes))) {
477 op_array->refcount = (uint32_t*)(intptr_t)-1;
478 SERIALIZE_PTR(op_array->static_variables);
479 SERIALIZE_PTR(op_array->literals);
480 SERIALIZE_PTR(op_array->opcodes);
481 SERIALIZE_PTR(op_array->arg_info);
482 SERIALIZE_PTR(op_array->vars);
483 SERIALIZE_STR(op_array->function_name);
484 SERIALIZE_STR(op_array->filename);
485 SERIALIZE_PTR(op_array->live_range);
486 SERIALIZE_PTR(op_array->scope);
487 SERIALIZE_STR(op_array->doc_comment);
488 SERIALIZE_ATTRIBUTES(op_array->attributes);
489 SERIALIZE_PTR(op_array->try_catch_array);
490 SERIALIZE_PTR(op_array->prototype);
491 SERIALIZE_PTR(op_array->prop_info);
492 return;
493 }
494 zend_shared_alloc_register_xlat_entry(op_array->opcodes, op_array->opcodes);
495 }
496
497 if (op_array->static_variables) {
498 HashTable *ht;
499
500 SERIALIZE_PTR(op_array->static_variables);
501 ht = op_array->static_variables;
502 UNSERIALIZE_PTR(ht);
503 zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval);
504 }
505
506 if (op_array->literals) {
507 zval *p, *end;
508
509 SERIALIZE_PTR(op_array->literals);
510 p = op_array->literals;
511 UNSERIALIZE_PTR(p);
512 end = p + op_array->last_literal;
513 while (p < end) {
514 zend_file_cache_serialize_zval(p, script, info, buf);
515 p++;
516 }
517 }
518
519 {
520 zend_op *opline, *end;
521
522 #if !ZEND_USE_ABS_CONST_ADDR
523 zval *literals = op_array->literals;
524 UNSERIALIZE_PTR(literals);
525 #endif
526
527 SERIALIZE_PTR(op_array->opcodes);
528 opline = op_array->opcodes;
529 UNSERIALIZE_PTR(opline);
530 end = opline + op_array->last;
531 while (opline < end) {
532 #if ZEND_USE_ABS_CONST_ADDR
533 if (opline->op1_type == IS_CONST) {
534 SERIALIZE_PTR(opline->op1.zv);
535 }
536 if (opline->op2_type == IS_CONST) {
537 SERIALIZE_PTR(opline->op2.zv);
538 }
539 #else
540 if (opline->op1_type == IS_CONST) {
541 opline->op1.constant = RT_CONSTANT(opline, opline->op1) - literals;
542 }
543 if (opline->op2_type == IS_CONST) {
544 opline->op2.constant = RT_CONSTANT(opline, opline->op2) - literals;
545 }
546 #endif
547 #if ZEND_USE_ABS_JMP_ADDR
548 switch (opline->opcode) {
549 case ZEND_JMP:
550 case ZEND_FAST_CALL:
551 SERIALIZE_PTR(opline->op1.jmp_addr);
552 break;
553 case ZEND_JMPZ:
554 case ZEND_JMPNZ:
555 case ZEND_JMPZ_EX:
556 case ZEND_JMPNZ_EX:
557 case ZEND_JMP_SET:
558 case ZEND_COALESCE:
559 case ZEND_FE_RESET_R:
560 case ZEND_FE_RESET_RW:
561 case ZEND_ASSERT_CHECK:
562 case ZEND_JMP_NULL:
563 case ZEND_BIND_INIT_STATIC_OR_JMP:
564 case ZEND_JMP_FRAMELESS:
565 SERIALIZE_PTR(opline->op2.jmp_addr);
566 break;
567 case ZEND_CATCH:
568 if (!(opline->extended_value & ZEND_LAST_CATCH)) {
569 SERIALIZE_PTR(opline->op2.jmp_addr);
570 }
571 break;
572 case ZEND_FE_FETCH_R:
573 case ZEND_FE_FETCH_RW:
574 case ZEND_SWITCH_LONG:
575 case ZEND_SWITCH_STRING:
576 case ZEND_MATCH:
577 /* relative extended_value don't have to be changed */
578 break;
579 }
580 #endif
581 zend_serialize_opcode_handler(opline);
582 opline++;
583 }
584
585 if (op_array->arg_info) {
586 zend_arg_info *p, *end;
587 SERIALIZE_PTR(op_array->arg_info);
588 p = op_array->arg_info;
589 UNSERIALIZE_PTR(p);
590 end = p + op_array->num_args;
591 if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
592 p--;
593 }
594 if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
595 end++;
596 }
597 while (p < end) {
598 if (!IS_SERIALIZED(p->name)) {
599 SERIALIZE_STR(p->name);
600 }
601 zend_file_cache_serialize_type(&p->type, script, info, buf);
602 p++;
603 }
604 }
605
606 if (op_array->vars) {
607 zend_string **p, **end;
608
609 SERIALIZE_PTR(op_array->vars);
610 p = op_array->vars;
611 UNSERIALIZE_PTR(p);
612 end = p + op_array->last_var;
613 while (p < end) {
614 if (!IS_SERIALIZED(*p)) {
615 SERIALIZE_STR(*p);
616 }
617 p++;
618 }
619 }
620
621 if (op_array->num_dynamic_func_defs) {
622 zend_op_array **defs;
623 SERIALIZE_PTR(op_array->dynamic_func_defs);
624 defs = op_array->dynamic_func_defs;
625 UNSERIALIZE_PTR(defs);
626 for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) {
627 zend_op_array *def;
628 SERIALIZE_PTR(defs[i]);
629 def = defs[i];
630 UNSERIALIZE_PTR(def);
631 zend_file_cache_serialize_op_array(def, script, info, buf);
632 }
633 }
634
635 SERIALIZE_STR(op_array->function_name);
636 SERIALIZE_STR(op_array->filename);
637 SERIALIZE_PTR(op_array->live_range);
638 SERIALIZE_PTR(op_array->scope);
639 SERIALIZE_STR(op_array->doc_comment);
640 SERIALIZE_ATTRIBUTES(op_array->attributes);
641 SERIALIZE_PTR(op_array->try_catch_array);
642 SERIALIZE_PTR(op_array->prototype);
643 SERIALIZE_PTR(op_array->prop_info);
644 }
645 }
646
647 static void zend_file_cache_serialize_func(zval *zv,
648 zend_persistent_script *script,
649 zend_file_cache_metainfo *info,
650 void *buf)
651 {
652 zend_function *func;
653 SERIALIZE_PTR(Z_PTR_P(zv));
654 func = Z_PTR_P(zv);
655 UNSERIALIZE_PTR(func);
656 ZEND_ASSERT(func->type == ZEND_USER_FUNCTION);
657 zend_file_cache_serialize_op_array(&func->op_array, script, info, buf);
658 }
659
660 static void zend_file_cache_serialize_prop_info(zval *zv,
661 zend_persistent_script *script,
662 zend_file_cache_metainfo *info,
663 void *buf)
664 {
665 if (!IS_SERIALIZED(Z_PTR_P(zv))) {
666 zend_property_info *prop;
667
668 SERIALIZE_PTR(Z_PTR_P(zv));
669 prop = Z_PTR_P(zv);
670 UNSERIALIZE_PTR(prop);
671
672 ZEND_ASSERT(prop->ce != NULL && prop->name != NULL);
673 if (!IS_SERIALIZED(prop->ce)) {
674 SERIALIZE_PTR(prop->ce);
675 SERIALIZE_STR(prop->name);
676 if (prop->doc_comment) {
677 SERIALIZE_STR(prop->doc_comment);
678 }
679 SERIALIZE_ATTRIBUTES(prop->attributes);
680 SERIALIZE_PTR(prop->prototype);
681 if (prop->hooks) {
682 SERIALIZE_PTR(prop->hooks);
683 zend_function **hooks = prop->hooks;
684 UNSERIALIZE_PTR(hooks);
685 for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) {
686 if (hooks[i]) {
687 SERIALIZE_PTR(hooks[i]);
688 zend_function *hook = hooks[i];
689 UNSERIALIZE_PTR(hook);
690 zend_file_cache_serialize_op_array(&hook->op_array, script, info, buf);
691 }
692 }
693 }
694 zend_file_cache_serialize_type(&prop->type, script, info, buf);
695 }
696 }
697 }
698
699 static void zend_file_cache_serialize_class_constant(zval *zv,
700 zend_persistent_script *script,
701 zend_file_cache_metainfo *info,
702 void *buf)
703 {
704 if (!IS_SERIALIZED(Z_PTR_P(zv))) {
705 zend_class_constant *c;
706
707 SERIALIZE_PTR(Z_PTR_P(zv));
708 c = Z_PTR_P(zv);
709 UNSERIALIZE_PTR(c);
710
711 ZEND_ASSERT(c->ce != NULL);
712 if (!IS_SERIALIZED(c->ce)) {
713 SERIALIZE_PTR(c->ce);
714
715 zend_file_cache_serialize_zval(&c->value, script, info, buf);
716 if (c->doc_comment) {
717 SERIALIZE_STR(c->doc_comment);
718 }
719
720 SERIALIZE_ATTRIBUTES(c->attributes);
721 zend_file_cache_serialize_type(&c->type, script, info, buf);
722 }
723 }
724 }
725
726 static void zend_file_cache_serialize_class(zval *zv,
727 zend_persistent_script *script,
728 zend_file_cache_metainfo *info,
729 void *buf)
730 {
731 zend_class_entry *ce;
732
733 SERIALIZE_PTR(Z_PTR_P(zv));
734 ce = Z_PTR_P(zv);
735 UNSERIALIZE_PTR(ce);
736
737 SERIALIZE_STR(ce->name);
738 if (ce->parent) {
739 if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
740 SERIALIZE_STR(ce->parent_name);
741 } else {
742 SERIALIZE_PTR(ce->parent);
743 }
744 }
745 zend_file_cache_serialize_hash(&ce->function_table, script, info, buf, zend_file_cache_serialize_func);
746 if (ce->default_properties_table) {
747 zval *p, *end;
748
749 SERIALIZE_PTR(ce->default_properties_table);
750 p = ce->default_properties_table;
751 UNSERIALIZE_PTR(p);
752 end = p + ce->default_properties_count;
753 while (p < end) {
754 zend_file_cache_serialize_zval(p, script, info, buf);
755 p++;
756 }
757 }
758 if (ce->default_static_members_table) {
759 zval *p, *end;
760
761 SERIALIZE_PTR(ce->default_static_members_table);
762 p = ce->default_static_members_table;
763 UNSERIALIZE_PTR(p);
764
765 end = p + ce->default_static_members_count;
766 while (p < end) {
767 zend_file_cache_serialize_zval(p, script, info, buf);
768 p++;
769 }
770 }
771 zend_file_cache_serialize_hash(&ce->constants_table, script, info, buf, zend_file_cache_serialize_class_constant);
772 SERIALIZE_STR(ce->info.user.filename);
773 SERIALIZE_STR(ce->doc_comment);
774 SERIALIZE_ATTRIBUTES(ce->attributes);
775 zend_file_cache_serialize_hash(&ce->properties_info, script, info, buf, zend_file_cache_serialize_prop_info);
776
777 if (ce->properties_info_table) {
778 uint32_t i;
779 zend_property_info **table;
780
781 SERIALIZE_PTR(ce->properties_info_table);
782 table = ce->properties_info_table;
783 UNSERIALIZE_PTR(table);
784
785 for (i = 0; i < ce->default_properties_count; i++) {
786 SERIALIZE_PTR(table[i]);
787 }
788 }
789
790 if (ce->num_interfaces) {
791 uint32_t i;
792 zend_class_name *interface_names;
793
794 ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_LINKED));
795
796 SERIALIZE_PTR(ce->interface_names);
797 interface_names = ce->interface_names;
798 UNSERIALIZE_PTR(interface_names);
799
800 for (i = 0; i < ce->num_interfaces; i++) {
801 SERIALIZE_STR(interface_names[i].name);
802 SERIALIZE_STR(interface_names[i].lc_name);
803 }
804 }
805
806 if (ce->num_traits) {
807 uint32_t i;
808 zend_class_name *trait_names;
809
810 SERIALIZE_PTR(ce->trait_names);
811 trait_names = ce->trait_names;
812 UNSERIALIZE_PTR(trait_names);
813
814 for (i = 0; i < ce->num_traits; i++) {
815 SERIALIZE_STR(trait_names[i].name);
816 SERIALIZE_STR(trait_names[i].lc_name);
817 }
818
819 if (ce->trait_aliases) {
820 zend_trait_alias **p, *q;
821
822 SERIALIZE_PTR(ce->trait_aliases);
823 p = ce->trait_aliases;
824 UNSERIALIZE_PTR(p);
825
826 while (*p) {
827 SERIALIZE_PTR(*p);
828 q = *p;
829 UNSERIALIZE_PTR(q);
830
831 if (q->trait_method.method_name) {
832 SERIALIZE_STR(q->trait_method.method_name);
833 }
834 if (q->trait_method.class_name) {
835 SERIALIZE_STR(q->trait_method.class_name);
836 }
837
838 if (q->alias) {
839 SERIALIZE_STR(q->alias);
840 }
841 p++;
842 }
843 }
844
845 if (ce->trait_precedences) {
846 zend_trait_precedence **p, *q;
847 uint32_t j;
848
849 SERIALIZE_PTR(ce->trait_precedences);
850 p = ce->trait_precedences;
851 UNSERIALIZE_PTR(p);
852
853 while (*p) {
854 SERIALIZE_PTR(*p);
855 q = *p;
856 UNSERIALIZE_PTR(q);
857
858 if (q->trait_method.method_name) {
859 SERIALIZE_STR(q->trait_method.method_name);
860 }
861 if (q->trait_method.class_name) {
862 SERIALIZE_STR(q->trait_method.class_name);
863 }
864
865 for (j = 0; j < q->num_excludes; j++) {
866 SERIALIZE_STR(q->exclude_class_names[j]);
867 }
868 p++;
869 }
870 }
871 }
872
873 SERIALIZE_PTR(ce->constructor);
874 SERIALIZE_PTR(ce->destructor);
875 SERIALIZE_PTR(ce->clone);
876 SERIALIZE_PTR(ce->__get);
877 SERIALIZE_PTR(ce->__set);
878 SERIALIZE_PTR(ce->__call);
879 SERIALIZE_PTR(ce->__serialize);
880 SERIALIZE_PTR(ce->__unserialize);
881 SERIALIZE_PTR(ce->__isset);
882 SERIALIZE_PTR(ce->__unset);
883 SERIALIZE_PTR(ce->__tostring);
884 SERIALIZE_PTR(ce->__callstatic);
885 SERIALIZE_PTR(ce->__debugInfo);
886
887 if (ce->iterator_funcs_ptr) {
888 SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_new_iterator);
889 SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_rewind);
890 SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_valid);
891 SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_key);
892 SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_current);
893 SERIALIZE_PTR(ce->iterator_funcs_ptr->zf_next);
894 SERIALIZE_PTR(ce->iterator_funcs_ptr);
895 }
896
897 if (ce->arrayaccess_funcs_ptr) {
898 SERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetget);
899 SERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetexists);
900 SERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetset);
901 SERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetunset);
902 SERIALIZE_PTR(ce->arrayaccess_funcs_ptr);
903 }
904
905 ZEND_MAP_PTR_INIT(ce->static_members_table, NULL);
906 ZEND_MAP_PTR_INIT(ce->mutable_data, NULL);
907
908 ce->inheritance_cache = NULL;
909
910 if (ce->get_iterator) {
911 ZEND_ASSERT(ce->get_iterator == zend_hooked_object_get_iterator);
912 ce->get_iterator = HOOKED_ITERATOR_PLACEHOLDER;
913 }
914 }
915
916 static void zend_file_cache_serialize_warnings(
917 zend_persistent_script *script, zend_file_cache_metainfo *info, void *buf)
918 {
919 if (script->warnings) {
920 zend_error_info **warnings;
921 SERIALIZE_PTR(script->warnings);
922 warnings = script->warnings;
923 UNSERIALIZE_PTR(warnings);
924
925 for (uint32_t i = 0; i < script->num_warnings; i++) {
926 zend_error_info *warning;
927 SERIALIZE_PTR(warnings[i]);
928 warning = warnings[i];
929 UNSERIALIZE_PTR(warning);
930 SERIALIZE_STR(warning->filename);
931 SERIALIZE_STR(warning->message);
932 }
933 }
934 }
935
936 static void zend_file_cache_serialize_early_bindings(
937 zend_persistent_script *script, zend_file_cache_metainfo *info, void *buf)
938 {
939 if (script->early_bindings) {
940 SERIALIZE_PTR(script->early_bindings);
941 zend_early_binding *early_bindings = script->early_bindings;
942 UNSERIALIZE_PTR(early_bindings);
943 for (uint32_t i = 0; i < script->num_early_bindings; i++) {
944 SERIALIZE_STR(early_bindings[i].lcname);
945 SERIALIZE_STR(early_bindings[i].rtd_key);
946 SERIALIZE_STR(early_bindings[i].lc_parent_name);
947 }
948 }
949 }
950
951 static void zend_file_cache_serialize(zend_persistent_script *script,
952 zend_file_cache_metainfo *info,
953 void *buf)
954 {
955 zend_persistent_script *new_script;
956
957 memcpy(info->magic, "OPCACHE", 8);
958 memcpy(info->system_id, zend_system_id, 32);
959 info->mem_size = script->size;
960 info->str_size = 0;
961 info->script_offset = (char*)script - (char*)script->mem;
962 info->timestamp = script->timestamp;
963
964 memcpy(buf, script->mem, script->size);
965
966 new_script = (zend_persistent_script*)((char*)buf + info->script_offset);
967 SERIALIZE_STR(new_script->script.filename);
968
969 zend_file_cache_serialize_hash(&new_script->script.class_table, script, info, buf, zend_file_cache_serialize_class);
970 zend_file_cache_serialize_hash(&new_script->script.function_table, script, info, buf, zend_file_cache_serialize_func);
971 zend_file_cache_serialize_op_array(&new_script->script.main_op_array, script, info, buf);
972 zend_file_cache_serialize_warnings(new_script, info, buf);
973 zend_file_cache_serialize_early_bindings(new_script, info, buf);
974
975 new_script->mem = NULL;
976 }
977
978 static char *zend_file_cache_get_bin_file_path(zend_string *script_path)
979 {
980 size_t len;
981 char *filename;
982
983 #ifndef ZEND_WIN32
984 len = strlen(ZCG(accel_directives).file_cache);
985 filename = emalloc(len + 33 + ZSTR_LEN(script_path) + sizeof(SUFFIX));
986 memcpy(filename, ZCG(accel_directives).file_cache, len);
987 filename[len] = '/';
988 memcpy(filename + len + 1, zend_system_id, 32);
989 memcpy(filename + len + 33, ZSTR_VAL(script_path), ZSTR_LEN(script_path));
990 memcpy(filename + len + 33 + ZSTR_LEN(script_path), SUFFIX, sizeof(SUFFIX));
991 #else
992 len = strlen(ZCG(accel_directives).file_cache);
993
994 filename = emalloc(len + 33 + 33 + ZSTR_LEN(script_path) + sizeof(SUFFIX));
995
996 memcpy(filename, ZCG(accel_directives).file_cache, len);
997 filename[len] = '\\';
998 memcpy(filename + 1 + len, accel_uname_id, 32);
999 len += 1 + 32;
1000 filename[len] = '\\';
1001
1002 memcpy(filename + len + 1, zend_system_id, 32);
1003
1004 if (ZSTR_LEN(script_path) >= 7 && ':' == ZSTR_VAL(script_path)[4] && '/' == ZSTR_VAL(script_path)[5] && '/' == ZSTR_VAL(script_path)[6]) {
1005 /* phar:// or file:// */
1006 *(filename + len + 33) = '\\';
1007 memcpy(filename + len + 34, ZSTR_VAL(script_path), 4);
1008 if (ZSTR_LEN(script_path) - 7 >= 2 && ':' == ZSTR_VAL(script_path)[8]) {
1009 *(filename + len + 38) = '\\';
1010 *(filename + len + 39) = ZSTR_VAL(script_path)[7];
1011 memcpy(filename + len + 40, ZSTR_VAL(script_path) + 9, ZSTR_LEN(script_path) - 9);
1012 memcpy(filename + len + 40 + ZSTR_LEN(script_path) - 9, SUFFIX, sizeof(SUFFIX));
1013 } else {
1014 memcpy(filename + len + 38, ZSTR_VAL(script_path) + 7, ZSTR_LEN(script_path) - 7);
1015 memcpy(filename + len + 38 + ZSTR_LEN(script_path) - 7, SUFFIX, sizeof(SUFFIX));
1016 }
1017 } else if (ZSTR_LEN(script_path) >= 2 && ':' == ZSTR_VAL(script_path)[1]) {
1018 /* local fs */
1019 *(filename + len + 33) = '\\';
1020 *(filename + len + 34) = ZSTR_VAL(script_path)[0];
1021 memcpy(filename + len + 35, ZSTR_VAL(script_path) + 2, ZSTR_LEN(script_path) - 2);
1022 memcpy(filename + len + 35 + ZSTR_LEN(script_path) - 2, SUFFIX, sizeof(SUFFIX));
1023 } else {
1024 /* network path */
1025 memcpy(filename + len + 33, ZSTR_VAL(script_path), ZSTR_LEN(script_path));
1026 memcpy(filename + len + 33 + ZSTR_LEN(script_path), SUFFIX, sizeof(SUFFIX));
1027 }
1028 #endif
1029
1030 return filename;
1031 }
1032
1033 /**
1034 * Helper function for zend_file_cache_script_store().
1035 *
1036 * @return true on success, false on error and errno is set to indicate the cause of the error
1037 */
1038 static bool zend_file_cache_script_write(int fd, const zend_persistent_script *script, const zend_file_cache_metainfo *info, const void *buf, const zend_string *s)
1039 {
1040 ssize_t written;
1041 const ssize_t total_size = (ssize_t)(sizeof(*info) + script->size + info->str_size);
1042
1043 #ifdef HAVE_SYS_UIO_H
1044 const struct iovec vec[] = {
1045 { .iov_base = (void *)info, .iov_len = sizeof(*info) },
1046 { .iov_base = (void *)buf, .iov_len = script->size },
1047 { .iov_base = (void *)ZSTR_VAL(s), .iov_len = info->str_size },
1048 };
1049
1050 written = writev(fd, vec, sizeof(vec) / sizeof(vec[0]));
1051 if (EXPECTED(written == total_size)) {
1052 return true;
1053 }
1054
1055 errno = written == -1 ? errno : EAGAIN;
1056 return false;
1057 #else
1058 if (UNEXPECTED(ZEND_LONG_MAX < (zend_long)total_size)) {
1059 # ifdef EFBIG
1060 errno = EFBIG;
1061 # else
1062 errno = ERANGE;
1063 # endif
1064 return false;
1065 }
1066
1067 written = write(fd, info, sizeof(*info));
1068 if (UNEXPECTED(written != sizeof(*info))) {
1069 errno = written == -1 ? errno : EAGAIN;
1070 return false;
1071 }
1072
1073 written = write(fd, buf, script->size);
1074 if (UNEXPECTED(written != script->size)) {
1075 errno = written == -1 ? errno : EAGAIN;
1076 return false;
1077 }
1078
1079 written = write(fd, ZSTR_VAL(s), info->str_size);
1080 if (UNEXPECTED(written != info->str_size)) {
1081 errno = written == -1 ? errno : EAGAIN;
1082 return false;
1083 }
1084
1085 return true;
1086 #endif
1087 }
1088
1089 int zend_file_cache_script_store(zend_persistent_script *script, bool in_shm)
1090 {
1091 int fd;
1092 char *filename;
1093 zend_file_cache_metainfo info;
1094 void *mem, *buf;
1095
1096 #ifdef HAVE_JIT
1097 /* FIXME: dump jited codes out to file cache? */
1098 if (JIT_G(on)) {
1099 return FAILURE;
1100 }
1101 #endif
1102
1103 filename = zend_file_cache_get_bin_file_path(script->script.filename);
1104
1105 if (zend_file_cache_mkdir(filename, strlen(ZCG(accel_directives).file_cache)) != SUCCESS) {
1106 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot create directory for file '%s', %s\n", filename, strerror(errno));
1107 efree(filename);
1108 return FAILURE;
1109 }
1110
1111 fd = zend_file_cache_open(filename, O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
1112 if (fd < 0) {
1113 if (errno != EEXIST) {
1114 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot create file '%s', %s\n", filename, strerror(errno));
1115 }
1116 efree(filename);
1117 return FAILURE;
1118 }
1119
1120 if (zend_file_cache_flock(fd, LOCK_EX) != 0) {
1121 close(fd);
1122 efree(filename);
1123 return FAILURE;
1124 }
1125
1126 #if defined(__AVX__) || defined(__SSE2__)
1127 /* Align to 64-byte boundary */
1128 mem = emalloc(script->size + 64);
1129 buf = (void*)(((uintptr_t)mem + 63L) & ~63L);
1130 #else
1131 mem = buf = emalloc(script->size);
1132 #endif
1133
1134 ZCG(mem) = zend_string_alloc(4096 - (_ZSTR_HEADER_SIZE + 1), 0);
1135
1136 zend_shared_alloc_init_xlat_table();
1137 if (!in_shm) {
1138 script->corrupted = true; /* used to check if script restored to SHM or process memory */
1139 }
1140 zend_file_cache_serialize(script, &info, buf);
1141 if (!in_shm) {
1142 script->corrupted = false;
1143 }
1144 zend_shared_alloc_destroy_xlat_table();
1145
1146 zend_string *const s = (zend_string*)ZCG(mem);
1147
1148 #if __has_feature(memory_sanitizer)
1149 /* The buffer may contain uninitialized regions. However, the uninitialized parts will not be
1150 * used when reading the cache. We should probably still try to get things fully initialized
1151 * for reproducibility, but for now ignore this issue. */
1152 __msan_unpoison(&info, sizeof(info));
1153 __msan_unpoison(buf, script->size);
1154 #endif
1155
1156 info.checksum = zend_adler32(ADLER32_INIT, buf, script->size);
1157 info.checksum = zend_adler32(info.checksum, (unsigned char*)ZSTR_VAL(s), info.str_size);
1158
1159 if (!zend_file_cache_script_write(fd, script, &info, buf, s)) {
1160 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot write to file '%s': %s\n", filename, strerror(errno));
1161 zend_string_release_ex(s, 0);
1162 close(fd);
1163 efree(mem);
1164 zend_file_cache_unlink(filename);
1165 efree(filename);
1166 return FAILURE;
1167 }
1168
1169 zend_string_release_ex(s, 0);
1170 efree(mem);
1171 if (zend_file_cache_flock(fd, LOCK_UN) != 0) {
1172 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s': %s\n", filename, strerror(errno));
1173 }
1174 close(fd);
1175 efree(filename);
1176
1177 return SUCCESS;
1178 }
1179
1180 static void zend_file_cache_unserialize_hash(HashTable *ht,
1181 zend_persistent_script *script,
1182 void *buf,
1183 unserialize_callback_t func,
1184 dtor_func_t dtor)
1185 {
1186 ht->pDestructor = dtor;
1187 if (HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED) {
1188 if (EXPECTED(!file_cache_only)) {
1189 HT_SET_DATA_ADDR(ht, &ZCSG(uninitialized_bucket));
1190 } else {
1191 HT_SET_DATA_ADDR(ht, &uninitialized_bucket);
1192 }
1193 return;
1194 }
1195 if (IS_UNSERIALIZED(ht->arData)) {
1196 return;
1197 }
1198 UNSERIALIZE_PTR(ht->arData);
1199 if (HT_IS_PACKED(ht)) {
1200 zval *p, *end;
1201
1202 p = ht->arPacked;
1203 end = p + ht->nNumUsed;
1204 while (p < end) {
1205 if (Z_TYPE_P(p) != IS_UNDEF) {
1206 func(p, script, buf);
1207 }
1208 p++;
1209 }
1210 } else {
1211 Bucket *p, *end;
1212
1213 p = ht->arData;
1214 end = p + ht->nNumUsed;
1215 while (p < end) {
1216 if (Z_TYPE(p->val) != IS_UNDEF) {
1217 UNSERIALIZE_STR(p->key);
1218 func(&p->val, script, buf);
1219 }
1220 p++;
1221 }
1222 }
1223 }
1224
1225 static void zend_file_cache_unserialize_ast(zend_ast *ast,
1226 zend_persistent_script *script,
1227 void *buf)
1228 {
1229 uint32_t i;
1230
1231 if (ast->kind == ZEND_AST_ZVAL || ast->kind == ZEND_AST_CONSTANT) {
1232 zend_file_cache_unserialize_zval(&((zend_ast_zval*)ast)->val, script, buf);
1233 } else if (zend_ast_is_list(ast)) {
1234 zend_ast_list *list = zend_ast_get_list(ast);
1235 for (i = 0; i < list->children; i++) {
1236 if (list->child[i] && !IS_UNSERIALIZED(list->child[i])) {
1237 UNSERIALIZE_PTR(list->child[i]);
1238 zend_file_cache_unserialize_ast(list->child[i], script, buf);
1239 }
1240 }
1241 } else {
1242 uint32_t children = zend_ast_get_num_children(ast);
1243 for (i = 0; i < children; i++) {
1244 if (ast->child[i] && !IS_UNSERIALIZED(ast->child[i])) {
1245 UNSERIALIZE_PTR(ast->child[i]);
1246 zend_file_cache_unserialize_ast(ast->child[i], script, buf);
1247 }
1248 }
1249 }
1250 }
1251
1252 static void zend_file_cache_unserialize_zval(zval *zv,
1253 zend_persistent_script *script,
1254 void *buf)
1255 {
1256 switch (Z_TYPE_P(zv)) {
1257 case IS_STRING:
1258 /* We can't use !IS_UNSERIALIZED here, because that does not recognize unserialized
1259 * interned strings in non-shm mode. */
1260 if (IS_SERIALIZED(Z_STR_P(zv)) || IS_SERIALIZED_INTERNED(Z_STR_P(zv))) {
1261 UNSERIALIZE_STR(Z_STR_P(zv));
1262 }
1263 break;
1264 case IS_ARRAY:
1265 if (!IS_UNSERIALIZED(Z_ARR_P(zv))) {
1266 HashTable *ht;
1267
1268 UNSERIALIZE_PTR(Z_ARR_P(zv));
1269 ht = Z_ARR_P(zv);
1270 zend_file_cache_unserialize_hash(ht,
1271 script, buf, zend_file_cache_unserialize_zval, ZVAL_PTR_DTOR);
1272 }
1273 break;
1274 case IS_CONSTANT_AST:
1275 if (!IS_UNSERIALIZED(Z_AST_P(zv))) {
1276 UNSERIALIZE_PTR(Z_AST_P(zv));
1277 zend_file_cache_unserialize_ast(Z_ASTVAL_P(zv), script, buf);
1278 }
1279 break;
1280 case IS_INDIRECT:
1281 /* Used by static properties. */
1282 UNSERIALIZE_PTR(Z_INDIRECT_P(zv));
1283 break;
1284 default:
1285 ZEND_ASSERT(Z_TYPE_P(zv) < IS_STRING);
1286 break;
1287 }
1288 }
1289
1290 static void zend_file_cache_unserialize_attribute(zval *zv, zend_persistent_script *script, void *buf)
1291 {
1292 zend_attribute *attr;
1293 uint32_t i;
1294
1295 UNSERIALIZE_PTR(Z_PTR_P(zv));
1296 attr = Z_PTR_P(zv);
1297
1298 UNSERIALIZE_STR(attr->name);
1299 UNSERIALIZE_STR(attr->lcname);
1300
1301 for (i = 0; i < attr->argc; i++) {
1302 UNSERIALIZE_STR(attr->args[i].name);
1303 zend_file_cache_unserialize_zval(&attr->args[i].value, script, buf);
1304 }
1305 }
1306
1307 static void zend_file_cache_unserialize_type(
1308 zend_type *type, zend_class_entry *scope, zend_persistent_script *script, void *buf)
1309 {
1310 if (ZEND_TYPE_HAS_LIST(*type)) {
1311 zend_type_list *list = ZEND_TYPE_LIST(*type);
1312 UNSERIALIZE_PTR(list);
1313 ZEND_TYPE_SET_PTR(*type, list);
1314
1315 zend_type *list_type;
1316 ZEND_TYPE_LIST_FOREACH(list, list_type) {
1317 zend_file_cache_unserialize_type(list_type, scope, script, buf);
1318 } ZEND_TYPE_LIST_FOREACH_END();
1319 } else if (ZEND_TYPE_HAS_NAME(*type)) {
1320 zend_string *type_name = ZEND_TYPE_NAME(*type);
1321 UNSERIALIZE_STR(type_name);
1322 ZEND_TYPE_SET_PTR(*type, type_name);
1323 if (!script->corrupted) {
1324 zend_accel_get_class_name_map_ptr(type_name);
1325 } else {
1326 zend_alloc_ce_cache(type_name);
1327 }
1328 }
1329 }
1330
1331 static void zend_file_cache_unserialize_op_array(zend_op_array *op_array,
1332 zend_persistent_script *script,
1333 void *buf)
1334 {
1335 if (!script->corrupted) {
1336 if (op_array != &script->script.main_op_array) {
1337 op_array->fn_flags |= ZEND_ACC_IMMUTABLE;
1338 ZEND_MAP_PTR_NEW(op_array->run_time_cache);
1339 } else {
1340 ZEND_ASSERT(!(op_array->fn_flags & ZEND_ACC_IMMUTABLE));
1341 ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL);
1342 }
1343 if (op_array->static_variables) {
1344 ZEND_MAP_PTR_NEW(op_array->static_variables_ptr);
1345 }
1346 } else {
1347 op_array->fn_flags &= ~ZEND_ACC_IMMUTABLE;
1348 ZEND_MAP_PTR_INIT(op_array->static_variables_ptr, NULL);
1349 ZEND_MAP_PTR_INIT(op_array->run_time_cache, NULL);
1350 }
1351
1352 /* Check whether this op_array has already been unserialized. */
1353 if (IS_UNSERIALIZED(op_array->opcodes)) {
1354 ZEND_ASSERT(op_array->scope && "Only method op_arrays should be shared");
1355 return;
1356 }
1357
1358 if (op_array->refcount) {
1359 op_array->refcount = NULL;
1360 UNSERIALIZE_PTR(op_array->static_variables);
1361 UNSERIALIZE_PTR(op_array->literals);
1362 UNSERIALIZE_PTR(op_array->opcodes);
1363 UNSERIALIZE_PTR(op_array->arg_info);
1364 UNSERIALIZE_PTR(op_array->vars);
1365 UNSERIALIZE_STR(op_array->function_name);
1366 UNSERIALIZE_STR(op_array->filename);
1367 UNSERIALIZE_PTR(op_array->live_range);
1368 UNSERIALIZE_PTR(op_array->scope);
1369 UNSERIALIZE_STR(op_array->doc_comment);
1370 UNSERIALIZE_ATTRIBUTES(op_array->attributes);
1371 UNSERIALIZE_PTR(op_array->try_catch_array);
1372 UNSERIALIZE_PTR(op_array->prototype);
1373 UNSERIALIZE_PTR(op_array->prop_info);
1374 return;
1375 }
1376
1377 if (op_array->static_variables) {
1378 HashTable *ht;
1379
1380 UNSERIALIZE_PTR(op_array->static_variables);
1381 ht = op_array->static_variables;
1382 zend_file_cache_unserialize_hash(ht,
1383 script, buf, zend_file_cache_unserialize_zval, ZVAL_PTR_DTOR);
1384 }
1385
1386 if (op_array->literals) {
1387 zval *p, *end;
1388
1389 UNSERIALIZE_PTR(op_array->literals);
1390 p = op_array->literals;
1391 end = p + op_array->last_literal;
1392 while (p < end) {
1393 zend_file_cache_unserialize_zval(p, script, buf);
1394 p++;
1395 }
1396 }
1397
1398 {
1399 zend_op *opline, *end;
1400
1401 UNSERIALIZE_PTR(op_array->opcodes);
1402 opline = op_array->opcodes;
1403 end = opline + op_array->last;
1404 while (opline < end) {
1405 #if ZEND_USE_ABS_CONST_ADDR
1406 if (opline->op1_type == IS_CONST) {
1407 UNSERIALIZE_PTR(opline->op1.zv);
1408 }
1409 if (opline->op2_type == IS_CONST) {
1410 UNSERIALIZE_PTR(opline->op2.zv);
1411 }
1412 #else
1413 if (opline->op1_type == IS_CONST) {
1414 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op1);
1415 }
1416 if (opline->op2_type == IS_CONST) {
1417 ZEND_PASS_TWO_UPDATE_CONSTANT(op_array, opline, opline->op2);
1418 }
1419 #endif
1420 #if ZEND_USE_ABS_JMP_ADDR
1421 switch (opline->opcode) {
1422 case ZEND_JMP:
1423 case ZEND_FAST_CALL:
1424 UNSERIALIZE_PTR(opline->op1.jmp_addr);
1425 break;
1426 case ZEND_JMPZ:
1427 case ZEND_JMPNZ:
1428 case ZEND_JMPZ_EX:
1429 case ZEND_JMPNZ_EX:
1430 case ZEND_JMP_SET:
1431 case ZEND_COALESCE:
1432 case ZEND_FE_RESET_R:
1433 case ZEND_FE_RESET_RW:
1434 case ZEND_ASSERT_CHECK:
1435 case ZEND_JMP_NULL:
1436 case ZEND_BIND_INIT_STATIC_OR_JMP:
1437 case ZEND_JMP_FRAMELESS:
1438 UNSERIALIZE_PTR(opline->op2.jmp_addr);
1439 break;
1440 case ZEND_CATCH:
1441 if (!(opline->extended_value & ZEND_LAST_CATCH)) {
1442 UNSERIALIZE_PTR(opline->op2.jmp_addr);
1443 }
1444 break;
1445 case ZEND_FE_FETCH_R:
1446 case ZEND_FE_FETCH_RW:
1447 case ZEND_SWITCH_LONG:
1448 case ZEND_SWITCH_STRING:
1449 /* relative extended_value don't have to be changed */
1450 break;
1451 }
1452 #endif
1453 zend_deserialize_opcode_handler(opline);
1454 opline++;
1455 }
1456
1457 UNSERIALIZE_PTR(op_array->scope);
1458
1459 if (op_array->arg_info) {
1460 zend_arg_info *p, *end;
1461 UNSERIALIZE_PTR(op_array->arg_info);
1462 p = op_array->arg_info;
1463 end = p + op_array->num_args;
1464 if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
1465 p--;
1466 }
1467 if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
1468 end++;
1469 }
1470 while (p < end) {
1471 if (!IS_UNSERIALIZED(p->name)) {
1472 UNSERIALIZE_STR(p->name);
1473 }
1474 zend_file_cache_unserialize_type(&p->type, (op_array->fn_flags & ZEND_ACC_CLOSURE) ? NULL : op_array->scope, script, buf);
1475 p++;
1476 }
1477 }
1478
1479 if (op_array->vars) {
1480 zend_string **p, **end;
1481
1482 UNSERIALIZE_PTR(op_array->vars);
1483 p = op_array->vars;
1484 end = p + op_array->last_var;
1485 while (p < end) {
1486 if (!IS_UNSERIALIZED(*p)) {
1487 UNSERIALIZE_STR(*p);
1488 }
1489 p++;
1490 }
1491 }
1492
1493 if (op_array->num_dynamic_func_defs) {
1494 UNSERIALIZE_PTR(op_array->dynamic_func_defs);
1495 for (uint32_t i = 0; i < op_array->num_dynamic_func_defs; i++) {
1496 UNSERIALIZE_PTR(op_array->dynamic_func_defs[i]);
1497 zend_file_cache_unserialize_op_array(op_array->dynamic_func_defs[i], script, buf);
1498 }
1499 }
1500
1501 UNSERIALIZE_STR(op_array->function_name);
1502 UNSERIALIZE_STR(op_array->filename);
1503 UNSERIALIZE_PTR(op_array->live_range);
1504 UNSERIALIZE_STR(op_array->doc_comment);
1505 UNSERIALIZE_ATTRIBUTES(op_array->attributes);
1506 UNSERIALIZE_PTR(op_array->try_catch_array);
1507 UNSERIALIZE_PTR(op_array->prototype);
1508 UNSERIALIZE_PTR(op_array->prop_info);
1509 }
1510 }
1511
1512 static void zend_file_cache_unserialize_func(zval *zv,
1513 zend_persistent_script *script,
1514 void *buf)
1515 {
1516 zend_function *func;
1517 UNSERIALIZE_PTR(Z_PTR_P(zv));
1518 func = Z_PTR_P(zv);
1519 ZEND_ASSERT(func->type == ZEND_USER_FUNCTION);
1520 zend_file_cache_unserialize_op_array(&func->op_array, script, buf);
1521 }
1522
1523 static void zend_file_cache_unserialize_prop_info(zval *zv,
1524 zend_persistent_script *script,
1525 void *buf)
1526 {
1527 if (!IS_UNSERIALIZED(Z_PTR_P(zv))) {
1528 zend_property_info *prop;
1529
1530 UNSERIALIZE_PTR(Z_PTR_P(zv));
1531 prop = Z_PTR_P(zv);
1532
1533 ZEND_ASSERT(prop->ce != NULL && prop->name != NULL);
1534 if (!IS_UNSERIALIZED(prop->ce)) {
1535 UNSERIALIZE_PTR(prop->ce);
1536 UNSERIALIZE_STR(prop->name);
1537 if (prop->doc_comment) {
1538 UNSERIALIZE_STR(prop->doc_comment);
1539 }
1540 UNSERIALIZE_ATTRIBUTES(prop->attributes);
1541 UNSERIALIZE_PTR(prop->prototype);
1542 if (prop->hooks) {
1543 UNSERIALIZE_PTR(prop->hooks);
1544 for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) {
1545 if (prop->hooks[i]) {
1546 UNSERIALIZE_PTR(prop->hooks[i]);
1547 zend_file_cache_unserialize_op_array(&prop->hooks[i]->op_array, script, buf);
1548 }
1549 }
1550 }
1551 zend_file_cache_unserialize_type(&prop->type, prop->ce, script, buf);
1552 }
1553 }
1554 }
1555
1556 static void zend_file_cache_unserialize_class_constant(zval *zv,
1557 zend_persistent_script *script,
1558 void *buf)
1559 {
1560 if (!IS_UNSERIALIZED(Z_PTR_P(zv))) {
1561 zend_class_constant *c;
1562
1563 UNSERIALIZE_PTR(Z_PTR_P(zv));
1564 c = Z_PTR_P(zv);
1565
1566 ZEND_ASSERT(c->ce != NULL);
1567 if (!IS_UNSERIALIZED(c->ce)) {
1568 UNSERIALIZE_PTR(c->ce);
1569
1570 zend_file_cache_unserialize_zval(&c->value, script, buf);
1571
1572 if (c->doc_comment) {
1573 UNSERIALIZE_STR(c->doc_comment);
1574 }
1575 UNSERIALIZE_ATTRIBUTES(c->attributes);
1576 zend_file_cache_unserialize_type(&c->type, c->ce, script, buf);
1577 }
1578 }
1579 }
1580
1581 static void zend_file_cache_unserialize_class(zval *zv,
1582 zend_persistent_script *script,
1583 void *buf)
1584 {
1585 zend_class_entry *ce;
1586
1587 UNSERIALIZE_PTR(Z_PTR_P(zv));
1588 ce = Z_PTR_P(zv);
1589
1590 UNSERIALIZE_STR(ce->name);
1591 if (!(ce->ce_flags & ZEND_ACC_ANON_CLASS)) {
1592 if (!script->corrupted) {
1593 zend_accel_get_class_name_map_ptr(ce->name);
1594 } else {
1595 zend_alloc_ce_cache(ce->name);
1596 }
1597 }
1598 if (ce->parent) {
1599 if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
1600 UNSERIALIZE_STR(ce->parent_name);
1601 } else {
1602 UNSERIALIZE_PTR(ce->parent);
1603 }
1604 }
1605 zend_file_cache_unserialize_hash(&ce->function_table,
1606 script, buf, zend_file_cache_unserialize_func, ZEND_FUNCTION_DTOR);
1607 if (ce->default_properties_table) {
1608 zval *p, *end;
1609
1610 UNSERIALIZE_PTR(ce->default_properties_table);
1611 p = ce->default_properties_table;
1612 end = p + ce->default_properties_count;
1613 while (p < end) {
1614 zend_file_cache_unserialize_zval(p, script, buf);
1615 p++;
1616 }
1617 }
1618 if (ce->default_static_members_table) {
1619 zval *p, *end;
1620 UNSERIALIZE_PTR(ce->default_static_members_table);
1621 p = ce->default_static_members_table;
1622 end = p + ce->default_static_members_count;
1623 while (p < end) {
1624 zend_file_cache_unserialize_zval(p, script, buf);
1625 p++;
1626 }
1627 }
1628 zend_file_cache_unserialize_hash(&ce->constants_table,
1629 script, buf, zend_file_cache_unserialize_class_constant, NULL);
1630 UNSERIALIZE_STR(ce->info.user.filename);
1631 UNSERIALIZE_STR(ce->doc_comment);
1632 UNSERIALIZE_ATTRIBUTES(ce->attributes);
1633 zend_file_cache_unserialize_hash(&ce->properties_info,
1634 script, buf, zend_file_cache_unserialize_prop_info, NULL);
1635
1636 if (ce->properties_info_table) {
1637 uint32_t i;
1638 UNSERIALIZE_PTR(ce->properties_info_table);
1639
1640 for (i = 0; i < ce->default_properties_count; i++) {
1641 UNSERIALIZE_PTR(ce->properties_info_table[i]);
1642 }
1643 }
1644
1645 if (ce->num_interfaces) {
1646 uint32_t i;
1647
1648 ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_LINKED));
1649 UNSERIALIZE_PTR(ce->interface_names);
1650
1651 for (i = 0; i < ce->num_interfaces; i++) {
1652 UNSERIALIZE_STR(ce->interface_names[i].name);
1653 UNSERIALIZE_STR(ce->interface_names[i].lc_name);
1654 }
1655 }
1656
1657 if (ce->num_traits) {
1658 uint32_t i;
1659
1660 UNSERIALIZE_PTR(ce->trait_names);
1661
1662 for (i = 0; i < ce->num_traits; i++) {
1663 UNSERIALIZE_STR(ce->trait_names[i].name);
1664 UNSERIALIZE_STR(ce->trait_names[i].lc_name);
1665 }
1666
1667 if (ce->trait_aliases) {
1668 zend_trait_alias **p, *q;
1669
1670 UNSERIALIZE_PTR(ce->trait_aliases);
1671 p = ce->trait_aliases;
1672
1673 while (*p) {
1674 UNSERIALIZE_PTR(*p);
1675 q = *p;
1676
1677 if (q->trait_method.method_name) {
1678 UNSERIALIZE_STR(q->trait_method.method_name);
1679 }
1680 if (q->trait_method.class_name) {
1681 UNSERIALIZE_STR(q->trait_method.class_name);
1682 }
1683
1684 if (q->alias) {
1685 UNSERIALIZE_STR(q->alias);
1686 }
1687 p++;
1688 }
1689 }
1690
1691 if (ce->trait_precedences) {
1692 zend_trait_precedence **p, *q;
1693 uint32_t j;
1694
1695 UNSERIALIZE_PTR(ce->trait_precedences);
1696 p = ce->trait_precedences;
1697
1698 while (*p) {
1699 UNSERIALIZE_PTR(*p);
1700 q = *p;
1701
1702 if (q->trait_method.method_name) {
1703 UNSERIALIZE_STR(q->trait_method.method_name);
1704 }
1705 if (q->trait_method.class_name) {
1706 UNSERIALIZE_STR(q->trait_method.class_name);
1707 }
1708
1709 for (j = 0; j < q->num_excludes; j++) {
1710 UNSERIALIZE_STR(q->exclude_class_names[j]);
1711 }
1712 p++;
1713 }
1714 }
1715 }
1716
1717 UNSERIALIZE_PTR(ce->constructor);
1718 UNSERIALIZE_PTR(ce->destructor);
1719 UNSERIALIZE_PTR(ce->clone);
1720 UNSERIALIZE_PTR(ce->__get);
1721 UNSERIALIZE_PTR(ce->__set);
1722 UNSERIALIZE_PTR(ce->__call);
1723 UNSERIALIZE_PTR(ce->__serialize);
1724 UNSERIALIZE_PTR(ce->__unserialize);
1725 UNSERIALIZE_PTR(ce->__isset);
1726 UNSERIALIZE_PTR(ce->__unset);
1727 UNSERIALIZE_PTR(ce->__tostring);
1728 UNSERIALIZE_PTR(ce->__callstatic);
1729 UNSERIALIZE_PTR(ce->__debugInfo);
1730
1731 if (ce->iterator_funcs_ptr) {
1732 UNSERIALIZE_PTR(ce->iterator_funcs_ptr);
1733 UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_new_iterator);
1734 UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_rewind);
1735 UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_valid);
1736 UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_key);
1737 UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_current);
1738 UNSERIALIZE_PTR(ce->iterator_funcs_ptr->zf_next);
1739 }
1740 if (ce->arrayaccess_funcs_ptr) {
1741 UNSERIALIZE_PTR(ce->arrayaccess_funcs_ptr);
1742 UNSERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetget);
1743 UNSERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetexists);
1744 UNSERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetset);
1745 UNSERIALIZE_PTR(ce->arrayaccess_funcs_ptr->zf_offsetunset);
1746 }
1747
1748 if (!(script->corrupted)) {
1749 ce->ce_flags |= ZEND_ACC_IMMUTABLE;
1750 ce->ce_flags &= ~ZEND_ACC_FILE_CACHED;
1751 ZEND_MAP_PTR_NEW(ce->mutable_data);
1752 if (ce->default_static_members_count) {
1753 ZEND_MAP_PTR_NEW(ce->static_members_table);
1754 }
1755 } else {
1756 ce->ce_flags &= ~ZEND_ACC_IMMUTABLE;
1757 ce->ce_flags |= ZEND_ACC_FILE_CACHED;
1758 ZEND_MAP_PTR_INIT(ce->mutable_data, NULL);
1759 ZEND_MAP_PTR_INIT(ce->static_members_table, NULL);
1760 }
1761
1762 if (ce->get_iterator) {
1763 ZEND_ASSERT(ce->get_iterator == HOOKED_ITERATOR_PLACEHOLDER);
1764 ce->get_iterator = zend_hooked_object_get_iterator;
1765 }
1766
1767 // Memory addresses of object handlers are not stable. They can change due to ASLR or order of linking dynamic. To
1768 // avoid pointing to invalid memory we relink default_object_handlers here.
1769 ce->default_object_handlers = ce->ce_flags & ZEND_ACC_ENUM ? &zend_enum_object_handlers : &std_object_handlers;
1770 }
1771
1772 static void zend_file_cache_unserialize_warnings(zend_persistent_script *script, void *buf)
1773 {
1774 if (script->warnings) {
1775 UNSERIALIZE_PTR(script->warnings);
1776 for (uint32_t i = 0; i < script->num_warnings; i++) {
1777 UNSERIALIZE_PTR(script->warnings[i]);
1778 UNSERIALIZE_STR(script->warnings[i]->filename);
1779 UNSERIALIZE_STR(script->warnings[i]->message);
1780 }
1781 }
1782 }
1783
1784 static void zend_file_cache_unserialize_early_bindings(zend_persistent_script *script, void *buf)
1785 {
1786 if (script->early_bindings) {
1787 UNSERIALIZE_PTR(script->early_bindings);
1788 for (uint32_t i = 0; i < script->num_early_bindings; i++) {
1789 UNSERIALIZE_STR(script->early_bindings[i].lcname);
1790 UNSERIALIZE_STR(script->early_bindings[i].rtd_key);
1791 UNSERIALIZE_STR(script->early_bindings[i].lc_parent_name);
1792 }
1793 }
1794 }
1795
1796 static void zend_file_cache_unserialize(zend_persistent_script *script,
1797 void *buf)
1798 {
1799 script->mem = buf;
1800
1801 UNSERIALIZE_STR(script->script.filename);
1802
1803 zend_file_cache_unserialize_hash(&script->script.class_table,
1804 script, buf, zend_file_cache_unserialize_class, ZEND_CLASS_DTOR);
1805 zend_file_cache_unserialize_hash(&script->script.function_table,
1806 script, buf, zend_file_cache_unserialize_func, ZEND_FUNCTION_DTOR);
1807 zend_file_cache_unserialize_op_array(&script->script.main_op_array, script, buf);
1808 zend_file_cache_unserialize_warnings(script, buf);
1809 zend_file_cache_unserialize_early_bindings(script, buf);
1810 }
1811
1812 zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handle)
1813 {
1814 zend_string *full_path = file_handle->opened_path;
1815 int fd;
1816 char *filename;
1817 zend_persistent_script *script;
1818 zend_file_cache_metainfo info;
1819 zend_accel_hash_entry *bucket;
1820 void *mem, *checkpoint, *buf;
1821 bool cache_it = true;
1822 unsigned int actual_checksum;
1823 bool ok;
1824
1825 if (!full_path) {
1826 return NULL;
1827 }
1828 filename = zend_file_cache_get_bin_file_path(full_path);
1829
1830 fd = zend_file_cache_open(filename, O_RDONLY | O_BINARY);
1831 if (fd < 0) {
1832 efree(filename);
1833 return NULL;
1834 }
1835
1836 if (zend_file_cache_flock(fd, LOCK_SH) != 0) {
1837 close(fd);
1838 efree(filename);
1839 return NULL;
1840 }
1841
1842 if (read(fd, &info, sizeof(info)) != sizeof(info)) {
1843 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (info)\n", filename);
1844 zend_file_cache_flock(fd, LOCK_UN);
1845 close(fd);
1846 zend_file_cache_unlink(filename);
1847 efree(filename);
1848 return NULL;
1849 }
1850
1851 /* verify header */
1852 if (memcmp(info.magic, "OPCACHE", 8) != 0) {
1853 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (wrong header)\n", filename);
1854 zend_file_cache_flock(fd, LOCK_UN);
1855 close(fd);
1856 zend_file_cache_unlink(filename);
1857 efree(filename);
1858 return NULL;
1859 }
1860 if (memcmp(info.system_id, zend_system_id, 32) != 0) {
1861 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (wrong \"system_id\")\n", filename);
1862 zend_file_cache_flock(fd, LOCK_UN);
1863 close(fd);
1864 zend_file_cache_unlink(filename);
1865 efree(filename);
1866 return NULL;
1867 }
1868
1869 /* verify timestamp */
1870 if (ZCG(accel_directives).validate_timestamps &&
1871 zend_get_file_handle_timestamp(file_handle, NULL) != info.timestamp) {
1872 if (zend_file_cache_flock(fd, LOCK_UN) != 0) {
1873 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s'\n", filename);
1874 }
1875 close(fd);
1876 zend_file_cache_unlink(filename);
1877 efree(filename);
1878 return NULL;
1879 }
1880
1881 checkpoint = zend_arena_checkpoint(CG(arena));
1882 #if defined(__AVX__) || defined(__SSE2__)
1883 /* Align to 64-byte boundary */
1884 mem = zend_arena_alloc(&CG(arena), info.mem_size + info.str_size + 64);
1885 mem = (void*)(((uintptr_t)mem + 63L) & ~63L);
1886 #else
1887 mem = zend_arena_alloc(&CG(arena), info.mem_size + info.str_size);
1888 #endif
1889
1890 if (read(fd, mem, info.mem_size + info.str_size) != (ssize_t)(info.mem_size + info.str_size)) {
1891 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (mem)\n", filename);
1892 zend_file_cache_flock(fd, LOCK_UN);
1893 close(fd);
1894 zend_file_cache_unlink(filename);
1895 zend_arena_release(&CG(arena), checkpoint);
1896 efree(filename);
1897 return NULL;
1898 }
1899 if (zend_file_cache_flock(fd, LOCK_UN) != 0) {
1900 zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s'\n", filename);
1901 }
1902 close(fd);
1903
1904 /* verify checksum */
1905 if (ZCG(accel_directives).file_cache_consistency_checks &&
1906 (actual_checksum = zend_adler32(ADLER32_INIT, mem, info.mem_size + info.str_size)) != info.checksum) {
1907 zend_accel_error(ACCEL_LOG_WARNING, "corrupted file '%s' excepted checksum: 0x%08x actual checksum: 0x%08x\n", filename, info.checksum, actual_checksum);
1908 zend_file_cache_unlink(filename);
1909 zend_arena_release(&CG(arena), checkpoint);
1910 efree(filename);
1911 return NULL;
1912 }
1913
1914 if (!file_cache_only &&
1915 !ZCSG(restart_in_progress) &&
1916 !ZCSG(restart_pending) &&
1917 !ZSMMG(memory_exhausted) &&
1918 accelerator_shm_read_lock() == SUCCESS) {
1919 /* exclusive lock */
1920 zend_shared_alloc_lock();
1921
1922 /* Check if we still need to put the file into the cache (may be it was
1923 * already stored by another process. This final check is done under
1924 * exclusive lock) */
1925 bucket = zend_accel_hash_find_entry(&ZCSG(hash), full_path);
1926 if (bucket) {
1927 script = (zend_persistent_script *)bucket->data;
1928 if (!script->corrupted) {
1929 zend_shared_alloc_unlock();
1930 zend_arena_release(&CG(arena), checkpoint);
1931 efree(filename);
1932 return script;
1933 }
1934 }
1935
1936 if (zend_accel_hash_is_full(&ZCSG(hash))) {
1937 zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1938 ZSMMG(memory_exhausted) = 1;
1939 zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
1940 zend_shared_alloc_unlock();
1941 goto use_process_mem;
1942 }
1943
1944 buf = zend_shared_alloc_aligned(info.mem_size);
1945
1946 if (!buf) {
1947 zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
1948 zend_shared_alloc_unlock();
1949 goto use_process_mem;
1950 }
1951 memcpy(buf, mem, info.mem_size);
1952 zend_map_ptr_extend(ZCSG(map_ptr_last));
1953 } else {
1954 use_process_mem:
1955 buf = mem;
1956 cache_it = false;
1957 }
1958
1959 ZCG(mem) = ((char*)mem + info.mem_size);
1960 script = (zend_persistent_script*)((char*)buf + info.script_offset);
1961 script->corrupted = !cache_it; /* used to check if script restored to SHM or process memory */
1962
1963 ok = true;
1964 zend_try {
1965 zend_file_cache_unserialize(script, buf);
1966 } zend_catch {
1967 ok = false;
1968 } zend_end_try();
1969 if (!ok) {
1970 if (cache_it) {
1971 zend_shared_alloc_unlock();
1972 goto use_process_mem;
1973 } else {
1974 zend_arena_release(&CG(arena), checkpoint);
1975 efree(filename);
1976 return NULL;
1977 }
1978 }
1979
1980 script->corrupted = false;
1981
1982 if (cache_it) {
1983 ZCSG(map_ptr_last) = CG(map_ptr_last);
1984 script->dynamic_members.last_used = ZCG(request_time);
1985
1986 zend_accel_hash_update(&ZCSG(hash), script->script.filename, 0, script);
1987
1988 zend_shared_alloc_unlock();
1989 zend_accel_error(ACCEL_LOG_INFO, "File cached script loaded into memory '%s'", ZSTR_VAL(script->script.filename));
1990
1991 zend_arena_release(&CG(arena), checkpoint);
1992 }
1993 efree(filename);
1994
1995 return script;
1996 }
1997
1998 void zend_file_cache_invalidate(zend_string *full_path)
1999 {
2000 char *filename;
2001
2002 filename = zend_file_cache_get_bin_file_path(full_path);
2003
2004 zend_file_cache_unlink(filename);
2005 efree(filename);
2006 }
2007