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