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