xref: /PHP-7.0/ext/opcache/zend_file_cache.c (revision da61c7a2)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend OPcache                                                         |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1998-2017 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@zend.com>                             |
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 
27 #ifdef HAVE_OPCACHE_FILE_CACHE
28 
29 #include "ZendAccelerator.h"
30 #include "zend_file_cache.h"
31 #include "zend_shared_alloc.h"
32 #include "zend_accelerator_util_funcs.h"
33 #include "zend_accelerator_hash.h"
34 
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 
39 #if HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42 
43 #ifdef HAVE_SYS_UIO_H
44 # include <sys/uio.h>
45 #endif
46 
47 #ifdef HAVE_SYS_FILE_H
48 # include <sys/file.h>
49 #endif
50 
51 #ifdef ZEND_WIN32
52 # define LOCK_SH 0
53 # define LOCK_EX 1
54 # define LOCK_UN 2
zend_file_cache_flock(int fd,int op)55 static int zend_file_cache_flock(int fd, int op)
56 {
57 	OVERLAPPED offset = {0,0,0,0,NULL};
58 	if (op == LOCK_EX) {
59 		if (LockFileEx((HANDLE)_get_osfhandle(fd),
60 		               LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &offset) == TRUE) {
61 			return 0;
62 		}
63 	} else if (op == LOCK_SH) {
64 		if (LockFileEx((HANDLE)_get_osfhandle(fd),
65 		               0, 0, 1, 0, &offset) == TRUE) {
66 			return 0;
67 		}
68 	} else if (op == LOCK_UN) {
69 		if (UnlockFileEx((HANDLE)_get_osfhandle(fd),
70 		                 0, 1, 0, &offset) == TRUE) {
71 			return 0;
72 		}
73 	}
74 	return -1;
75 }
76 #elif defined(HAVE_FLOCK)
77 # define zend_file_cache_flock flock
78 #else
79 # define LOCK_SH 0
80 # define LOCK_EX 1
81 # define LOCK_UN 2
zend_file_cache_flock(int fd,int type)82 static int zend_file_cache_flock(int fd, int type)
83 {
84 	return 0;
85 }
86 #endif
87 
88 #ifndef O_BINARY
89 #  define O_BINARY 0
90 #endif
91 
92 #define SUFFIX ".bin"
93 
94 #define IS_SERIALIZED_INTERNED(ptr) \
95 	((size_t)(ptr) & Z_UL(1))
96 #define IS_SERIALIZED(ptr) \
97 	((char*)(ptr) < (char*)script->size)
98 #define IS_UNSERIALIZED(ptr) \
99 	(((char*)(ptr) >= (char*)script->mem && (char*)(ptr) < (char*)script->mem + script->size) || \
100 	 IS_ACCEL_INTERNED(ptr))
101 #define SERIALIZE_PTR(ptr) do { \
102 		if (ptr) { \
103 			ZEND_ASSERT(IS_UNSERIALIZED(ptr)); \
104 			(ptr) = (void*)((char*)(ptr) - (char*)script->mem); \
105 		} \
106 	} while (0)
107 #define UNSERIALIZE_PTR(ptr) do { \
108 		if (ptr) { \
109 			ZEND_ASSERT(IS_SERIALIZED(ptr)); \
110 			(ptr) = (void*)((char*)buf + (size_t)(ptr)); \
111 		} \
112 	} while (0)
113 #define SERIALIZE_STR(ptr) do { \
114 		if (ptr) { \
115 			if (IS_ACCEL_INTERNED(ptr)) { \
116 				(ptr) = zend_file_cache_serialize_interned((zend_string*)(ptr), info); \
117 			} else { \
118 				ZEND_ASSERT(IS_UNSERIALIZED(ptr)); \
119 				/* script->corrupted shows if the script in SHM or not */ \
120 				if (EXPECTED(script->corrupted)) { \
121 					GC_FLAGS(ptr) |= IS_STR_INTERNED; \
122 					GC_FLAGS(ptr) &= ~IS_STR_PERMANENT; \
123 				} \
124 				(ptr) = (void*)((char*)(ptr) - (char*)script->mem); \
125 			} \
126 		} \
127 	} while (0)
128 #define UNSERIALIZE_STR(ptr) do { \
129 		if (ptr) { \
130 			if (IS_SERIALIZED_INTERNED(ptr)) { \
131 				(ptr) = (void*)zend_file_cache_unserialize_interned((zend_string*)(ptr), !script->corrupted); \
132 			} else { \
133 				ZEND_ASSERT(IS_SERIALIZED(ptr)); \
134 				(ptr) = (void*)((char*)buf + (size_t)(ptr)); \
135 				/* script->corrupted shows if the script in SHM or not */ \
136 				if (EXPECTED(!script->corrupted)) { \
137 					GC_FLAGS(ptr) |= IS_STR_INTERNED | IS_STR_PERMANENT; \
138 				} else { \
139 					GC_FLAGS(ptr) |= IS_STR_INTERNED; \
140 					GC_FLAGS(ptr) &= ~IS_STR_PERMANENT; \
141 				} \
142 			} \
143 		} \
144 	} while (0)
145 
146 static const uint32_t uninitialized_bucket[-HT_MIN_MASK] =
147 	{HT_INVALID_IDX, HT_INVALID_IDX};
148 
149 typedef struct _zend_file_cache_metainfo {
150 	char         magic[8];
151 	char         system_id[32];
152 	size_t       mem_size;
153 	size_t       str_size;
154 	size_t       script_offset;
155 	accel_time_t timestamp;
156 	uint32_t     checksum;
157 } zend_file_cache_metainfo;
158 
zend_file_cache_mkdir(char * filename,size_t start)159 static int zend_file_cache_mkdir(char *filename, size_t start)
160 {
161 	char *s = filename + start;
162 
163 	while (*s) {
164 		if (IS_SLASH(*s)) {
165 			char old = *s;
166 			*s = '\000';
167 			if (mkdir(filename, S_IRWXU) < 0 && errno != EEXIST) {
168 				*s = old;
169 				return FAILURE;
170 			}
171 			*s = old;
172 		}
173 		s++;
174 	}
175 	return SUCCESS;
176 }
177 
178 typedef void (*serialize_callback_t)(zval                     *zv,
179                                      zend_persistent_script   *script,
180                                      zend_file_cache_metainfo *info,
181                                      void                     *buf);
182 
183 typedef void (*unserialize_callback_t)(zval                    *zv,
184                                        zend_persistent_script  *script,
185                                        void                    *buf);
186 
187 static void zend_file_cache_serialize_zval(zval                     *zv,
188                                            zend_persistent_script   *script,
189                                            zend_file_cache_metainfo *info,
190                                            void                     *buf);
191 static void zend_file_cache_unserialize_zval(zval                    *zv,
192                                              zend_persistent_script  *script,
193                                              void                    *buf);
194 
zend_file_cache_serialize_interned(zend_string * str,zend_file_cache_metainfo * info)195 static void *zend_file_cache_serialize_interned(zend_string              *str,
196                                                 zend_file_cache_metainfo *info)
197 {
198 	size_t len;
199 	void *ret;
200 
201 	/* check if the same interned string was already stored */
202 	ret = zend_shared_alloc_get_xlat_entry(str);
203 	if (ret) {
204 		return ret;
205 	}
206 
207 	len = ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(ZSTR_LEN(str)));
208 	ret = (void*)(info->str_size | Z_UL(1));
209 	zend_shared_alloc_register_xlat_entry(str, ret);
210 	if (info->str_size + len > ZSTR_LEN((zend_string*)ZCG(mem))) {
211 		size_t new_len = info->str_size + len;
212 		ZCG(mem) = (void*)zend_string_realloc(
213 			(zend_string*)ZCG(mem),
214 			((_ZSTR_HEADER_SIZE + 1 + new_len + 4095) & ~0xfff) - (_ZSTR_HEADER_SIZE + 1),
215 			0);
216 	}
217 	memcpy(ZSTR_VAL((zend_string*)ZCG(mem)) + info->str_size, str, len);
218 	info->str_size += len;
219 	return ret;
220 }
221 
zend_file_cache_unserialize_interned(zend_string * str,int in_shm)222 static void *zend_file_cache_unserialize_interned(zend_string *str, int in_shm)
223 {
224 	zend_string *ret;
225 
226 	str = (zend_string*)((char*)ZCG(mem) + ((size_t)(str) & ~Z_UL(1)));
227 	if (in_shm) {
228 		ret = accel_new_interned_string(str);
229 		if (ret == str) {
230 			/* We have to create new SHM allocated string */
231 			size_t size = _ZSTR_STRUCT_SIZE(ZSTR_LEN(str));
232 			ret = zend_shared_alloc(size);
233 			if (!ret) {
234 				zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
235 				LONGJMP(*EG(bailout), FAILURE);
236 			}
237 			memcpy(ret, str, size);
238 			/* String wasn't interned but we will use it as interned anyway */
239 			GC_REFCOUNT(ret) = 1;
240 			GC_TYPE_INFO(ret) = IS_STRING | ((IS_STR_INTERNED | IS_STR_PERSISTENT | IS_STR_PERMANENT) << 8);
241 		}
242 	} else {
243 		ret = str;
244 		GC_FLAGS(ret) |= IS_STR_INTERNED;
245 		GC_FLAGS(ret) &= ~IS_STR_PERMANENT;
246 	}
247 	return ret;
248 }
249 
zend_file_cache_serialize_hash(HashTable * ht,zend_persistent_script * script,zend_file_cache_metainfo * info,void * buf,serialize_callback_t func)250 static void zend_file_cache_serialize_hash(HashTable                *ht,
251                                            zend_persistent_script   *script,
252                                            zend_file_cache_metainfo *info,
253                                            void                     *buf,
254                                            serialize_callback_t      func)
255 {
256 	Bucket *p, *end;
257 
258 	if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
259 		ht->arData = NULL;
260 		return;
261 	}
262 	if (IS_SERIALIZED(ht->arData)) {
263 		return;
264 	}
265 	SERIALIZE_PTR(ht->arData);
266 	p = ht->arData;
267 	UNSERIALIZE_PTR(p);
268 	end = p + ht->nNumUsed;
269 	while (p < end) {
270 		if (Z_TYPE(p->val) != IS_UNDEF) {
271 			SERIALIZE_STR(p->key);
272 			func(&p->val, script, info, buf);
273 		}
274 		p++;
275 	}
276 }
277 
zend_file_cache_serialize_ast(zend_ast * ast,zend_persistent_script * script,zend_file_cache_metainfo * info,void * buf)278 static zend_ast *zend_file_cache_serialize_ast(zend_ast                 *ast,
279                                                zend_persistent_script   *script,
280                                                zend_file_cache_metainfo *info,
281                                                void                     *buf)
282 {
283 	uint32_t i;
284 	zend_ast *ret;
285 
286 	SERIALIZE_PTR(ast);
287 	ret = ast;
288 	UNSERIALIZE_PTR(ast);
289 
290 	if (ast->kind == ZEND_AST_ZVAL) {
291 		zend_file_cache_serialize_zval(&((zend_ast_zval*)ast)->val, script, info, buf);
292 	} else if (zend_ast_is_list(ast)) {
293 		zend_ast_list *list = zend_ast_get_list(ast);
294 		for (i = 0; i < list->children; i++) {
295 			if (list->child[i]) {
296 				list->child[i] = zend_file_cache_serialize_ast(list->child[i], script, info, buf);
297 			}
298 		}
299 	} else {
300 		uint32_t children = zend_ast_get_num_children(ast);
301 		for (i = 0; i < children; i++) {
302 			if (ast->child[i]) {
303 				ast->child[i] = zend_file_cache_serialize_ast(ast->child[i], script, info, buf);
304 			}
305 		}
306 	}
307 	return ret;
308 }
309 
zend_file_cache_serialize_zval(zval * zv,zend_persistent_script * script,zend_file_cache_metainfo * info,void * buf)310 static void zend_file_cache_serialize_zval(zval                     *zv,
311                                            zend_persistent_script   *script,
312                                            zend_file_cache_metainfo *info,
313                                            void                     *buf)
314 {
315 	switch (Z_TYPE_P(zv)) {
316 		case IS_STRING:
317 		case IS_CONSTANT:
318 			if (!IS_SERIALIZED(Z_STR_P(zv))) {
319 				SERIALIZE_STR(Z_STR_P(zv));
320 			}
321 			break;
322 		case IS_ARRAY:
323 			if (!IS_SERIALIZED(Z_ARR_P(zv))) {
324 				HashTable *ht;
325 
326 				SERIALIZE_PTR(Z_ARR_P(zv));
327 				ht = Z_ARR_P(zv);
328 				UNSERIALIZE_PTR(ht);
329 				zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval);
330 			}
331 			break;
332 		case IS_REFERENCE:
333 			if (!IS_SERIALIZED(Z_REF_P(zv))) {
334 				zend_reference *ref;
335 
336 				SERIALIZE_PTR(Z_REF_P(zv));
337 				ref = Z_REF_P(zv);
338 				UNSERIALIZE_PTR(ref);
339 				zend_file_cache_serialize_zval(&ref->val, script, info, buf);
340 			}
341 			break;
342 		case IS_CONSTANT_AST:
343 			if (!IS_SERIALIZED(Z_AST_P(zv))) {
344 				zend_ast_ref *ast;
345 
346 				SERIALIZE_PTR(Z_AST_P(zv));
347 				ast = Z_AST_P(zv);
348 				UNSERIALIZE_PTR(ast);
349 				if (!IS_SERIALIZED(ast->ast)) {
350 					ast->ast = zend_file_cache_serialize_ast(ast->ast, script, info, buf);
351 				}
352 			}
353 			break;
354 	}
355 }
356 
zend_file_cache_serialize_op_array(zend_op_array * op_array,zend_persistent_script * script,zend_file_cache_metainfo * info,void * buf)357 static void zend_file_cache_serialize_op_array(zend_op_array            *op_array,
358                                                zend_persistent_script   *script,
359                                                zend_file_cache_metainfo *info,
360                                                void                     *buf)
361 {
362 	if (op_array->static_variables && !IS_SERIALIZED(op_array->static_variables)) {
363 		HashTable *ht;
364 
365 		SERIALIZE_PTR(op_array->static_variables);
366 		ht = op_array->static_variables;
367 		UNSERIALIZE_PTR(ht);
368 		zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_zval);
369 	}
370 
371 	if (op_array->scope && !IS_SERIALIZED(op_array->opcodes)) {
372 		if (UNEXPECTED(zend_shared_alloc_get_xlat_entry(op_array->opcodes))) {
373 			op_array->refcount = (uint32_t*)(intptr_t)-1;
374 			SERIALIZE_PTR(op_array->literals);
375 			SERIALIZE_PTR(op_array->opcodes);
376 			SERIALIZE_PTR(op_array->arg_info);
377 			SERIALIZE_PTR(op_array->vars);
378 			SERIALIZE_STR(op_array->function_name);
379 			SERIALIZE_STR(op_array->filename);
380 			SERIALIZE_PTR(op_array->brk_cont_array);
381 			SERIALIZE_PTR(op_array->scope);
382 			SERIALIZE_STR(op_array->doc_comment);
383 			SERIALIZE_PTR(op_array->try_catch_array);
384 			SERIALIZE_PTR(op_array->prototype);
385 			return;
386 		}
387 		zend_shared_alloc_register_xlat_entry(op_array->opcodes, op_array->opcodes);
388 	}
389 
390 	if (op_array->literals && !IS_SERIALIZED(op_array->literals)) {
391 		zval *p, *end;
392 
393 		SERIALIZE_PTR(op_array->literals);
394 		p = op_array->literals;
395 		UNSERIALIZE_PTR(p);
396 		end = p + op_array->last_literal;
397 		while (p < end) {
398 			zend_file_cache_serialize_zval(p, script, info, buf);
399 			p++;
400 		}
401 	}
402 
403 	if (!IS_SERIALIZED(op_array->opcodes)) {
404 #if ZEND_USE_ABS_CONST_ADDR || ZEND_USE_ABS_JMP_ADDR
405 		zend_op *opline, *end;
406 
407 		SERIALIZE_PTR(op_array->opcodes);
408 		opline = op_array->opcodes;
409 		UNSERIALIZE_PTR(opline);
410 		end = opline + op_array->last;
411 		while (opline < end) {
412 # if ZEND_USE_ABS_CONST_ADDR
413 			if (ZEND_OP1_TYPE(opline) == IS_CONST) {
414 				SERIALIZE_PTR(opline->op1.zv);
415 			}
416 			if (ZEND_OP2_TYPE(opline) == IS_CONST) {
417 				SERIALIZE_PTR(opline->op2.zv);
418 			}
419 # endif
420 # if ZEND_USE_ABS_JMP_ADDR
421 			switch (opline->opcode) {
422 				case ZEND_JMP:
423 				case ZEND_FAST_CALL:
424 				case ZEND_DECLARE_ANON_CLASS:
425 				case ZEND_DECLARE_ANON_INHERITED_CLASS:
426 					SERIALIZE_PTR(opline->op1.jmp_addr);
427 					break;
428 				case ZEND_JMPZNZ:
429 					/* relative extended_value don't have to be changed */
430 					/* break omitted intentionally */
431 				case ZEND_JMPZ:
432 				case ZEND_JMPNZ:
433 				case ZEND_JMPZ_EX:
434 				case ZEND_JMPNZ_EX:
435 				case ZEND_JMP_SET:
436 				case ZEND_COALESCE:
437 				case ZEND_NEW:
438 				case ZEND_FE_RESET_R:
439 				case ZEND_FE_RESET_RW:
440 				case ZEND_ASSERT_CHECK:
441 					SERIALIZE_PTR(opline->op2.jmp_addr);
442 					break;
443 				case ZEND_FE_FETCH_R:
444 				case ZEND_FE_FETCH_RW:
445 					/* relative extended_value don't have to be changed */
446 					break;
447 			}
448 # endif
449 			opline++;
450 		}
451 #else
452 		SERIALIZE_PTR(op_array->opcodes);
453 #endif
454 
455 		if (op_array->arg_info) {
456 			zend_arg_info *p, *end;
457 			SERIALIZE_PTR(op_array->arg_info);
458 			p = op_array->arg_info;
459 			UNSERIALIZE_PTR(p);
460 			end = p + op_array->num_args;
461 			if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
462 				p--;
463 			}
464 			if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
465 				end++;
466 			}
467 			while (p < end) {
468 				if (!IS_SERIALIZED(p->name)) {
469 					SERIALIZE_STR(p->name);
470 				}
471 				if (!IS_SERIALIZED(p->class_name)) {
472 					SERIALIZE_STR(p->class_name);
473 				}
474 				p++;
475 			}
476 		}
477 
478 		if (op_array->vars) {
479 			zend_string **p, **end;
480 
481 			SERIALIZE_PTR(op_array->vars);
482 			p = op_array->vars;
483 			UNSERIALIZE_PTR(p);
484 			end = p + op_array->last_var;
485 			while (p < end) {
486 				if (!IS_SERIALIZED(*p)) {
487 					SERIALIZE_STR(*p);
488 				}
489 				p++;
490 			}
491 		}
492 
493 		SERIALIZE_STR(op_array->function_name);
494 		SERIALIZE_STR(op_array->filename);
495 		SERIALIZE_PTR(op_array->brk_cont_array);
496 		SERIALIZE_PTR(op_array->scope);
497 		SERIALIZE_STR(op_array->doc_comment);
498 		SERIALIZE_PTR(op_array->try_catch_array);
499 		SERIALIZE_PTR(op_array->prototype);
500 	}
501 }
502 
zend_file_cache_serialize_func(zval * zv,zend_persistent_script * script,zend_file_cache_metainfo * info,void * buf)503 static void zend_file_cache_serialize_func(zval                     *zv,
504                                            zend_persistent_script   *script,
505                                            zend_file_cache_metainfo *info,
506                                            void                     *buf)
507 {
508 	zend_op_array *op_array;
509 
510 	SERIALIZE_PTR(Z_PTR_P(zv));
511 	op_array = Z_PTR_P(zv);
512 	UNSERIALIZE_PTR(op_array);
513 	zend_file_cache_serialize_op_array(op_array, script, info, buf);
514 }
515 
zend_file_cache_serialize_prop_info(zval * zv,zend_persistent_script * script,zend_file_cache_metainfo * info,void * buf)516 static void zend_file_cache_serialize_prop_info(zval                     *zv,
517                                                 zend_persistent_script   *script,
518                                                 zend_file_cache_metainfo *info,
519                                                 void                     *buf)
520 {
521 	if (!IS_SERIALIZED(Z_PTR_P(zv))) {
522 		zend_property_info *prop;
523 
524 		SERIALIZE_PTR(Z_PTR_P(zv));
525 		prop = Z_PTR_P(zv);
526 		UNSERIALIZE_PTR(prop);
527 
528 		if (prop->ce && !IS_SERIALIZED(prop->ce)) {
529 			SERIALIZE_PTR(prop->ce);
530 		}
531 		if (prop->name && !IS_SERIALIZED(prop->name)) {
532 			SERIALIZE_STR(prop->name);
533 		}
534 		if (prop->doc_comment && !IS_SERIALIZED(prop->doc_comment)) {
535 			SERIALIZE_STR(prop->doc_comment);
536 		}
537 	}
538 }
539 
zend_file_cache_serialize_class(zval * zv,zend_persistent_script * script,zend_file_cache_metainfo * info,void * buf)540 static void zend_file_cache_serialize_class(zval                     *zv,
541                                             zend_persistent_script   *script,
542                                             zend_file_cache_metainfo *info,
543                                             void                     *buf)
544 {
545 	zend_class_entry *ce;
546 
547 	SERIALIZE_PTR(Z_PTR_P(zv));
548 	ce = Z_PTR_P(zv);
549 	UNSERIALIZE_PTR(ce);
550 
551 	SERIALIZE_STR(ce->name);
552 	zend_file_cache_serialize_hash(&ce->function_table, script, info, buf, zend_file_cache_serialize_func);
553 	if (ce->default_properties_table) {
554 		zval *p, *end;
555 
556 		SERIALIZE_PTR(ce->default_properties_table);
557 		p = ce->default_properties_table;
558 		UNSERIALIZE_PTR(p);
559 		end = p + ce->default_properties_count;
560 		while (p < end) {
561 			zend_file_cache_serialize_zval(p, script, info, buf);
562 			p++;
563 		}
564 	}
565 	if (ce->default_static_members_table) {
566 		zval *p, *end;
567 
568 		SERIALIZE_PTR(ce->default_static_members_table);
569 		p = ce->default_static_members_table;
570 		UNSERIALIZE_PTR(p);
571 		end = p + ce->default_static_members_count;
572 		while (p < end) {
573 			zend_file_cache_serialize_zval(p, script, info, buf);
574 			p++;
575 		}
576 	}
577 	zend_file_cache_serialize_hash(&ce->constants_table, script, info, buf, zend_file_cache_serialize_zval);
578 	SERIALIZE_STR(ZEND_CE_FILENAME(ce));
579 	SERIALIZE_STR(ZEND_CE_DOC_COMMENT(ce));
580 	zend_file_cache_serialize_hash(&ce->properties_info, script, info, buf, zend_file_cache_serialize_prop_info);
581 
582 	if (ce->trait_aliases) {
583 		zend_trait_alias **p, *q;
584 
585 		SERIALIZE_PTR(ce->trait_aliases);
586 		p = ce->trait_aliases;
587 		UNSERIALIZE_PTR(p);
588 
589 		while (*p) {
590 			SERIALIZE_PTR(*p);
591 			q = *p;
592 			UNSERIALIZE_PTR(q);
593 
594 			if (q->trait_method) {
595 				zend_trait_method_reference *m;
596 
597 				SERIALIZE_PTR(q->trait_method);
598 				m = q->trait_method;
599 				UNSERIALIZE_PTR(m);
600 
601 				if (m->method_name) {
602 					SERIALIZE_STR(m->method_name);
603 				}
604 				if (m->class_name) {
605 					SERIALIZE_STR(m->class_name);
606 				}
607 			}
608 
609 			if (q->alias) {
610 				SERIALIZE_STR(q->alias);
611 			}
612 			p++;
613 		}
614 	}
615 
616 	if (ce->trait_precedences) {
617 		zend_trait_precedence **p, *q;
618 
619 		SERIALIZE_PTR(ce->trait_precedences);
620 		p = ce->trait_precedences;
621 		UNSERIALIZE_PTR(p);
622 
623 		while (*p) {
624 			SERIALIZE_PTR(*p);
625 			q = *p;
626 			UNSERIALIZE_PTR(q);
627 
628 			if (q->trait_method) {
629 				zend_trait_method_reference *m;
630 
631 				SERIALIZE_PTR(q->trait_method);
632 				m = q->trait_method;
633 				UNSERIALIZE_PTR(m);
634 
635 				if (m->method_name) {
636 					SERIALIZE_STR(m->method_name);
637 				}
638 				if (m->class_name) {
639 					SERIALIZE_STR(m->class_name);
640 				}
641 			}
642 
643 			if (q->exclude_from_classes) {
644 				zend_string **s;
645 
646 				SERIALIZE_PTR(q->exclude_from_classes);
647 				s = (zend_string**)q->exclude_from_classes;
648 				UNSERIALIZE_PTR(s);
649 
650 				while (*s) {
651 					SERIALIZE_STR(*s);
652 					s++;
653 				}
654 			}
655 			p++;
656 		}
657 	}
658 
659 	SERIALIZE_PTR(ce->parent);
660 	SERIALIZE_PTR(ce->constructor);
661 	SERIALIZE_PTR(ce->destructor);
662 	SERIALIZE_PTR(ce->clone);
663 	SERIALIZE_PTR(ce->__get);
664 	SERIALIZE_PTR(ce->__set);
665 	SERIALIZE_PTR(ce->__call);
666 	SERIALIZE_PTR(ce->serialize_func);
667 	SERIALIZE_PTR(ce->unserialize_func);
668 	SERIALIZE_PTR(ce->__isset);
669 	SERIALIZE_PTR(ce->__unset);
670 	SERIALIZE_PTR(ce->__tostring);
671 	SERIALIZE_PTR(ce->__callstatic);
672 	SERIALIZE_PTR(ce->__debugInfo);
673 }
674 
zend_file_cache_serialize(zend_persistent_script * script,zend_file_cache_metainfo * info,void * buf)675 static void zend_file_cache_serialize(zend_persistent_script   *script,
676                                       zend_file_cache_metainfo *info,
677                                       void                     *buf)
678 {
679 	zend_persistent_script *new_script;
680 
681 	memcpy(info->magic, "OPCACHE", 8);
682 	memcpy(info->system_id, ZCG(system_id), 32);
683 	info->mem_size = script->size;
684 	info->str_size = 0;
685 	info->script_offset = (char*)script - (char*)script->mem;
686 	info->timestamp = script->timestamp;
687 
688 	memcpy(buf, script->mem, script->size);
689 
690 	new_script = (zend_persistent_script*)((char*)buf + info->script_offset);
691 	SERIALIZE_STR(new_script->full_path);
692 
693 	zend_file_cache_serialize_hash(&new_script->class_table, script, info, buf, zend_file_cache_serialize_class);
694 	zend_file_cache_serialize_hash(&new_script->function_table, script, info, buf, zend_file_cache_serialize_func);
695 	zend_file_cache_serialize_op_array(&new_script->main_op_array, script, info, buf);
696 
697 	SERIALIZE_PTR(new_script->arena_mem);
698 	new_script->mem = NULL;
699 }
700 
zend_file_cache_get_bin_file_path(zend_string * script_path)701 static char *zend_file_cache_get_bin_file_path(zend_string *script_path)
702 {
703 	size_t len;
704 	char *filename;
705 
706 	len = strlen(ZCG(accel_directives).file_cache);
707 	filename = emalloc(len + 33 + ZSTR_LEN(script_path) + sizeof(SUFFIX));
708 	memcpy(filename, ZCG(accel_directives).file_cache, len);
709 #ifndef ZEND_WIN32
710 	filename[len] = '/';
711 	memcpy(filename + len + 1, ZCG(system_id), 32);
712 	memcpy(filename + len + 33, ZSTR_VAL(script_path), ZSTR_LEN(script_path));
713 	memcpy(filename + len + 33 + ZSTR_LEN(script_path), SUFFIX, sizeof(SUFFIX));
714 #else
715 	filename[len] = '\\';
716 	memcpy(filename + len + 1, ZCG(system_id), 32);
717 	if (ZSTR_LEN(script_path) >= 2 && ':' == ZSTR_VAL(script_path)[1]) {
718 		/* local fs */
719 		*(filename + len + 33) = '\\';
720 		*(filename + len + 34) = ZSTR_VAL(script_path)[0];
721 		memcpy(filename + len + 35, ZSTR_VAL(script_path) + 2, ZSTR_LEN(script_path) - 2);
722 		memcpy(filename + len + 35 + ZSTR_LEN(script_path) - 2, SUFFIX, sizeof(SUFFIX));
723 	} else {
724 		/* network path */
725 		memcpy(filename + len + 33, ZSTR_VAL(script_path), ZSTR_LEN(script_path));
726 		memcpy(filename + len + 33 + ZSTR_LEN(script_path), SUFFIX, sizeof(SUFFIX));
727 	}
728 #endif
729 
730 	return filename;
731 }
732 
zend_file_cache_script_store(zend_persistent_script * script,int in_shm)733 int zend_file_cache_script_store(zend_persistent_script *script, int in_shm)
734 {
735 	int fd;
736 	char *filename;
737 	zend_file_cache_metainfo info;
738 #ifdef HAVE_SYS_UIO_H
739 	struct iovec vec[3];
740 #endif
741 	void *mem, *buf;
742 
743 	filename = zend_file_cache_get_bin_file_path(script->full_path);
744 
745 	if (zend_file_cache_mkdir(filename, strlen(ZCG(accel_directives).file_cache)) != SUCCESS) {
746 		zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot create directory for file '%s'\n", filename);
747 		efree(filename);
748 		return FAILURE;
749 	}
750 
751 #ifndef ZEND_WIN32
752 	fd = open(filename, O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
753 #else
754 	fd = open(filename, O_CREAT | O_EXCL | O_RDWR | O_BINARY, _S_IREAD | _S_IWRITE);
755 #endif
756 	if (fd < 0) {
757 		if (errno != EEXIST) {
758 			zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot create file '%s'\n", filename);
759 		}
760 		efree(filename);
761 		return FAILURE;
762 	}
763 
764 	if (zend_file_cache_flock(fd, LOCK_EX) != 0) {
765 		close(fd);
766 		efree(filename);
767 		return FAILURE;
768 	}
769 
770 #ifdef __SSE2__
771 	/* Align to 64-byte boundary */
772 	mem = emalloc(script->size + 64);
773 	buf = (void*)(((zend_uintptr_t)mem + 63L) & ~63L);
774 #else
775 	mem = buf = emalloc(script->size);
776 #endif
777 
778 	ZCG(mem) = zend_string_alloc(4096 - (_ZSTR_HEADER_SIZE + 1), 0);
779 
780 	zend_shared_alloc_init_xlat_table();
781 	if (!in_shm) {
782 		script->corrupted = 1; /* used to check if script restored to SHM or process memory */
783 	}
784 	zend_file_cache_serialize(script, &info, buf);
785 	if (!in_shm) {
786 		script->corrupted = 0;
787 	}
788 	zend_shared_alloc_destroy_xlat_table();
789 
790 	info.checksum = zend_adler32(ADLER32_INIT, buf, script->size);
791 	info.checksum = zend_adler32(info.checksum, (signed char*)ZSTR_VAL((zend_string*)ZCG(mem)), info.str_size);
792 
793 #ifdef HAVE_SYS_UIO_H
794 	vec[0].iov_base = &info;
795 	vec[0].iov_len = sizeof(info);
796 	vec[1].iov_base = buf;
797 	vec[1].iov_len = script->size;
798 	vec[2].iov_base = ZSTR_VAL((zend_string*)ZCG(mem));
799 	vec[2].iov_len = info.str_size;
800 
801 	if (writev(fd, vec, 3) != (ssize_t)(sizeof(info) + script->size + info.str_size)) {
802 		zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot write to file '%s'\n", filename);
803 		zend_string_release((zend_string*)ZCG(mem));
804 		close(fd);
805 		efree(mem);
806 		unlink(filename);
807 		efree(filename);
808 		return FAILURE;
809 	}
810 #else
811 	if (ZEND_LONG_MAX < (zend_long)(sizeof(info) + script->size + info.str_size) ||
812 		write(fd, &info, sizeof(info)) != sizeof(info) ||
813 		write(fd, buf, script->size) != script->size ||
814 		write(fd, ((zend_string*)ZCG(mem))->val, info.str_size) != info.str_size
815 		) {
816 		zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot write to file '%s'\n", filename);
817 		zend_string_release((zend_string*)ZCG(mem));
818 		close(fd);
819 		efree(mem);
820 		unlink(filename);
821 		efree(filename);
822 		return FAILURE;
823 	}
824 #endif
825 
826 	zend_string_release((zend_string*)ZCG(mem));
827 	efree(mem);
828 	if (zend_file_cache_flock(fd, LOCK_UN) != 0) {
829 		zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s'\n", filename);
830 	}
831 	close(fd);
832 	efree(filename);
833 
834 	return SUCCESS;
835 }
836 
zend_file_cache_unserialize_hash(HashTable * ht,zend_persistent_script * script,void * buf,unserialize_callback_t func,dtor_func_t dtor)837 static void zend_file_cache_unserialize_hash(HashTable               *ht,
838                                              zend_persistent_script  *script,
839                                              void                    *buf,
840                                              unserialize_callback_t   func,
841                                              dtor_func_t              dtor)
842 {
843 	Bucket *p, *end;
844 
845 	ht->pDestructor = dtor;
846 	if (!(ht->u.flags & HASH_FLAG_INITIALIZED)) {
847 		HT_SET_DATA_ADDR(ht, &uninitialized_bucket);
848 		return;
849 	}
850 	if (IS_UNSERIALIZED(ht->arData)) {
851 		return;
852 	}
853 	UNSERIALIZE_PTR(ht->arData);
854 	p = ht->arData;
855 	end = p + ht->nNumUsed;
856 	while (p < end) {
857 		if (Z_TYPE(p->val) != IS_UNDEF) {
858 			UNSERIALIZE_STR(p->key);
859 			func(&p->val, script, buf);
860 		}
861 		p++;
862 	}
863 }
864 
zend_file_cache_unserialize_ast(zend_ast * ast,zend_persistent_script * script,void * buf)865 static zend_ast *zend_file_cache_unserialize_ast(zend_ast                *ast,
866                                                  zend_persistent_script  *script,
867                                                  void                    *buf)
868 {
869 	uint32_t i;
870 
871 	UNSERIALIZE_PTR(ast);
872 
873 	if (ast->kind == ZEND_AST_ZVAL) {
874 		zend_file_cache_unserialize_zval(&((zend_ast_zval*)ast)->val, script, buf);
875 	} else if (zend_ast_is_list(ast)) {
876 		zend_ast_list *list = zend_ast_get_list(ast);
877 		for (i = 0; i < list->children; i++) {
878 			if (list->child[i]) {
879 				list->child[i] = zend_file_cache_unserialize_ast(list->child[i], script, buf);
880 			}
881 		}
882 	} else {
883 		uint32_t children = zend_ast_get_num_children(ast);
884 		for (i = 0; i < children; i++) {
885 			if (ast->child[i]) {
886 				ast->child[i] = zend_file_cache_unserialize_ast(ast->child[i], script, buf);
887 			}
888 		}
889 	}
890 	return ast;
891 }
892 
zend_file_cache_unserialize_zval(zval * zv,zend_persistent_script * script,void * buf)893 static void zend_file_cache_unserialize_zval(zval                    *zv,
894                                              zend_persistent_script  *script,
895                                              void                    *buf)
896 {
897 	switch (Z_TYPE_P(zv)) {
898 		case IS_STRING:
899 		case IS_CONSTANT:
900 			if (!IS_UNSERIALIZED(Z_STR_P(zv))) {
901 				UNSERIALIZE_STR(Z_STR_P(zv));
902 			}
903 			break;
904 		case IS_ARRAY:
905 			if (!IS_UNSERIALIZED(Z_ARR_P(zv))) {
906 				HashTable *ht;
907 
908 				UNSERIALIZE_PTR(Z_ARR_P(zv));
909 				ht = Z_ARR_P(zv);
910 				zend_file_cache_unserialize_hash(ht,
911 						script, buf, zend_file_cache_unserialize_zval, ZVAL_PTR_DTOR);
912 			}
913 			break;
914 		case IS_REFERENCE:
915 			if (!IS_UNSERIALIZED(Z_REF_P(zv))) {
916 				zend_reference *ref;
917 
918 				UNSERIALIZE_PTR(Z_REF_P(zv));
919 				ref = Z_REF_P(zv);
920 				zend_file_cache_unserialize_zval(&ref->val, script, buf);
921 			}
922 			break;
923 		case IS_CONSTANT_AST:
924 			if (!IS_UNSERIALIZED(Z_AST_P(zv))) {
925 				zend_ast_ref *ast;
926 
927 				UNSERIALIZE_PTR(Z_AST_P(zv));
928 				ast = Z_AST_P(zv);
929 				if (!IS_UNSERIALIZED(ast->ast)) {
930 					ast->ast = zend_file_cache_unserialize_ast(ast->ast, script, buf);
931 				}
932 			}
933 			break;
934 	}
935 }
936 
zend_file_cache_unserialize_op_array(zend_op_array * op_array,zend_persistent_script * script,void * buf)937 static void zend_file_cache_unserialize_op_array(zend_op_array           *op_array,
938                                                  zend_persistent_script  *script,
939                                                  void                    *buf)
940 {
941 	if (op_array->static_variables && !IS_UNSERIALIZED(op_array->static_variables)) {
942 		HashTable *ht;
943 
944 		UNSERIALIZE_PTR(op_array->static_variables);
945 		ht = op_array->static_variables;
946 		zend_file_cache_unserialize_hash(ht,
947 				script, buf, zend_file_cache_unserialize_zval, ZVAL_PTR_DTOR);
948 	}
949 
950 	if (op_array->refcount) {
951 		op_array->refcount = NULL;
952 		UNSERIALIZE_PTR(op_array->literals);
953 		UNSERIALIZE_PTR(op_array->opcodes);
954 		UNSERIALIZE_PTR(op_array->arg_info);
955 		UNSERIALIZE_PTR(op_array->vars);
956 		UNSERIALIZE_STR(op_array->function_name);
957 		UNSERIALIZE_STR(op_array->filename);
958 		UNSERIALIZE_PTR(op_array->brk_cont_array);
959 		UNSERIALIZE_PTR(op_array->scope);
960 		UNSERIALIZE_STR(op_array->doc_comment);
961 		UNSERIALIZE_PTR(op_array->try_catch_array);
962 		UNSERIALIZE_PTR(op_array->prototype);
963 		return;
964 	}
965 
966 	if (op_array->literals && !IS_UNSERIALIZED(op_array->literals)) {
967 		zval *p, *end;
968 
969 		UNSERIALIZE_PTR(op_array->literals);
970 		p = op_array->literals;
971 		end = p + op_array->last_literal;
972 		while (p < end) {
973 			zend_file_cache_unserialize_zval(p, script, buf);
974 			p++;
975 		}
976 	}
977 
978 	if (!IS_UNSERIALIZED(op_array->opcodes)) {
979 		zend_op *opline, *end;
980 
981 		UNSERIALIZE_PTR(op_array->opcodes);
982 		opline = op_array->opcodes;
983 		end = opline + op_array->last;
984 		while (opline < end) {
985 # if ZEND_USE_ABS_CONST_ADDR
986 			if (ZEND_OP1_TYPE(opline) == IS_CONST) {
987 				UNSERIALIZE_PTR(opline->op1.zv);
988 			}
989 			if (ZEND_OP2_TYPE(opline) == IS_CONST) {
990 				UNSERIALIZE_PTR(opline->op2.zv);
991 			}
992 # endif
993 # if ZEND_USE_ABS_JMP_ADDR
994 			switch (opline->opcode) {
995 				case ZEND_JMP:
996 				case ZEND_FAST_CALL:
997 				case ZEND_DECLARE_ANON_CLASS:
998 				case ZEND_DECLARE_ANON_INHERITED_CLASS:
999 					UNSERIALIZE_PTR(opline->op1.jmp_addr);
1000 					break;
1001 				case ZEND_JMPZNZ:
1002 					/* relative extended_value don't have to be changed */
1003 					/* break omitted intentionally */
1004 				case ZEND_JMPZ:
1005 				case ZEND_JMPNZ:
1006 				case ZEND_JMPZ_EX:
1007 				case ZEND_JMPNZ_EX:
1008 				case ZEND_JMP_SET:
1009 				case ZEND_COALESCE:
1010 				case ZEND_NEW:
1011 				case ZEND_FE_RESET_R:
1012 				case ZEND_FE_RESET_RW:
1013 				case ZEND_ASSERT_CHECK:
1014 					UNSERIALIZE_PTR(opline->op2.jmp_addr);
1015 					break;
1016 				case ZEND_FE_FETCH_R:
1017 				case ZEND_FE_FETCH_RW:
1018 					/* relative extended_value don't have to be changed */
1019 					break;
1020 			}
1021 # endif
1022 			ZEND_VM_SET_OPCODE_HANDLER(opline);
1023 			opline++;
1024 		}
1025 
1026 		if (op_array->arg_info) {
1027 			zend_arg_info *p, *end;
1028 			UNSERIALIZE_PTR(op_array->arg_info);
1029 			p = op_array->arg_info;
1030 			end = p + op_array->num_args;
1031 			if (op_array->fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
1032 				p--;
1033 			}
1034 			if (op_array->fn_flags & ZEND_ACC_VARIADIC) {
1035 				end++;
1036 			}
1037 			while (p < end) {
1038 				if (!IS_UNSERIALIZED(p->name)) {
1039 					UNSERIALIZE_STR(p->name);
1040 				}
1041 				if (!IS_UNSERIALIZED(p->class_name)) {
1042 					UNSERIALIZE_STR(p->class_name);
1043 				}
1044 				p++;
1045 			}
1046 		}
1047 
1048 		if (op_array->vars) {
1049 			zend_string **p, **end;
1050 
1051 			UNSERIALIZE_PTR(op_array->vars);
1052 			p = op_array->vars;
1053 			end = p + op_array->last_var;
1054 			while (p < end) {
1055 				if (!IS_UNSERIALIZED(*p)) {
1056 					UNSERIALIZE_STR(*p);
1057 				}
1058 				p++;
1059 			}
1060 		}
1061 
1062 		UNSERIALIZE_STR(op_array->function_name);
1063 		UNSERIALIZE_STR(op_array->filename);
1064 		UNSERIALIZE_PTR(op_array->brk_cont_array);
1065 		UNSERIALIZE_PTR(op_array->scope);
1066 		UNSERIALIZE_STR(op_array->doc_comment);
1067 		UNSERIALIZE_PTR(op_array->try_catch_array);
1068 		UNSERIALIZE_PTR(op_array->prototype);
1069 	}
1070 }
1071 
zend_file_cache_unserialize_func(zval * zv,zend_persistent_script * script,void * buf)1072 static void zend_file_cache_unserialize_func(zval                    *zv,
1073                                              zend_persistent_script  *script,
1074                                              void                    *buf)
1075 {
1076 	zend_op_array *op_array;
1077 
1078 	UNSERIALIZE_PTR(Z_PTR_P(zv));
1079 	op_array = Z_PTR_P(zv);
1080 	zend_file_cache_unserialize_op_array(op_array, script, buf);
1081 }
1082 
zend_file_cache_unserialize_prop_info(zval * zv,zend_persistent_script * script,void * buf)1083 static void zend_file_cache_unserialize_prop_info(zval                    *zv,
1084                                                   zend_persistent_script  *script,
1085                                                   void                    *buf)
1086 {
1087 	if (!IS_UNSERIALIZED(Z_PTR_P(zv))) {
1088 		zend_property_info *prop;
1089 
1090 		UNSERIALIZE_PTR(Z_PTR_P(zv));
1091 		prop = Z_PTR_P(zv);
1092 
1093 		if (prop->ce && !IS_UNSERIALIZED(prop->ce)) {
1094 			UNSERIALIZE_PTR(prop->ce);
1095 		}
1096 		if (prop->name && !IS_UNSERIALIZED(prop->name)) {
1097 			UNSERIALIZE_STR(prop->name);
1098 		}
1099 		if (prop->doc_comment && !IS_UNSERIALIZED(prop->doc_comment)) {
1100 			UNSERIALIZE_STR(prop->doc_comment);
1101 		}
1102 	}
1103 }
1104 
zend_file_cache_unserialize_class(zval * zv,zend_persistent_script * script,void * buf)1105 static void zend_file_cache_unserialize_class(zval                    *zv,
1106                                               zend_persistent_script  *script,
1107                                               void                    *buf)
1108 {
1109 	zend_class_entry *ce;
1110 
1111 	UNSERIALIZE_PTR(Z_PTR_P(zv));
1112 	ce = Z_PTR_P(zv);
1113 
1114 	UNSERIALIZE_STR(ce->name);
1115 	zend_file_cache_unserialize_hash(&ce->function_table,
1116 			script, buf, zend_file_cache_unserialize_func, ZEND_FUNCTION_DTOR);
1117 	if (ce->default_properties_table) {
1118 		zval *p, *end;
1119 
1120 		UNSERIALIZE_PTR(ce->default_properties_table);
1121 		p = ce->default_properties_table;
1122 		end = p + ce->default_properties_count;
1123 		while (p < end) {
1124 			zend_file_cache_unserialize_zval(p, script, buf);
1125 			p++;
1126 		}
1127 	}
1128 	if (ce->default_static_members_table) {
1129 		zval *p, *end;
1130 
1131 		UNSERIALIZE_PTR(ce->default_static_members_table);
1132 		p = ce->default_static_members_table;
1133 		end = p + ce->default_static_members_count;
1134 		while (p < end) {
1135 			zend_file_cache_unserialize_zval(p, script, buf);
1136 			p++;
1137 		}
1138 	}
1139 	zend_file_cache_unserialize_hash(&ce->constants_table,
1140 			script, buf, zend_file_cache_unserialize_zval, NULL);
1141 	UNSERIALIZE_STR(ZEND_CE_FILENAME(ce));
1142 	UNSERIALIZE_STR(ZEND_CE_DOC_COMMENT(ce));
1143 	zend_file_cache_unserialize_hash(&ce->properties_info,
1144 			script, buf, zend_file_cache_unserialize_prop_info, ZVAL_PTR_DTOR);
1145 
1146 	if (ce->trait_aliases) {
1147 		zend_trait_alias **p, *q;
1148 
1149 		UNSERIALIZE_PTR(ce->trait_aliases);
1150 		p = ce->trait_aliases;
1151 
1152 		while (*p) {
1153 			UNSERIALIZE_PTR(*p);
1154 			q = *p;
1155 
1156 			if (q->trait_method) {
1157 				zend_trait_method_reference *m;
1158 
1159 				UNSERIALIZE_PTR(q->trait_method);
1160 				m = q->trait_method;
1161 
1162 				if (m->method_name) {
1163 					UNSERIALIZE_STR(m->method_name);
1164 				}
1165 				if (m->class_name) {
1166 					UNSERIALIZE_STR(m->class_name);
1167 				}
1168 			}
1169 
1170 			if (q->alias) {
1171 				UNSERIALIZE_STR(q->alias);
1172 			}
1173 			p++;
1174 		}
1175 	}
1176 
1177 	if (ce->trait_precedences) {
1178 		zend_trait_precedence **p, *q;
1179 
1180 		UNSERIALIZE_PTR(ce->trait_precedences);
1181 		p = ce->trait_precedences;
1182 
1183 		while (*p) {
1184 			UNSERIALIZE_PTR(*p);
1185 			q = *p;
1186 
1187 			if (q->trait_method) {
1188 				zend_trait_method_reference *m;
1189 
1190 				UNSERIALIZE_PTR(q->trait_method);
1191 				m = q->trait_method;
1192 
1193 				if (m->method_name) {
1194 					UNSERIALIZE_STR(m->method_name);
1195 				}
1196 				if (m->class_name) {
1197 					UNSERIALIZE_STR(m->class_name);
1198 				}
1199 			}
1200 
1201 			if (q->exclude_from_classes) {
1202 				zend_string **s;
1203 
1204 				UNSERIALIZE_PTR(q->exclude_from_classes);
1205 				s = (zend_string**)q->exclude_from_classes;
1206 
1207 				while (*s) {
1208 					UNSERIALIZE_STR(*s);
1209 					s++;
1210 				}
1211 			}
1212 			p++;
1213 		}
1214 	}
1215 
1216 	UNSERIALIZE_PTR(ce->parent);
1217 	UNSERIALIZE_PTR(ce->constructor);
1218 	UNSERIALIZE_PTR(ce->destructor);
1219 	UNSERIALIZE_PTR(ce->clone);
1220 	UNSERIALIZE_PTR(ce->__get);
1221 	UNSERIALIZE_PTR(ce->__set);
1222 	UNSERIALIZE_PTR(ce->__call);
1223 	UNSERIALIZE_PTR(ce->serialize_func);
1224 	UNSERIALIZE_PTR(ce->unserialize_func);
1225 	UNSERIALIZE_PTR(ce->__isset);
1226 	UNSERIALIZE_PTR(ce->__unset);
1227 	UNSERIALIZE_PTR(ce->__tostring);
1228 	UNSERIALIZE_PTR(ce->__callstatic);
1229 	UNSERIALIZE_PTR(ce->__debugInfo);
1230 
1231 	if (UNEXPECTED((ce->ce_flags & ZEND_ACC_ANON_CLASS))) {
1232 		ce->serialize = zend_class_serialize_deny;
1233 		ce->unserialize = zend_class_unserialize_deny;
1234 	}
1235 }
1236 
zend_file_cache_unserialize(zend_persistent_script * script,void * buf)1237 static void zend_file_cache_unserialize(zend_persistent_script  *script,
1238                                         void                    *buf)
1239 {
1240 	script->mem = buf;
1241 
1242 	UNSERIALIZE_STR(script->full_path);
1243 
1244 	zend_file_cache_unserialize_hash(&script->class_table,
1245 			script, buf, zend_file_cache_unserialize_class, ZEND_CLASS_DTOR);
1246 	zend_file_cache_unserialize_hash(&script->function_table,
1247 			script, buf, zend_file_cache_unserialize_func, ZEND_FUNCTION_DTOR);
1248 	zend_file_cache_unserialize_op_array(&script->main_op_array, script, buf);
1249 
1250 	UNSERIALIZE_PTR(script->arena_mem);
1251 }
1252 
zend_file_cache_script_load(zend_file_handle * file_handle)1253 zend_persistent_script *zend_file_cache_script_load(zend_file_handle *file_handle)
1254 {
1255 	zend_string *full_path = file_handle->opened_path;
1256 	int fd;
1257 	char *filename;
1258 	zend_persistent_script *script;
1259 	zend_file_cache_metainfo info;
1260 	zend_accel_hash_entry *bucket;
1261 	void *mem, *checkpoint, *buf;
1262 	int cache_it = 1;
1263 	int ok;
1264 
1265 	if (!full_path) {
1266 		return NULL;
1267 	}
1268 	filename = zend_file_cache_get_bin_file_path(full_path);
1269 
1270 	fd = open(filename, O_RDONLY | O_BINARY);
1271 	if (fd < 0) {
1272 		efree(filename);
1273 		return NULL;
1274 	}
1275 
1276 	if (zend_file_cache_flock(fd, LOCK_SH) != 0) {
1277 		close(fd);
1278 		efree(filename);
1279 		return NULL;
1280 	}
1281 
1282 	if (read(fd, &info, sizeof(info)) != sizeof(info)) {
1283 		zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s'\n", filename);
1284 		zend_file_cache_flock(fd, LOCK_UN);
1285 		close(fd);
1286 		unlink(filename);
1287 		efree(filename);
1288 		return NULL;
1289 	}
1290 
1291 	/* verify header */
1292 	if (memcmp(info.magic, "OPCACHE", 8) != 0) {
1293 		zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (wrong header)\n", filename);
1294 		zend_file_cache_flock(fd, LOCK_UN);
1295 		close(fd);
1296 		unlink(filename);
1297 		efree(filename);
1298 		return NULL;
1299 	}
1300 	if (memcmp(info.system_id, ZCG(system_id), 32) != 0) {
1301 		zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s' (wrong \"system_id\")\n", filename);
1302 		zend_file_cache_flock(fd, LOCK_UN);
1303 		close(fd);
1304 		unlink(filename);
1305 		efree(filename);
1306 		return NULL;
1307 	}
1308 
1309 	/* verify timestamp */
1310 	if (ZCG(accel_directives).validate_timestamps &&
1311 	    zend_get_file_handle_timestamp(file_handle, NULL) != info.timestamp) {
1312 		if (zend_file_cache_flock(fd, LOCK_UN) != 0) {
1313 			zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s'\n", filename);
1314 		}
1315 		close(fd);
1316 		unlink(filename);
1317 		efree(filename);
1318 		return NULL;
1319 	}
1320 
1321 	checkpoint = zend_arena_checkpoint(CG(arena));
1322 #ifdef __SSE2__
1323 	/* Align to 64-byte boundary */
1324 	mem = zend_arena_alloc(&CG(arena), info.mem_size + info.str_size + 64);
1325 	mem = (void*)(((zend_uintptr_t)mem + 63L) & ~63L);
1326 #else
1327 	mem = zend_arena_alloc(&CG(arena), info.mem_size + info.str_size);
1328 #endif
1329 
1330 	if (read(fd, mem, info.mem_size + info.str_size) != (ssize_t)(info.mem_size + info.str_size)) {
1331 		zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot read from file '%s'\n", filename);
1332 		zend_file_cache_flock(fd, LOCK_UN);
1333 		close(fd);
1334 		unlink(filename);
1335 		zend_arena_release(&CG(arena), checkpoint);
1336 		efree(filename);
1337 		return NULL;
1338 	}
1339 	if (zend_file_cache_flock(fd, LOCK_UN) != 0) {
1340 		zend_accel_error(ACCEL_LOG_WARNING, "opcache cannot unlock file '%s'\n", filename);
1341 	}
1342 	close(fd);
1343 
1344 	/* verify checksum */
1345 	if (ZCG(accel_directives).file_cache_consistency_checks &&
1346 	    zend_adler32(ADLER32_INIT, mem, info.mem_size + info.str_size) != info.checksum) {
1347 		zend_accel_error(ACCEL_LOG_WARNING, "corrupted file '%s'\n", filename);
1348 		unlink(filename);
1349 		zend_arena_release(&CG(arena), checkpoint);
1350 		efree(filename);
1351 		return NULL;
1352 	}
1353 
1354 	if (!ZCG(accel_directives).file_cache_only &&
1355 	    !ZCSG(restart_in_progress) &&
1356 		!ZSMMG(memory_exhausted) &&
1357 	    accelerator_shm_read_lock() == SUCCESS) {
1358 		/* exclusive lock */
1359 		zend_shared_alloc_lock();
1360 
1361 		/* Check if we still need to put the file into the cache (may be it was
1362 		 * already stored by another process. This final check is done under
1363 		 * exclusive lock) */
1364 		bucket = zend_accel_hash_find_entry(&ZCSG(hash), full_path);
1365 		if (bucket) {
1366 			script = (zend_persistent_script *)bucket->data;
1367 			if (!script->corrupted) {
1368 				zend_shared_alloc_unlock();
1369 				zend_arena_release(&CG(arena), checkpoint);
1370 				efree(filename);
1371 				return script;
1372 			}
1373 		}
1374 
1375 		if (zend_accel_hash_is_full(&ZCSG(hash))) {
1376 			zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1377 			ZSMMG(memory_exhausted) = 1;
1378 			zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
1379 			zend_shared_alloc_unlock();
1380 			goto use_process_mem;
1381 		}
1382 
1383 #ifdef __SSE2__
1384 		/* Align to 64-byte boundary */
1385 		buf = zend_shared_alloc(info.mem_size + 64);
1386 		buf = (void*)(((zend_uintptr_t)buf + 63L) & ~63L);
1387 #else
1388 		buf = zend_shared_alloc(info.mem_size);
1389 #endif
1390 
1391 		if (!buf) {
1392 			zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
1393 			zend_shared_alloc_unlock();
1394 			goto use_process_mem;
1395 		}
1396 		memcpy(buf, mem, info.mem_size);
1397 	} else {
1398 use_process_mem:
1399 		buf = mem;
1400 		cache_it = 0;
1401 	}
1402 
1403 	ZCG(mem) = ((char*)mem + info.mem_size);
1404 	script = (zend_persistent_script*)((char*)buf + info.script_offset);
1405 	script->corrupted = !cache_it; /* used to check if script restored to SHM or process memory */
1406 
1407 	ok = 1;
1408 	zend_try {
1409 		zend_file_cache_unserialize(script, buf);
1410 	} zend_catch {
1411 		ok = 0;
1412 	} zend_end_try();
1413 	if (!ok) {
1414 		if (cache_it) {
1415 			zend_shared_alloc_unlock();
1416 			goto use_process_mem;
1417 		} else {
1418 			zend_arena_release(&CG(arena), checkpoint);
1419 			efree(filename);
1420 			return NULL;
1421 		}
1422 	}
1423 
1424 	script->corrupted = 0;
1425 
1426 	if (cache_it) {
1427 		script->dynamic_members.checksum = zend_accel_script_checksum(script);
1428 		script->dynamic_members.last_used = ZCG(request_time);
1429 
1430 		zend_accel_hash_update(&ZCSG(hash), ZSTR_VAL(script->full_path), ZSTR_LEN(script->full_path), 0, script);
1431 
1432 		zend_shared_alloc_unlock();
1433 		zend_arena_release(&CG(arena), checkpoint);
1434 	}
1435 	efree(filename);
1436 
1437 	return script;
1438 }
1439 
zend_file_cache_invalidate(zend_string * full_path)1440 void zend_file_cache_invalidate(zend_string *full_path)
1441 {
1442 	char *filename;
1443 
1444 	filename = zend_file_cache_get_bin_file_path(full_path);
1445 
1446 	unlink(filename);
1447 	efree(filename);
1448 }
1449 
1450 #endif /* HAVE_OPCACHE_FILE_CACHE */
1451