Coverage Report

Created: 2025-09-27 06:26

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