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