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