Coverage Report

Created: 2026-09-14 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/opcache/ZendAccelerator.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Zend OPcache                                                         |
4
   +----------------------------------------------------------------------+
5
   | Copyright © The PHP Group and Contributors.                          |
6
   +----------------------------------------------------------------------+
7
   | This source file is subject to the Modified BSD License that is      |
8
   | bundled with this package in the file LICENSE, and is available      |
9
   | through the World Wide Web at <https://www.php.net/license/>.        |
10
   |                                                                      |
11
   | SPDX-License-Identifier: BSD-3-Clause                                |
12
   +----------------------------------------------------------------------+
13
   | Authors: Andi Gutmans <andi@php.net>                                 |
14
   |          Zeev Suraski <zeev@php.net>                                 |
15
   |          Stanislav Malyshev <stas@zend.com>                          |
16
   |          Dmitry Stogov <dmitry@php.net>                              |
17
   +----------------------------------------------------------------------+
18
*/
19
20
#include "main/php.h"
21
#include "main/php_globals.h"
22
#include "zend.h"
23
#include "zend_extensions.h"
24
#include "zend_compile.h"
25
#include "ZendAccelerator.h"
26
#include "zend_modules.h"
27
#include "zend_operators.h"
28
#include "zend_persist.h"
29
#include "zend_portability.h"
30
#include "zend_shared_alloc.h"
31
#include "zend_accelerator_module.h"
32
#include "zend_accelerator_blacklist.h"
33
#include "zend_list.h"
34
#include "zend_execute.h"
35
#include "zend_vm.h"
36
#include "zend_inheritance.h"
37
#include "zend_exceptions.h"
38
#include "zend_mmap.h"
39
#include "zend_observer.h"
40
#include "main/php_main.h"
41
#include "main/SAPI.h"
42
#include "main/php_streams.h"
43
#include "main/php_open_temporary_file.h"
44
#include "zend_API.h"
45
#include "zend_ini.h"
46
#include "zend_virtual_cwd.h"
47
#include "zend_accelerator_util_funcs.h"
48
#include "zend_accelerator_hash.h"
49
#include "zend_file_cache.h"
50
#include "zend_system_id.h"
51
#include "ext/pcre/php_pcre.h"
52
#include "ext/standard/basic_functions.h"
53
#include "zend_vm_opcodes.h"
54
55
#ifdef ZEND_WIN32
56
# include "ext/standard/md5.h"
57
#endif
58
59
#ifdef HAVE_JIT
60
# include "jit/zend_jit.h"
61
# include "Optimizer/zend_func_info.h"
62
# include "Optimizer/zend_call_graph.h"
63
#endif
64
65
#ifndef ZEND_WIN32
66
#include  <netdb.h>
67
#endif
68
69
#ifdef ZEND_WIN32
70
typedef int uid_t;
71
typedef int gid_t;
72
#include <io.h>
73
#include <lmcons.h>
74
#endif
75
76
#ifndef ZEND_WIN32
77
# include <sys/time.h>
78
#else
79
# include <process.h>
80
#endif
81
82
#ifdef HAVE_UNISTD_H
83
# include <unistd.h>
84
#endif
85
#include <fcntl.h>
86
#include <signal.h>
87
#include <time.h>
88
89
#ifndef ZEND_WIN32
90
# include <sys/types.h>
91
# include <sys/wait.h>
92
# include <pwd.h>
93
# include <grp.h>
94
#endif
95
96
#include <sys/stat.h>
97
#include <errno.h>
98
99
#ifdef __AVX__
100
#include <immintrin.h>
101
#endif
102
103
#include "zend_simd.h"
104
105
static zend_extension opcache_extension_entry;
106
107
#ifndef ZTS
108
zend_accel_globals accel_globals;
109
#else
110
int accel_globals_id;
111
size_t accel_globals_offset;
112
#endif
113
114
/* Points to the structure shared across all PHP processes */
115
zend_accel_shared_globals *accel_shared_globals = NULL;
116
117
/* true globals, no need for thread safety */
118
#ifdef ZEND_WIN32
119
char accel_uname_id[32];
120
#endif
121
bool accel_startup_ok = false;
122
static const char *zps_failure_reason = NULL;
123
const char *zps_api_failure_reason = NULL;
124
bool file_cache_only = false;  /* process uses file cache only */
125
#if ENABLE_FILE_CACHE_FALLBACK
126
bool fallback_process = false; /* process uses file cache fallback */
127
#endif
128
129
static zend_op_array *(*accelerator_orig_compile_file)(zend_file_handle *file_handle, int type);
130
static zend_class_entry* (*accelerator_orig_inheritance_cache_get)(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces);
131
static zend_class_entry* (*accelerator_orig_inheritance_cache_add)(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies);
132
static zend_result (*accelerator_orig_zend_stream_open_function)(zend_file_handle *handle );
133
static zend_string *(*accelerator_orig_zend_resolve_path)(zend_string *filename);
134
static zif_handler orig_chdir = NULL;
135
static ZEND_INI_MH((*orig_include_path_on_modify)) = NULL;
136
static zend_result (*orig_post_startup_cb)(void);
137
138
static zend_result accel_post_startup(void);
139
static zend_result accel_finish_startup(void);
140
141
#ifndef ZEND_WIN32
142
# define PRELOAD_SUPPORT
143
#endif
144
145
#ifdef PRELOAD_SUPPORT
146
static void preload_shutdown(void);
147
static void preload_activate(void);
148
static void preload_restart(void);
149
#endif
150
151
#ifdef ZEND_WIN32
152
# define INCREMENT(v) InterlockedIncrement64(&ZCSG(v))
153
# define DECREMENT(v) InterlockedDecrement64(&ZCSG(v))
154
# define LOCKVAL(v)   (ZCSG(v))
155
#endif
156
157
16
#define ZCG_KEY_LEN (MAXPATHLEN * 8)
158
159
/**
160
 * Clear AVX/SSE2-aligned memory.
161
 */
162
static void bzero_aligned(void *mem, size_t size)
163
61.8k
{
164
61.8k
#if defined(__x86_64__)
165
61.8k
  memset(mem, 0, size);
166
#elif defined(__AVX__)
167
  char *p = (char*)mem;
168
  char *end = p + size;
169
  __m256i ymm0 = _mm256_setzero_si256();
170
171
  while (p < end) {
172
    _mm256_store_si256((__m256i*)p, ymm0);
173
    _mm256_store_si256((__m256i*)(p+32), ymm0);
174
    p += 64;
175
  }
176
#elif defined(XSSE2)
177
  char *p = (char*)mem;
178
  char *end = p + size;
179
  __m128i xmm0 = _mm_setzero_si128();
180
181
  while (p < end) {
182
    _mm_store_si128((__m128i*)p, xmm0);
183
    _mm_store_si128((__m128i*)(p+16), xmm0);
184
    _mm_store_si128((__m128i*)(p+32), xmm0);
185
    _mm_store_si128((__m128i*)(p+48), xmm0);
186
    p += 64;
187
  }
188
#else
189
  memset(mem, 0, size);
190
#endif
191
61.8k
}
192
193
#ifdef ZEND_WIN32
194
static time_t zend_accel_get_time(void)
195
{
196
  FILETIME now;
197
  GetSystemTimeAsFileTime(&now);
198
199
  return (time_t) ((((((__int64)now.dwHighDateTime) << 32)|now.dwLowDateTime) - 116444736000000000L)/10000000);
200
}
201
#else
202
16
# define zend_accel_get_time() time(NULL)
203
#endif
204
205
static inline bool is_cacheable_stream_path(const char *filename)
206
1.59k
{
207
1.59k
  return memcmp(filename, "file://", sizeof("file://") - 1) == 0 ||
208
1.59k
         memcmp(filename, "phar://", sizeof("phar://") - 1) == 0;
209
1.59k
}
210
211
/* O+ overrides PHP chdir() function and remembers the current working directory
212
 * in ZCG(cwd) and ZCG(cwd_len). Later accel_getcwd() can use stored value and
213
 * avoid getcwd() call.
214
 */
215
static ZEND_FUNCTION(accel_chdir)
216
0
{
217
0
  char cwd[MAXPATHLEN];
218
219
0
  orig_chdir(INTERNAL_FUNCTION_PARAM_PASSTHRU);
220
0
  if (VCWD_GETCWD(cwd, MAXPATHLEN)) {
221
0
    if (ZCG(cwd)) {
222
0
      zend_string_release_ex(ZCG(cwd), 0);
223
0
    }
224
0
    ZCG(cwd) = zend_string_init(cwd, strlen(cwd), 0);
225
0
  } else {
226
0
    if (ZCG(cwd)) {
227
0
      zend_string_release_ex(ZCG(cwd), 0);
228
0
      ZCG(cwd) = NULL;
229
0
    }
230
0
  }
231
0
  ZCG(cwd_key_len) = 0;
232
0
  ZCG(cwd_check) = true;
233
0
}
234
235
static inline zend_string* accel_getcwd(void)
236
34.4k
{
237
34.4k
  if (ZCG(cwd)) {
238
0
    return ZCG(cwd);
239
34.4k
  } else {
240
34.4k
    char cwd[MAXPATHLEN + 1];
241
242
34.4k
    if (!VCWD_GETCWD(cwd, MAXPATHLEN)) {
243
0
      return NULL;
244
0
    }
245
34.4k
    ZCG(cwd) = zend_string_init(cwd, strlen(cwd), 0);
246
34.4k
    ZCG(cwd_key_len) = 0;
247
34.4k
    ZCG(cwd_check) = true;
248
34.4k
    return ZCG(cwd);
249
34.4k
  }
250
34.4k
}
251
252
void zend_accel_schedule_restart_if_necessary(zend_accel_restart_reason reason)
253
0
{
254
0
  if ((((double) ZSMMG(wasted_shared_memory)) / ZCG(accel_directives).memory_consumption) >= ZCG(accel_directives).max_wasted_percentage) {
255
0
    zend_accel_schedule_restart(reason);
256
0
  }
257
0
}
258
259
/* O+ tracks changes of "include_path" directive. It stores all the requested
260
 * values in ZCG(include_paths) shared hash table, current value in
261
 * ZCG(include_path)/ZCG(include_path_len) and one letter "path key" in
262
 * ZCG(include_path_key).
263
 */
264
static ZEND_INI_MH(accel_include_path_on_modify)
265
154
{
266
154
  int ret = orig_include_path_on_modify(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
267
268
154
  if (ret == SUCCESS) {
269
154
    ZCG(include_path) = new_value;
270
154
    ZCG(include_path_key_len) = 0;
271
154
    ZCG(include_path_check) = true;
272
154
  }
273
154
  return ret;
274
154
}
275
276
static inline void accel_restart_enter(void)
277
0
{
278
#ifdef ZEND_WIN32
279
  INCREMENT(restart_in);
280
#else
281
0
  struct flock restart_in_progress;
282
283
0
  restart_in_progress.l_type = F_WRLCK;
284
0
  restart_in_progress.l_whence = SEEK_SET;
285
0
  restart_in_progress.l_start = 2;
286
0
  restart_in_progress.l_len = 1;
287
288
0
  if (fcntl(lock_file, F_SETLK, &restart_in_progress) == -1) {
289
0
    zend_accel_error(ACCEL_LOG_DEBUG, "RestartC(+1):  %s (%d)", strerror(errno), errno);
290
0
  }
291
0
#endif
292
0
  ZCSG(restart_in_progress) = true;
293
0
}
294
295
static inline void accel_restart_leave(void)
296
0
{
297
#ifdef ZEND_WIN32
298
  ZCSG(restart_in_progress) = false;
299
  DECREMENT(restart_in);
300
#else
301
0
  struct flock restart_finished;
302
303
0
  restart_finished.l_type = F_UNLCK;
304
0
  restart_finished.l_whence = SEEK_SET;
305
0
  restart_finished.l_start = 2;
306
0
  restart_finished.l_len = 1;
307
308
0
  ZCSG(restart_in_progress) = false;
309
0
  if (fcntl(lock_file, F_SETLK, &restart_finished) == -1) {
310
0
    zend_accel_error(ACCEL_LOG_DEBUG, "RestartC(-1):  %s (%d)", strerror(errno), errno);
311
0
  }
312
0
#endif
313
0
}
314
315
static inline bool accel_restart_is_active(void)
316
0
{
317
0
  if (ZCSG(restart_in_progress)) {
318
0
#ifndef ZEND_WIN32
319
0
    struct flock restart_check;
320
321
0
    restart_check.l_type = F_WRLCK;
322
0
    restart_check.l_whence = SEEK_SET;
323
0
    restart_check.l_start = 2;
324
0
    restart_check.l_len = 1;
325
326
0
    if (fcntl(lock_file, F_GETLK, &restart_check) == -1) {
327
0
      zend_accel_error(ACCEL_LOG_DEBUG, "RestartC:  %s (%d)", strerror(errno), errno);
328
0
      return true;
329
0
    }
330
0
    if (restart_check.l_type == F_UNLCK) {
331
0
      ZCSG(restart_in_progress) = false;
332
0
      return false;
333
0
    } else {
334
0
      return true;
335
0
    }
336
#else
337
    return LOCKVAL(restart_in) != 0;
338
#endif
339
0
  }
340
0
  return false;
341
0
}
342
343
/* Creates a read lock for SHM access */
344
static inline zend_result accel_activate_add(void)
345
177k
{
346
#ifdef ZEND_WIN32
347
  SHM_UNPROTECT();
348
  INCREMENT(mem_usage);
349
  SHM_PROTECT();
350
#else
351
177k
  struct flock mem_usage_lock;
352
353
177k
  mem_usage_lock.l_type = F_RDLCK;
354
177k
  mem_usage_lock.l_whence = SEEK_SET;
355
177k
  mem_usage_lock.l_start = 1;
356
177k
  mem_usage_lock.l_len = 1;
357
358
177k
  if (fcntl(lock_file, F_SETLK, &mem_usage_lock) == -1) {
359
0
    zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC(+1):  %s (%d)", strerror(errno), errno);
360
0
    return FAILURE;
361
0
  }
362
177k
#endif
363
177k
  return SUCCESS;
364
177k
}
365
366
/* Releases a lock for SHM access */
367
static inline void accel_deactivate_sub(void)
368
0
{
369
#ifdef ZEND_WIN32
370
  if (ZCG(counted)) {
371
    SHM_UNPROTECT();
372
    DECREMENT(mem_usage);
373
    ZCG(counted) = false;
374
    SHM_PROTECT();
375
  }
376
#else
377
0
  struct flock mem_usage_unlock;
378
379
0
  mem_usage_unlock.l_type = F_UNLCK;
380
0
  mem_usage_unlock.l_whence = SEEK_SET;
381
0
  mem_usage_unlock.l_start = 1;
382
0
  mem_usage_unlock.l_len = 1;
383
384
0
  if (fcntl(lock_file, F_SETLK, &mem_usage_unlock) == -1) {
385
0
    zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC(-1):  %s (%d)", strerror(errno), errno);
386
0
  }
387
0
#endif
388
0
}
389
390
static inline void accel_unlock_all(void)
391
295k
{
392
#ifdef ZEND_WIN32
393
  accel_deactivate_sub();
394
#else
395
295k
  if (lock_file == -1) {
396
0
    return;
397
0
  }
398
399
295k
  struct flock mem_usage_unlock_all;
400
401
295k
  mem_usage_unlock_all.l_type = F_UNLCK;
402
295k
  mem_usage_unlock_all.l_whence = SEEK_SET;
403
295k
  mem_usage_unlock_all.l_start = 0;
404
295k
  mem_usage_unlock_all.l_len = 0;
405
406
295k
  if (fcntl(lock_file, F_SETLK, &mem_usage_unlock_all) == -1) {
407
0
    zend_accel_error(ACCEL_LOG_DEBUG, "UnlockAll:  %s (%d)", strerror(errno), errno);
408
0
  }
409
295k
#endif
410
295k
}
411
412
/* Interned strings support */
413
414
/* O+ disables creation of interned strings by regular PHP compiler, instead,
415
 * it creates interned strings in shared memory when saves a script.
416
 * Such interned strings are shared across all PHP processes
417
 */
418
419
2.62M
#define STRTAB_INVALID_POS 0
420
421
#define STRTAB_HASH_TO_SLOT(tab, h) \
422
37.4M
  ((zend_string_table_pos_t*)((char*)(tab) + sizeof(*(tab)) + ((h) & (tab)->nTableMask)))
423
#define STRTAB_STR_TO_POS(tab, s) \
424
207k
  ((zend_string_table_pos_t)(((char*)s - (char*)(tab)) / ZEND_STRING_TABLE_POS_ALIGNMENT))
425
#define STRTAB_POS_TO_STR(tab, pos) \
426
6.34M
  ((zend_string*)((char*)(tab) + ((uintptr_t)(pos) * ZEND_STRING_TABLE_POS_ALIGNMENT)))
427
#define STRTAB_COLLISION(s) \
428
2.76M
  (*((zend_string_table_pos_t*)((char*)s - sizeof(zend_string_table_pos_t))))
429
#define STRTAB_STR_SIZE(s) \
430
138k
  ZEND_MM_ALIGNED_SIZE_EX(_ZSTR_STRUCT_SIZE(ZSTR_LEN(s)) + sizeof(zend_string_table_pos_t), ZEND_STRING_TABLE_POS_ALIGNMENT)
431
#define STRTAB_NEXT(s) \
432
138k
  ((zend_string*)((char*)(s) + STRTAB_STR_SIZE(s)))
433
434
static void accel_interned_strings_restore_state(void)
435
0
{
436
0
  zend_string *s, *top;
437
0
  zend_string_table_pos_t *hash_slot, n;
438
439
  /* clear removed content */
440
0
  memset(ZCSG(interned_strings).saved_top,
441
0
      0, (char*)ZCSG(interned_strings).top - (char*)ZCSG(interned_strings).saved_top);
442
443
  /* Reset "top" */
444
0
  ZCSG(interned_strings).top = ZCSG(interned_strings).saved_top;
445
446
  /* rehash */
447
0
  memset((char*)&ZCSG(interned_strings) + sizeof(zend_string_table),
448
0
    STRTAB_INVALID_POS,
449
0
    (char*)ZCSG(interned_strings).start -
450
0
      ((char*)&ZCSG(interned_strings) + sizeof(zend_string_table)));
451
0
  s = ZCSG(interned_strings).start;
452
0
  top = ZCSG(interned_strings).top;
453
0
  n = 0;
454
0
  if (EXPECTED(s < top)) {
455
0
    do {
456
0
      if (ZSTR_HAS_CE_CACHE(s)) {
457
        /* Discard non-global CE_CACHE slots on reset. */
458
0
        uintptr_t idx = (GC_REFCOUNT(s) - 1) / sizeof(void *);
459
0
        if (idx >= ZCSG(map_ptr_last)) {
460
0
          GC_SET_REFCOUNT(s, 2);
461
0
          GC_DEL_FLAGS(s, IS_STR_CLASS_NAME_MAP_PTR);
462
0
        }
463
0
      }
464
465
0
      hash_slot = STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), ZSTR_H(s));
466
0
      STRTAB_COLLISION(s) = *hash_slot;
467
0
      *hash_slot = STRTAB_STR_TO_POS(&ZCSG(interned_strings), s);
468
0
      s = STRTAB_NEXT(s);
469
0
      n++;
470
0
    } while (s < top);
471
0
  }
472
0
  ZCSG(interned_strings).nNumOfElements = n;
473
0
}
474
475
static void accel_interned_strings_save_state(void)
476
16
{
477
16
  ZCSG(interned_strings).saved_top = ZCSG(interned_strings).top;
478
16
}
479
480
static zend_always_inline zend_string *accel_find_interned_string(zend_string *str)
481
39.4M
{
482
39.4M
  zend_ulong   h;
483
39.4M
  zend_string_table_pos_t pos;
484
39.4M
  zend_string *s;
485
486
39.4M
  if (IS_ACCEL_INTERNED(str)) {
487
    /* this is already an interned string */
488
2.74M
    return str;
489
2.74M
  }
490
491
36.6M
  if (!ZCG(counted)) {
492
34.0k
    if (!ZCG(accelerator_enabled) || accel_activate_add() == FAILURE) {
493
0
      return NULL;
494
0
    }
495
34.0k
    ZCG(counted) = true;
496
34.0k
  }
497
498
36.6M
  h = zend_string_hash_val(str);
499
500
  /* check for existing interned string */
501
36.6M
  pos = *STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), h);
502
36.6M
  if (EXPECTED(pos != STRTAB_INVALID_POS)) {
503
5.86M
    do {
504
5.86M
      s = STRTAB_POS_TO_STR(&ZCSG(interned_strings), pos);
505
5.86M
      if (EXPECTED(ZSTR_H(s) == h) && zend_string_equal_content(s, str)) {
506
3.35M
        return s;
507
3.35M
      }
508
2.51M
      pos = STRTAB_COLLISION(s);
509
2.51M
    } while (pos != STRTAB_INVALID_POS);
510
3.43M
  }
511
512
33.3M
  return NULL;
513
36.6M
}
514
515
zend_string* ZEND_FASTCALL accel_new_interned_string(zend_string *str)
516
549k
{
517
549k
  zend_ulong   h;
518
549k
  zend_string_table_pos_t pos, *hash_slot;
519
549k
  zend_string *s;
520
521
549k
  if (UNEXPECTED(file_cache_only)) {
522
0
    return str;
523
0
  }
524
525
549k
  if (IS_ACCEL_INTERNED(str)) {
526
    /* this is already an interned string */
527
6.52k
    return str;
528
6.52k
  }
529
530
543k
  h = zend_string_hash_val(str);
531
532
  /* check for existing interned string */
533
543k
  hash_slot = STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), h);
534
543k
  pos = *hash_slot;
535
543k
  if (EXPECTED(pos != STRTAB_INVALID_POS)) {
536
436k
    do {
537
436k
      s = STRTAB_POS_TO_STR(&ZCSG(interned_strings), pos);
538
436k
      if (EXPECTED(ZSTR_H(s) == h) && zend_string_equal_content(s, str)) {
539
337k
        goto finish;
540
337k
      }
541
99.4k
      pos = STRTAB_COLLISION(s);
542
99.4k
    } while (pos != STRTAB_INVALID_POS);
543
353k
  }
544
545
206k
  if (UNEXPECTED((char*)ZCSG(interned_strings).end - (char*)ZCSG(interned_strings).top < STRTAB_STR_SIZE(str))) {
546
      /* no memory, return the same non-interned string */
547
67.3k
    zend_accel_error(ACCEL_LOG_WARNING, "Interned string buffer overflow");
548
67.3k
    return str;
549
67.3k
  }
550
551
  /* create new interning string in shared interned strings buffer */
552
138k
  ZCSG(interned_strings).nNumOfElements++;
553
138k
  s = ZCSG(interned_strings).top;
554
138k
  hash_slot = STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), h);
555
138k
  STRTAB_COLLISION(s) = *hash_slot;
556
138k
  *hash_slot = STRTAB_STR_TO_POS(&ZCSG(interned_strings), s);
557
138k
  GC_SET_REFCOUNT(s, 2);
558
138k
  GC_TYPE_INFO(s) = GC_STRING | ((IS_STR_INTERNED | IS_STR_PERMANENT) << GC_FLAGS_SHIFT)| (ZSTR_IS_VALID_UTF8(str) ? IS_STR_VALID_UTF8 : 0);
559
138k
  ZSTR_H(s) = h;
560
138k
  ZSTR_LEN(s) = ZSTR_LEN(str);
561
138k
  memcpy(ZSTR_VAL(s), ZSTR_VAL(str), ZSTR_LEN(s) + 1);
562
138k
  ZCSG(interned_strings).top = STRTAB_NEXT(s);
563
564
475k
finish:
565
  /* Transfer CE_CACHE map ptr slot to new interned string.
566
   * Should only happen for permanent interned strings with permanent map_ptr slot. */
567
475k
  if (ZSTR_HAS_CE_CACHE(str) && !ZSTR_HAS_CE_CACHE(s)) {
568
3.13k
    ZEND_ASSERT(GC_FLAGS(str) & IS_STR_PERMANENT);
569
3.13k
    GC_SET_REFCOUNT(s, GC_REFCOUNT(str));
570
3.13k
    GC_ADD_FLAGS(s, IS_STR_CLASS_NAME_MAP_PTR);
571
3.13k
  }
572
573
475k
  zend_string_release(str);
574
475k
  return s;
575
475k
}
576
577
static zend_string* ZEND_FASTCALL accel_new_interned_string_for_php(zend_string *str)
578
39.3M
{
579
39.3M
  zend_string_hash_val(str);
580
39.3M
  if (ZCG(counted)) {
581
39.3M
    zend_string *ret = accel_find_interned_string(str);
582
583
39.3M
    if (ret) {
584
6.02M
      zend_string_release(str);
585
6.02M
      return ret;
586
6.02M
    }
587
39.3M
  }
588
33.3M
  return str;
589
39.3M
}
590
591
static zend_always_inline zend_string *accel_find_interned_string_ex(zend_ulong h, const char *str, size_t size)
592
39.6k
{
593
39.6k
  zend_string_table_pos_t pos;
594
595
  /* check for existing interned string */
596
39.6k
  pos = *STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), h);
597
39.6k
  if (EXPECTED(pos != STRTAB_INVALID_POS)) {
598
44.2k
    do {
599
44.2k
      zend_string *s = STRTAB_POS_TO_STR(&ZCSG(interned_strings), pos);
600
44.2k
      if (EXPECTED(ZSTR_H(s) == h) && zend_string_equals_cstr(s, str, size)) {
601
32.4k
        return s;
602
32.4k
      }
603
11.8k
      pos = STRTAB_COLLISION(s);
604
11.8k
    } while (pos != STRTAB_INVALID_POS);
605
32.5k
  }
606
7.19k
  return NULL;
607
39.6k
}
608
609
static zend_string* ZEND_FASTCALL accel_init_interned_string_for_php(const char *str, size_t size, bool permanent)
610
1.41M
{
611
1.41M
  if (ZCG(counted)) {
612
39.6k
      zend_ulong h = zend_inline_hash_func(str, size);
613
39.6k
    zend_string *ret = accel_find_interned_string_ex(h, str, size);
614
615
39.6k
    if (!ret) {
616
7.19k
      ret = zend_string_init(str, size, permanent);
617
7.19k
      ZSTR_H(ret) = h;
618
7.19k
    }
619
620
39.6k
    return ret;
621
39.6k
  }
622
623
1.37M
  return zend_string_init(str, size, permanent);
624
1.41M
}
625
626
static inline void accel_copy_permanent_list_types(
627
  zend_new_interned_string_func_t new_interned_string, zend_type type)
628
55.3k
{
629
55.3k
  zend_type *single_type;
630
110k
  ZEND_TYPE_FOREACH_MUTABLE(type, single_type) {
631
110k
    if (ZEND_TYPE_HAS_LIST(*single_type)) {
632
0
      ZEND_ASSERT(ZEND_TYPE_IS_INTERSECTION(*single_type));
633
0
      accel_copy_permanent_list_types(new_interned_string, *single_type);
634
0
    }
635
110k
    if (ZEND_TYPE_HAS_NAME(*single_type)) {
636
4.25k
      ZEND_TYPE_SET_PTR(*single_type, new_interned_string(ZEND_TYPE_NAME(*single_type)));
637
4.25k
    }
638
55.3k
  } ZEND_TYPE_FOREACH_END();
639
55.3k
}
640
641
/* Copy PHP interned strings from PHP process memory into the shared memory */
642
static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_interned_string)
643
16
{
644
16
  uint32_t j;
645
16
  Bucket *p, *q;
646
16
  HashTable *ht;
647
648
  /* empty string */
649
16
  zend_empty_string = new_interned_string(zend_empty_string);
650
4.11k
  for (j = 0; j < 256; j++) {
651
4.09k
    zend_one_char_string[j] = new_interned_string(ZSTR_CHAR(j));
652
4.09k
  }
653
1.55k
  for (j = 0; j < ZEND_STR_LAST_KNOWN; j++) {
654
1.53k
    zend_known_strings[j] = new_interned_string(zend_known_strings[j]);
655
1.53k
  }
656
657
  /* function table hash keys */
658
22.1k
  ZEND_HASH_MAP_FOREACH_BUCKET(CG(function_table), p) {
659
22.1k
    if (p->key) {
660
11.0k
      p->key = new_interned_string(p->key);
661
11.0k
    }
662
22.1k
    if (Z_FUNC(p->val)->common.function_name) {
663
11.0k
      Z_FUNC(p->val)->common.function_name = new_interned_string(Z_FUNC(p->val)->common.function_name);
664
11.0k
    }
665
22.1k
    if (Z_FUNC(p->val)->common.arg_info) {
666
11.0k
      uint32_t i;
667
11.0k
      uint32_t num_args = Z_FUNC(p->val)->common.num_args + 1;
668
11.0k
      zend_arg_info *arg_info = Z_FUNC(p->val)->common.arg_info - 1;
669
670
11.0k
      if (Z_FUNC(p->val)->common.fn_flags & ZEND_ACC_VARIADIC) {
671
640
        num_args++;
672
640
      }
673
41.1k
      for (i = 0 ; i < num_args; i++) {
674
30.1k
        if (i > 0) {
675
19.0k
          arg_info[i].name = new_interned_string(arg_info[i].name);
676
19.0k
          if (arg_info[i].default_value) {
677
6.16k
            arg_info[i].default_value = new_interned_string(arg_info[i].default_value);
678
6.16k
          }
679
19.0k
        }
680
30.1k
        accel_copy_permanent_list_types(new_interned_string, arg_info[i].type);
681
30.1k
      }
682
11.0k
    }
683
22.1k
  } ZEND_HASH_FOREACH_END();
684
685
  /* class table hash keys, class names, properties, methods, constants, etc */
686
6.30k
  ZEND_HASH_MAP_FOREACH_BUCKET(CG(class_table), p) {
687
6.30k
    zend_class_entry *ce;
688
689
6.30k
    ce = (zend_class_entry*)Z_PTR(p->val);
690
691
6.30k
    if (p->key) {
692
3.13k
      p->key = new_interned_string(p->key);
693
3.13k
    }
694
695
6.30k
    if (ce->name) {
696
3.13k
      ce->name = new_interned_string(ce->name);
697
3.13k
      ZEND_ASSERT(ZSTR_HAS_CE_CACHE(ce->name));
698
3.13k
    }
699
700
22.3k
    ZEND_HASH_MAP_FOREACH_BUCKET(&ce->properties_info, q) {
701
22.3k
      zend_property_info *info;
702
703
22.3k
      info = (zend_property_info*)Z_PTR(q->val);
704
705
22.3k
      if (q->key) {
706
8.03k
        q->key = new_interned_string(q->key);
707
8.03k
      }
708
709
22.3k
      if (info->name) {
710
8.03k
        info->name = new_interned_string(info->name);
711
8.03k
      }
712
22.3k
    } ZEND_HASH_FOREACH_END();
713
714
81.0k
    ZEND_HASH_MAP_FOREACH_BUCKET(&ce->function_table, q) {
715
81.0k
      if (q->key) {
716
37.3k
        q->key = new_interned_string(q->key);
717
37.3k
      }
718
81.0k
      if (Z_FUNC(q->val)->common.function_name) {
719
37.3k
        Z_FUNC(q->val)->common.function_name = new_interned_string(Z_FUNC(q->val)->common.function_name);
720
37.3k
      }
721
81.0k
      if (Z_FUNC(q->val)->common.scope == ce) {
722
16.4k
        uint32_t num_args = Z_FUNC(q->val)->common.num_args + 1;
723
16.4k
        zend_arg_info *arg_info = Z_FUNC(q->val)->common.arg_info - 1;
724
725
16.4k
        if (Z_FUNC(q->val)->common.fn_flags & ZEND_ACC_VARIADIC) {
726
96
          num_args++;
727
96
        }
728
41.6k
        for (uint32_t i = 0 ; i < num_args; i++) {
729
25.2k
          if (i > 0) {
730
8.73k
            arg_info[i].name = new_interned_string(arg_info[i].name);
731
8.73k
            if (arg_info[i].default_value) {
732
2.57k
              arg_info[i].default_value = new_interned_string(arg_info[i].default_value);
733
2.57k
            }
734
8.73k
          }
735
25.2k
          accel_copy_permanent_list_types(new_interned_string, arg_info[i].type);
736
25.2k
        }
737
16.4k
      }
738
81.0k
    } ZEND_HASH_FOREACH_END();
739
740
20.8k
    ZEND_HASH_MAP_FOREACH_BUCKET(&ce->constants_table, q) {
741
20.8k
      zend_class_constant* c;
742
743
20.8k
      if (q->key) {
744
7.29k
        q->key = new_interned_string(q->key);
745
7.29k
      }
746
20.8k
      c = (zend_class_constant*)Z_PTR(q->val);
747
20.8k
      if (Z_TYPE(c->value) == IS_STRING) {
748
672
        ZVAL_STR(&c->value, new_interned_string(Z_STR(c->value)));
749
672
      }
750
20.8k
    } ZEND_HASH_FOREACH_END();
751
3.13k
  } ZEND_HASH_FOREACH_END();
752
753
  /* constant hash keys */
754
17.6k
  ZEND_HASH_MAP_FOREACH_BUCKET(EG(zend_constants), p) {
755
17.6k
    zend_constant *c;
756
757
17.6k
    if (p->key) {
758
8.78k
      p->key = new_interned_string(p->key);
759
8.78k
    }
760
17.6k
    c = (zend_constant*)Z_PTR(p->val);
761
17.6k
    if (c->name) {
762
8.78k
      c->name = new_interned_string(c->name);
763
8.78k
    }
764
17.6k
    if (Z_TYPE(c->value) == IS_STRING) {
765
688
      ZVAL_STR(&c->value, new_interned_string(Z_STR(c->value)));
766
688
    }
767
17.6k
  } ZEND_HASH_FOREACH_END();
768
769
  /* auto globals hash keys and names */
770
288
  ZEND_HASH_MAP_FOREACH_BUCKET(CG(auto_globals), p) {
771
288
    zend_auto_global *auto_global;
772
773
288
    auto_global = (zend_auto_global*)Z_PTR(p->val);
774
775
288
    zend_string_addref(auto_global->name);
776
288
    auto_global->name = new_interned_string(auto_global->name);
777
288
    if (p->key) {
778
128
      p->key = new_interned_string(p->key);
779
128
    }
780
288
  } ZEND_HASH_FOREACH_END();
781
782
448
  ZEND_HASH_MAP_FOREACH_BUCKET(&module_registry, p) {
783
448
    if (p->key) {
784
208
      p->key = new_interned_string(p->key);
785
208
    }
786
448
  } ZEND_HASH_FOREACH_END();
787
788
5.76k
  ZEND_HASH_MAP_FOREACH_BUCKET(EG(ini_directives), p) {
789
5.76k
    zend_ini_entry *entry = (zend_ini_entry*)Z_PTR(p->val);
790
791
5.76k
    if (p->key) {
792
2.86k
      p->key = new_interned_string(p->key);
793
2.86k
    }
794
5.76k
    if (entry->name) {
795
2.86k
      entry->name = new_interned_string(entry->name);
796
2.86k
    }
797
5.76k
    if (entry->value) {
798
2.46k
      entry->value = new_interned_string(entry->value);
799
2.46k
    }
800
5.76k
    if (entry->orig_value) {
801
0
      entry->orig_value = new_interned_string(entry->orig_value);
802
0
    }
803
5.76k
  } ZEND_HASH_FOREACH_END();
804
805
16
  ht = php_get_stream_filters_hash_global();
806
224
  ZEND_HASH_MAP_FOREACH_BUCKET(ht, p) {
807
224
    if (p->key) {
808
96
      p->key = new_interned_string(p->key);
809
96
    }
810
224
  } ZEND_HASH_FOREACH_END();
811
812
16
  ht = php_stream_get_url_stream_wrappers_hash_global();
813
224
  ZEND_HASH_MAP_FOREACH_BUCKET(ht, p) {
814
224
    if (p->key) {
815
96
      p->key = new_interned_string(p->key);
816
96
    }
817
224
  } ZEND_HASH_FOREACH_END();
818
819
16
  ht = php_stream_xport_get_hash();
820
160
  ZEND_HASH_MAP_FOREACH_BUCKET(ht, p) {
821
160
    if (p->key) {
822
64
      p->key = new_interned_string(p->key);
823
64
    }
824
160
  } ZEND_HASH_FOREACH_END();
825
16
}
826
827
static zend_string* ZEND_FASTCALL accel_replace_string_by_shm_permanent(zend_string *str)
828
0
{
829
0
  zend_string *ret = accel_find_interned_string(str);
830
831
0
  if (ret) {
832
0
    zend_string_release(str);
833
0
    return ret;
834
0
  }
835
0
  return str;
836
0
}
837
838
static void accel_use_shm_interned_strings(void)
839
16
{
840
16
  HANDLE_BLOCK_INTERRUPTIONS();
841
16
  SHM_UNPROTECT();
842
16
  zend_shared_alloc_lock();
843
844
16
  if (ZCSG(interned_strings).saved_top == NULL) {
845
16
    accel_copy_permanent_strings(accel_new_interned_string);
846
16
  } else {
847
0
    ZCG(counted) = true;
848
0
    accel_copy_permanent_strings(accel_replace_string_by_shm_permanent);
849
0
    ZCG(counted) = false;
850
0
  }
851
16
  accel_interned_strings_save_state();
852
853
16
  zend_shared_alloc_unlock();
854
16
  SHM_PROTECT();
855
16
  HANDLE_UNBLOCK_INTERRUPTIONS();
856
16
}
857
858
#ifndef ZEND_WIN32
859
static inline void kill_all_lockers(struct flock *mem_usage_check)
860
0
{
861
  /* so that other process won't try to force while we are busy cleaning up */
862
0
  ZCSG(force_restart_time) = 0;
863
0
  while (mem_usage_check->l_pid > 0) {
864
    /* Try SIGTERM first, switch to SIGKILL if not successful. */
865
0
    int signal = SIGTERM;
866
0
    errno = 0;
867
0
    bool success = false;
868
0
    int tries = 10;
869
870
0
    while (tries--) {
871
0
      zend_accel_error(ACCEL_LOG_WARNING, "Attempting to kill locker %d", mem_usage_check->l_pid);
872
0
      if (kill(mem_usage_check->l_pid, signal)) {
873
0
        if (errno == ESRCH) {
874
          /* Process died before the signal was sent */
875
0
          success = true;
876
0
          zend_accel_error(ACCEL_LOG_WARNING, "Process %d died before SIGKILL was sent", mem_usage_check->l_pid);
877
0
        } else if (errno != 0) {
878
0
          zend_accel_error(ACCEL_LOG_WARNING, "Failed to send SIGKILL to locker %d: %s", mem_usage_check->l_pid, strerror(errno));
879
0
        }
880
0
        break;
881
0
      }
882
      /* give it a chance to die */
883
0
      usleep(20000);
884
0
      if (kill(mem_usage_check->l_pid, 0)) {
885
0
        if (errno == ESRCH) {
886
          /* successfully killed locker, process no longer exists  */
887
0
          success = true;
888
0
          zend_accel_error(ACCEL_LOG_WARNING, "Killed locker %d", mem_usage_check->l_pid);
889
0
        } else if (errno != 0) {
890
0
          zend_accel_error(ACCEL_LOG_WARNING, "Failed to check locker %d: %s", mem_usage_check->l_pid, strerror(errno));
891
0
        }
892
0
        break;
893
0
      }
894
0
      usleep(10000);
895
      /* If SIGTERM was not sufficient, use SIGKILL. */
896
0
      signal = SIGKILL;
897
0
    }
898
0
    if (!success) {
899
      /* errno is not ESRCH or we ran out of tries to kill the locker */
900
0
      ZCSG(force_restart_time) = time(NULL); /* restore forced restart request */
901
      /* cannot kill the locker, bail out with error */
902
0
      zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Cannot kill process %d!", mem_usage_check->l_pid);
903
0
    }
904
905
0
    mem_usage_check->l_type = F_WRLCK;
906
0
    mem_usage_check->l_whence = SEEK_SET;
907
0
    mem_usage_check->l_start = 1;
908
0
    mem_usage_check->l_len = 1;
909
0
    mem_usage_check->l_pid = -1;
910
0
    if (fcntl(lock_file, F_GETLK, mem_usage_check) == -1) {
911
0
      zend_accel_error(ACCEL_LOG_DEBUG, "KLockers:  %s (%d)", strerror(errno), errno);
912
0
      break;
913
0
    }
914
915
0
    if (mem_usage_check->l_type == F_UNLCK || mem_usage_check->l_pid <= 0) {
916
0
      break;
917
0
    }
918
0
  }
919
0
}
920
#endif
921
922
static inline bool accel_is_inactive(void)
923
0
{
924
#ifdef ZEND_WIN32
925
  /* on Windows, we don't need kill_all_lockers() because SAPIs
926
     that work on Windows don't manage child processes (and we
927
     can't do anything about hanging threads anyway); therefore
928
     on Windows, we can simply manage this counter with atomics
929
     instead of flocks (atomics are much faster but they don't
930
     provide us with the PID of locker processes) */
931
932
  if (LOCKVAL(mem_usage) == 0) {
933
    return true;
934
  }
935
#else
936
0
  struct flock mem_usage_check;
937
938
0
  mem_usage_check.l_type = F_WRLCK;
939
0
  mem_usage_check.l_whence = SEEK_SET;
940
0
  mem_usage_check.l_start = 1;
941
0
  mem_usage_check.l_len = 1;
942
0
  mem_usage_check.l_pid = -1;
943
0
  if (fcntl(lock_file, F_GETLK, &mem_usage_check) == -1) {
944
0
    zend_accel_error(ACCEL_LOG_DEBUG, "UpdateC:  %s (%d)", strerror(errno), errno);
945
0
    return false;
946
0
  }
947
0
  if (mem_usage_check.l_type == F_UNLCK) {
948
0
    return true;
949
0
  }
950
951
0
  if (ZCG(accel_directives).force_restart_timeout
952
0
    && ZCSG(force_restart_time)
953
0
    && time(NULL) >= ZCSG(force_restart_time)) {
954
0
    zend_accel_error(ACCEL_LOG_WARNING, "Forced restart at %ld (after " ZEND_LONG_FMT " seconds), locked by %d", (long)time(NULL), ZCG(accel_directives).force_restart_timeout, mem_usage_check.l_pid);
955
0
    kill_all_lockers(&mem_usage_check);
956
957
0
    return false; /* next request should be able to restart it */
958
0
  }
959
0
#endif
960
961
0
  return false;
962
0
}
963
964
static zend_result zend_get_stream_timestamp(const char *filename, zend_stat_t *statbuf)
965
0
{
966
0
  php_stream_wrapper *wrapper;
967
0
  php_stream_statbuf stream_statbuf;
968
0
  int ret, er;
969
970
0
  if (!filename) {
971
0
    return FAILURE;
972
0
  }
973
974
0
  wrapper = php_stream_locate_url_wrapper(filename, NULL, STREAM_LOCATE_WRAPPERS_ONLY);
975
0
  if (!wrapper) {
976
0
    return FAILURE;
977
0
  }
978
0
  if (!wrapper->wops || !wrapper->wops->url_stat) {
979
0
    statbuf->st_mtime = 1;
980
0
    return SUCCESS; /* anything other than 0 is considered to be a valid timestamp */
981
0
  }
982
983
0
  er = EG(error_reporting);
984
0
  EG(error_reporting) = 0;
985
0
  zend_try {
986
0
    ret = wrapper->wops->url_stat(wrapper, (char*)filename, PHP_STREAM_URL_STAT_QUIET, &stream_statbuf, NULL);
987
0
  } zend_catch {
988
0
    ret = -1;
989
0
  } zend_end_try();
990
0
  EG(error_reporting) = er;
991
992
0
  if (ret != 0) {
993
0
    return FAILURE;
994
0
  }
995
996
0
  *statbuf = stream_statbuf.sb;
997
0
  return SUCCESS;
998
0
}
999
1000
#ifdef ZEND_WIN32
1001
static accel_time_t zend_get_file_handle_timestamp_win(zend_file_handle *file_handle, size_t *size)
1002
{
1003
  static unsigned __int64 utc_base = 0;
1004
  static FILETIME utc_base_ft;
1005
  WIN32_FILE_ATTRIBUTE_DATA fdata;
1006
1007
  if (!file_handle->opened_path) {
1008
    return 0;
1009
  }
1010
1011
  if (!utc_base) {
1012
    SYSTEMTIME st;
1013
1014
    st.wYear = 1970;
1015
    st.wMonth = 1;
1016
    st.wDay = 1;
1017
    st.wHour = 0;
1018
    st.wMinute = 0;
1019
    st.wSecond = 0;
1020
    st.wMilliseconds = 0;
1021
1022
    SystemTimeToFileTime (&st, &utc_base_ft);
1023
    utc_base = (((unsigned __int64)utc_base_ft.dwHighDateTime) << 32) + utc_base_ft.dwLowDateTime;
1024
  }
1025
1026
  if (file_handle->opened_path && GetFileAttributesEx(file_handle->opened_path->val, GetFileExInfoStandard, &fdata) != 0) {
1027
    unsigned __int64 ftime;
1028
1029
    if (CompareFileTime (&fdata.ftLastWriteTime, &utc_base_ft) < 0) {
1030
      return 0;
1031
    }
1032
1033
    ftime = (((unsigned __int64)fdata.ftLastWriteTime.dwHighDateTime) << 32) + fdata.ftLastWriteTime.dwLowDateTime - utc_base;
1034
    ftime /= 10000000L;
1035
1036
    if (size) {
1037
      *size = (size_t)((((unsigned __int64)fdata.nFileSizeHigh) << 32) + (unsigned __int64)fdata.nFileSizeLow);
1038
    }
1039
    return (accel_time_t)ftime;
1040
  }
1041
  return 0;
1042
}
1043
#endif
1044
1045
accel_time_t zend_get_file_handle_timestamp(zend_file_handle *file_handle, size_t *size)
1046
47.6k
{
1047
47.6k
  zend_stat_t statbuf = {0};
1048
#ifdef ZEND_WIN32
1049
  accel_time_t res;
1050
#endif
1051
1052
47.6k
  if (sapi_module.get_stat &&
1053
0
      !EG(current_execute_data) &&
1054
0
      file_handle->primary_script) {
1055
1056
0
    const zend_stat_t *tmpbuf = sapi_module.get_stat();
1057
1058
0
    if (tmpbuf) {
1059
0
      if (size) {
1060
0
        *size = tmpbuf->st_size;
1061
0
      }
1062
0
      return tmpbuf->st_mtime;
1063
0
    }
1064
0
  }
1065
1066
#ifdef ZEND_WIN32
1067
  res = zend_get_file_handle_timestamp_win(file_handle, size);
1068
  if (res) {
1069
    return res;
1070
  }
1071
#endif
1072
1073
47.6k
  switch (file_handle->type) {
1074
0
    case ZEND_HANDLE_FP:
1075
0
      if (zend_fstat(fileno(file_handle->handle.fp), &statbuf) == -1) {
1076
0
        if (zend_get_stream_timestamp(ZSTR_VAL(file_handle->filename), &statbuf) != SUCCESS) {
1077
0
          return 0;
1078
0
        }
1079
0
      }
1080
0
      break;
1081
0
    case ZEND_HANDLE_FILENAME:
1082
0
      if (file_handle->opened_path) {
1083
0
        char *file_path = ZSTR_VAL(file_handle->opened_path);
1084
1085
0
        if (php_is_stream_path(file_path)) {
1086
0
          if (zend_get_stream_timestamp(file_path, &statbuf) == SUCCESS) {
1087
0
            break;
1088
0
          }
1089
0
        }
1090
0
        if (VCWD_STAT(file_path, &statbuf) != -1) {
1091
0
          break;
1092
0
        }
1093
0
      }
1094
1095
0
      if (zend_get_stream_timestamp(ZSTR_VAL(file_handle->filename), &statbuf) != SUCCESS) {
1096
0
        return 0;
1097
0
      }
1098
0
      break;
1099
47.6k
    case ZEND_HANDLE_STREAM:
1100
47.6k
      {
1101
47.6k
        php_stream *stream = (php_stream *)file_handle->handle.stream.handle;
1102
47.6k
        php_stream_statbuf sb;
1103
47.6k
        int ret, er;
1104
1105
47.6k
        if (!stream ||
1106
4
            !stream->ops ||
1107
47.6k
            !stream->ops->stat) {
1108
47.6k
          return 0;
1109
47.6k
        }
1110
1111
4
        er = EG(error_reporting);
1112
4
        EG(error_reporting) = 0;
1113
4
        zend_try {
1114
4
          ret = stream->ops->stat(stream, &sb);
1115
4
        } zend_catch {
1116
0
          ret = -1;
1117
4
        } zend_end_try();
1118
4
        EG(error_reporting) = er;
1119
4
        if (ret != 0) {
1120
0
          return 0;
1121
0
        }
1122
1123
4
        statbuf = sb.sb;
1124
4
      }
1125
0
      break;
1126
1127
0
    default:
1128
0
      return 0;
1129
47.6k
  }
1130
1131
4
  if (size) {
1132
0
    *size = statbuf.st_size;
1133
0
  }
1134
4
  return statbuf.st_mtime;
1135
47.6k
}
1136
1137
static inline zend_result do_validate_timestamps(const zend_persistent_script *persistent_script, zend_file_handle *file_handle)
1138
0
{
1139
0
  zend_file_handle ps_handle;
1140
0
  zend_string *full_path_ptr = NULL;
1141
0
  zend_result ret;
1142
1143
  /** check that the persistent script is indeed the same file we cached
1144
   * (if part of the path is a symlink than it possible that the user will change it)
1145
   * See bug #15140
1146
   */
1147
0
  if (file_handle->opened_path) {
1148
0
    if (persistent_script->script.filename != file_handle->opened_path &&
1149
0
        !zend_string_equal_content(persistent_script->script.filename, file_handle->opened_path)) {
1150
0
      return FAILURE;
1151
0
    }
1152
0
  } else {
1153
0
    full_path_ptr = accelerator_orig_zend_resolve_path(file_handle->filename);
1154
0
    if (full_path_ptr &&
1155
0
        persistent_script->script.filename != full_path_ptr &&
1156
0
        !zend_string_equal_content(persistent_script->script.filename, full_path_ptr)) {
1157
0
      zend_string_release_ex(full_path_ptr, 0);
1158
0
      return FAILURE;
1159
0
    }
1160
0
    file_handle->opened_path = full_path_ptr;
1161
0
  }
1162
1163
0
  if (persistent_script->timestamp == 0) {
1164
0
    if (full_path_ptr) {
1165
0
      zend_string_release_ex(full_path_ptr, 0);
1166
0
      file_handle->opened_path = NULL;
1167
0
    }
1168
0
    return FAILURE;
1169
0
  }
1170
1171
0
  if (zend_get_file_handle_timestamp(file_handle, NULL) == persistent_script->timestamp) {
1172
0
    if (full_path_ptr) {
1173
0
      zend_string_release_ex(full_path_ptr, 0);
1174
0
      file_handle->opened_path = NULL;
1175
0
    }
1176
0
    return SUCCESS;
1177
0
  }
1178
0
  if (full_path_ptr) {
1179
0
    zend_string_release_ex(full_path_ptr, 0);
1180
0
    file_handle->opened_path = NULL;
1181
0
  }
1182
1183
0
  zend_stream_init_filename_ex(&ps_handle, persistent_script->script.filename);
1184
0
  ps_handle.opened_path = persistent_script->script.filename;
1185
1186
0
  ret = zend_get_file_handle_timestamp(&ps_handle, NULL) == persistent_script->timestamp
1187
0
    ? SUCCESS : FAILURE;
1188
1189
0
  zend_destroy_file_handle(&ps_handle);
1190
1191
0
  return ret;
1192
0
}
1193
1194
zend_result validate_timestamp_and_record(zend_persistent_script *persistent_script, zend_file_handle *file_handle)
1195
40.3k
{
1196
40.3k
  if (persistent_script->timestamp == 0) {
1197
0
    return SUCCESS; /* Don't check timestamps of preloaded scripts */
1198
40.3k
  } else if (ZCG(accel_directives).revalidate_freq &&
1199
40.3k
      persistent_script->dynamic_members.revalidate >= ZCG(request_time)) {
1200
40.3k
    return SUCCESS;
1201
40.3k
  } else if (do_validate_timestamps(persistent_script, file_handle) == FAILURE) {
1202
0
    return FAILURE;
1203
0
  } else {
1204
0
    persistent_script->dynamic_members.revalidate = ZCG(request_time) + ZCG(accel_directives).revalidate_freq;
1205
0
    return SUCCESS;
1206
0
  }
1207
40.3k
}
1208
1209
zend_result validate_timestamp_and_record_ex(zend_persistent_script *persistent_script, zend_file_handle *file_handle)
1210
0
{
1211
0
  SHM_UNPROTECT();
1212
0
  const zend_result ret = validate_timestamp_and_record(persistent_script, file_handle);
1213
0
  SHM_PROTECT();
1214
1215
0
  return ret;
1216
0
}
1217
1218
/* Instead of resolving full real path name each time we need to identify file,
1219
 * we create a key that consist from requested file name, current working
1220
 * directory, current include_path, etc */
1221
zend_string *accel_make_persistent_key(zend_string *str)
1222
280k
{
1223
280k
  const char *path = ZSTR_VAL(str);
1224
280k
  size_t path_length = ZSTR_LEN(str);
1225
1226
280k
  ZEND_ASSERT(GC_REFCOUNT(ZCG(key)) == 1);
1227
280k
  ZSTR_LEN(ZCG(key)) = 0;
1228
1229
  /* CWD and include_path don't matter for absolute file names and streams */
1230
280k
  if (IS_ABSOLUTE_PATH(path, path_length)) {
1231
    /* pass */
1232
240k
  } else if (UNEXPECTED(php_is_stream_path(path))) {
1233
1.59k
    if (!is_cacheable_stream_path(path)) {
1234
1.59k
      return NULL;
1235
1.59k
    }
1236
    /* pass */
1237
39.1k
  } else if (UNEXPECTED(!ZCG(accel_directives).use_cwd)) {
1238
    /* pass */
1239
39.1k
  } else {
1240
39.1k
    const char *include_path = NULL, *cwd = NULL;
1241
39.1k
    size_t include_path_len = 0, cwd_len = 0;
1242
39.1k
    const zend_string *parent_script = NULL;
1243
1244
39.1k
    if (EXPECTED(ZCG(cwd_key_len))) {
1245
4.71k
      cwd = ZCG(cwd_key);
1246
4.71k
      cwd_len = ZCG(cwd_key_len);
1247
34.4k
    } else {
1248
34.4k
      zend_string *cwd_str = accel_getcwd();
1249
1250
34.4k
      if (UNEXPECTED(!cwd_str)) {
1251
        /* we don't handle this well for now. */
1252
0
        zend_accel_error(ACCEL_LOG_INFO, "getcwd() failed for '%s' (%d), please try to set opcache.use_cwd to 0 in ini file", path, errno);
1253
0
        return NULL;
1254
0
      }
1255
34.4k
      cwd = ZSTR_VAL(cwd_str);
1256
34.4k
      cwd_len = ZSTR_LEN(cwd_str);
1257
34.4k
      if (ZCG(cwd_check)) {
1258
34.4k
        ZCG(cwd_check) = false;
1259
34.4k
        if (ZCG(accelerator_enabled)) {
1260
1261
34.4k
          zend_string *str = accel_find_interned_string(cwd_str);
1262
34.4k
          if (!str) {
1263
4
            HANDLE_BLOCK_INTERRUPTIONS();
1264
4
            SHM_UNPROTECT();
1265
4
            zend_shared_alloc_lock();
1266
4
            str = accel_new_interned_string(zend_string_copy(cwd_str));
1267
4
            if (str == cwd_str) {
1268
0
              zend_string_release_ex(str, 0);
1269
0
              str = NULL;
1270
0
            }
1271
4
            zend_shared_alloc_unlock();
1272
4
            SHM_PROTECT();
1273
4
            HANDLE_UNBLOCK_INTERRUPTIONS();
1274
4
          }
1275
34.4k
          if (str) {
1276
34.4k
            char buf[32];
1277
34.4k
            const char *res = zend_print_long_to_buf(buf + sizeof(buf) - 1, STRTAB_STR_TO_POS(&ZCSG(interned_strings), str));
1278
1279
34.4k
            cwd_len = ZCG(cwd_key_len) = buf + sizeof(buf) - 1 - res;
1280
34.4k
            cwd = ZCG(cwd_key);
1281
34.4k
            memcpy(ZCG(cwd_key), res, cwd_len + 1);
1282
34.4k
          } else {
1283
0
            return NULL;
1284
0
          }
1285
34.4k
        } else {
1286
0
          return NULL;
1287
0
        }
1288
34.4k
      }
1289
34.4k
    }
1290
1291
39.1k
    if (EXPECTED(ZCG(include_path_key_len))) {
1292
4.71k
      include_path = ZCG(include_path_key);
1293
4.71k
      include_path_len = ZCG(include_path_key_len);
1294
34.4k
    } else if (!ZCG(include_path) || ZSTR_LEN(ZCG(include_path)) == 0) {
1295
0
      include_path = "";
1296
0
      include_path_len = 0;
1297
34.4k
    } else {
1298
34.4k
      include_path = ZSTR_VAL(ZCG(include_path));
1299
34.4k
      include_path_len = ZSTR_LEN(ZCG(include_path));
1300
1301
34.4k
      if (ZCG(include_path_check)) {
1302
34.4k
        ZCG(include_path_check) = false;
1303
34.4k
        if (ZCG(accelerator_enabled)) {
1304
1305
34.4k
          zend_string *str = accel_find_interned_string(ZCG(include_path));
1306
34.4k
          if (!str) {
1307
5
            HANDLE_BLOCK_INTERRUPTIONS();
1308
5
            SHM_UNPROTECT();
1309
5
            zend_shared_alloc_lock();
1310
5
            str = accel_new_interned_string(zend_string_copy(ZCG(include_path)));
1311
5
            if (str == ZCG(include_path)) {
1312
0
              zend_string_release(str);
1313
0
              str = NULL;
1314
0
            }
1315
5
            zend_shared_alloc_unlock();
1316
5
            SHM_PROTECT();
1317
5
            HANDLE_UNBLOCK_INTERRUPTIONS();
1318
5
          }
1319
34.4k
          if (str) {
1320
34.4k
            char buf[32];
1321
34.4k
            const char *res = zend_print_long_to_buf(buf + sizeof(buf) - 1, STRTAB_STR_TO_POS(&ZCSG(interned_strings), str));
1322
1323
34.4k
            include_path_len = ZCG(include_path_key_len) = buf + sizeof(buf) - 1 - res;
1324
34.4k
            include_path = ZCG(include_path_key);
1325
34.4k
            memcpy(ZCG(include_path_key), res, include_path_len + 1);
1326
34.4k
          } else {
1327
0
            return NULL;
1328
0
          }
1329
34.4k
        } else {
1330
0
          return NULL;
1331
0
        }
1332
34.4k
      }
1333
34.4k
    }
1334
1335
    /* Calculate key length */
1336
39.1k
    if (UNEXPECTED((size_t)(cwd_len + path_length + include_path_len + 2) >= ZCG_KEY_LEN)) {
1337
0
      return NULL;
1338
0
    }
1339
1340
    /* Generate key
1341
     * Note - the include_path must be the last element in the key,
1342
     * since in itself, it may include colons (which we use to separate
1343
     * different components of the key)
1344
     */
1345
39.1k
    char *key = ZSTR_VAL(ZCG(key));
1346
39.1k
    memcpy(key, path, path_length);
1347
39.1k
    key[path_length] = ':';
1348
39.1k
    size_t key_length = path_length + 1;
1349
39.1k
    memcpy(key + key_length, cwd, cwd_len);
1350
39.1k
    key_length += cwd_len;
1351
1352
39.1k
    if (include_path_len) {
1353
39.1k
      key[key_length] = ':';
1354
39.1k
      key_length += 1;
1355
39.1k
      memcpy(key + key_length, include_path, include_path_len);
1356
39.1k
      key_length += include_path_len;
1357
39.1k
    }
1358
1359
    /* Here we add to the key the parent script directory,
1360
     * since fopen_wrappers from version 4.0.7 use current script's path
1361
     * in include path too.
1362
     */
1363
39.1k
    if (EXPECTED(EG(current_execute_data)) &&
1364
5.13k
        EXPECTED((parent_script = zend_get_executed_filename_ex()) != NULL)) {
1365
1366
5.13k
      size_t parent_script_len = ZSTR_LEN(parent_script);
1367
56.4k
      while (parent_script_len > 0) {
1368
56.4k
        --parent_script_len;
1369
56.4k
        if (IS_SLASH(ZSTR_VAL(parent_script)[parent_script_len])) {
1370
5.13k
          break;
1371
5.13k
        }
1372
56.4k
      }
1373
1374
5.13k
      if (UNEXPECTED((size_t)(key_length + parent_script_len + 1) >= ZCG_KEY_LEN)) {
1375
0
        return NULL;
1376
0
      }
1377
5.13k
      key[key_length] = ':';
1378
5.13k
      key_length += 1;
1379
5.13k
      memcpy(key + key_length, ZSTR_VAL(parent_script), parent_script_len);
1380
5.13k
      key_length += parent_script_len;
1381
5.13k
    }
1382
39.1k
    key[key_length] = '\0';
1383
39.1k
    ZSTR_H(ZCG(key)) = 0;
1384
39.1k
    ZSTR_LEN(ZCG(key)) = key_length;
1385
39.1k
    return ZCG(key);
1386
39.1k
  }
1387
1388
  /* not use_cwd */
1389
240k
  return str;
1390
280k
}
1391
1392
/**
1393
 * Discard a #zend_persistent_script currently stored in shared
1394
 * memory.
1395
 *
1396
 * Caller must lock shared memory via zend_shared_alloc_lock().
1397
 */
1398
static void zend_accel_discard_script(zend_persistent_script *persistent_script)
1399
60.9k
{
1400
60.9k
  if (persistent_script->corrupted) {
1401
    /* already discarded */
1402
0
    return;
1403
0
  }
1404
1405
60.9k
  persistent_script->corrupted = true;
1406
60.9k
  persistent_script->timestamp = 0;
1407
60.9k
  ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
1408
60.9k
  if (ZSMMG(memory_exhausted)) {
1409
0
    zend_accel_restart_reason reason =
1410
0
      zend_accel_hash_is_full(&ZCSG(hash)) ? ACCEL_RESTART_HASH : ACCEL_RESTART_OOM;
1411
0
    zend_accel_schedule_restart_if_necessary(reason);
1412
0
  }
1413
60.9k
}
1414
1415
/**
1416
 * Wrapper for zend_accel_discard_script() which locks shared memory
1417
 * via zend_shared_alloc_lock().
1418
 */
1419
static void zend_accel_lock_discard_script(zend_persistent_script *persistent_script)
1420
60.9k
{
1421
60.9k
  zend_shared_alloc_lock();
1422
60.9k
  zend_accel_discard_script(persistent_script);
1423
60.9k
  zend_shared_alloc_unlock();
1424
60.9k
}
1425
1426
zend_result zend_accel_invalidate(zend_string *filename, bool force)
1427
72.5k
{
1428
72.5k
  zend_string *realpath;
1429
72.5k
  zend_persistent_script *persistent_script;
1430
72.5k
  zend_bool file_found = true;
1431
1432
72.5k
  if (!ZCG(accelerator_enabled) || accelerator_shm_read_lock() != SUCCESS) {
1433
0
    return FAILURE;
1434
0
  }
1435
1436
72.5k
  realpath = accelerator_orig_zend_resolve_path(filename);
1437
1438
72.5k
  if (!realpath) {
1439
    //file could have been deleted, but we still need to invalidate it.
1440
    //so instead of failing, just use the provided filename for the lookup
1441
0
    realpath = zend_string_copy(filename);
1442
0
    file_found = false;
1443
0
  }
1444
1445
72.5k
  if (ZCG(accel_directives).file_cache) {
1446
0
    zend_file_cache_invalidate(realpath);
1447
0
  }
1448
1449
72.5k
  persistent_script = zend_accel_hash_find(&ZCSG(hash), realpath);
1450
72.5k
  if (persistent_script && !persistent_script->corrupted) {
1451
60.9k
    zend_file_handle file_handle;
1452
60.9k
    zend_stream_init_filename_ex(&file_handle, realpath);
1453
60.9k
    file_handle.opened_path = realpath;
1454
1455
60.9k
    if (force ||
1456
0
      !ZCG(accel_directives).validate_timestamps ||
1457
60.9k
      do_validate_timestamps(persistent_script, &file_handle) == FAILURE) {
1458
60.9k
      HANDLE_BLOCK_INTERRUPTIONS();
1459
60.9k
      SHM_UNPROTECT();
1460
60.9k
      zend_accel_lock_discard_script(persistent_script);
1461
60.9k
      SHM_PROTECT();
1462
60.9k
      HANDLE_UNBLOCK_INTERRUPTIONS();
1463
60.9k
    }
1464
1465
60.9k
    file_handle.opened_path = NULL;
1466
60.9k
    zend_destroy_file_handle(&file_handle);
1467
60.9k
    file_found = true;
1468
60.9k
  }
1469
1470
72.5k
  accelerator_shm_read_unlock();
1471
72.5k
  zend_string_release_ex(realpath, 0);
1472
1473
72.5k
  return file_found ? SUCCESS : FAILURE;
1474
72.5k
}
1475
1476
static zend_string* accel_new_interned_key(zend_string *key)
1477
0
{
1478
0
  zend_string *new_key;
1479
1480
0
  if (zend_accel_in_shm(key)) {
1481
0
    return key;
1482
0
  }
1483
0
  GC_ADDREF(key);
1484
0
  new_key = accel_new_interned_string(key);
1485
0
  if (UNEXPECTED(new_key == key)) {
1486
0
    GC_DELREF(key);
1487
0
    new_key = zend_shared_alloc(ZEND_MM_ALIGNED_SIZE_EX(_ZSTR_STRUCT_SIZE(ZSTR_LEN(key)), 8));
1488
0
    if (EXPECTED(new_key)) {
1489
0
      GC_SET_REFCOUNT(new_key, 2);
1490
0
      GC_TYPE_INFO(new_key) = GC_STRING | (IS_STR_INTERNED << GC_FLAGS_SHIFT);
1491
0
      ZSTR_H(new_key) = ZSTR_H(key);
1492
0
      ZSTR_LEN(new_key) = ZSTR_LEN(key);
1493
0
      memcpy(ZSTR_VAL(new_key), ZSTR_VAL(key), ZSTR_LEN(new_key) + 1);
1494
0
    }
1495
0
  }
1496
0
  return new_key;
1497
0
}
1498
1499
/* Adds another key for existing cached script */
1500
static void zend_accel_add_key(zend_string *key, zend_accel_hash_entry *bucket)
1501
0
{
1502
0
  if (!zend_accel_hash_find(&ZCSG(hash), key)) {
1503
0
    if (zend_accel_hash_is_full(&ZCSG(hash))) {
1504
0
      zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1505
0
      ZSMMG(memory_exhausted) = true;
1506
0
      zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
1507
0
    } else {
1508
0
      zend_string *new_key = accel_new_interned_key(key);
1509
0
      if (new_key) {
1510
0
        if (zend_accel_hash_update(&ZCSG(hash), new_key, 1, bucket)) {
1511
0
          zend_accel_error(ACCEL_LOG_INFO, "Added key '%s'", ZSTR_VAL(new_key));
1512
0
        }
1513
0
      } else {
1514
0
        zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
1515
0
      }
1516
0
    }
1517
0
  }
1518
0
}
1519
1520
static zend_always_inline bool is_phar_file(const zend_string *filename)
1521
61.8k
{
1522
61.8k
  return filename &&
1523
61.8k
    zend_string_ends_with_literal(filename, ".phar") &&
1524
0
    !strstr(ZSTR_VAL(filename), "://");
1525
61.8k
}
1526
1527
static zend_persistent_script *store_script_in_file_cache(zend_persistent_script *new_persistent_script)
1528
0
{
1529
0
  uint32_t memory_used;
1530
1531
0
  zend_shared_alloc_init_xlat_table();
1532
1533
  /* Calculate the required memory size */
1534
0
  memory_used = zend_accel_script_persist_calc(new_persistent_script, 0);
1535
1536
  /* Allocate memory block */
1537
0
#if defined(__AVX__) || defined(__SSE2__)
1538
  /* Align to 64-byte boundary */
1539
0
  ZCG(mem) = zend_arena_alloc(&CG(arena), memory_used + 64);
1540
0
  ZCG(mem) = (void*)(((uintptr_t)ZCG(mem) + 63L) & ~63L);
1541
#elif ZEND_MM_NEED_EIGHT_BYTE_REALIGNMENT
1542
  /* Align to 8-byte boundary */
1543
  ZCG(mem) = zend_arena_alloc(&CG(arena), memory_used + 8);
1544
  ZCG(mem) = (void*)(((uintptr_t)ZCG(mem) + 7L) & ~7L);
1545
#else
1546
  ZCG(mem) = zend_arena_alloc(&CG(arena), memory_used);
1547
#endif
1548
1549
0
  zend_shared_alloc_clear_xlat_table();
1550
1551
  /* Copy into memory block */
1552
0
  new_persistent_script = zend_accel_script_persist(new_persistent_script, 0);
1553
1554
0
  zend_shared_alloc_destroy_xlat_table();
1555
1556
0
  new_persistent_script->is_phar = is_phar_file(new_persistent_script->script.filename);
1557
1558
  /* Consistency check */
1559
0
  if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
1560
0
    zend_accel_error(
1561
0
      ((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
1562
0
      "Internal error: wrong size calculation: %s start=" ZEND_ADDR_FMT ", end=" ZEND_ADDR_FMT ", real=" ZEND_ADDR_FMT "\n",
1563
0
      ZSTR_VAL(new_persistent_script->script.filename),
1564
0
      (size_t)new_persistent_script->mem,
1565
0
      (size_t)((char *)new_persistent_script->mem + new_persistent_script->size),
1566
0
      (size_t)ZCG(mem));
1567
0
  }
1568
1569
0
  zend_file_cache_script_store(new_persistent_script, /* is_shm */ false);
1570
1571
0
  return new_persistent_script;
1572
0
}
1573
1574
static zend_persistent_script *cache_script_in_file_cache(zend_persistent_script *new_persistent_script, bool *from_shared_memory)
1575
0
{
1576
0
  uint32_t orig_compiler_options;
1577
1578
0
  orig_compiler_options = CG(compiler_options);
1579
0
  CG(compiler_options) |= ZEND_COMPILE_WITH_FILE_CACHE;
1580
0
  zend_optimize_script(&new_persistent_script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level);
1581
0
  zend_accel_finalize_delayed_early_binding_list(new_persistent_script);
1582
0
  CG(compiler_options) = orig_compiler_options;
1583
1584
0
  *from_shared_memory = true;
1585
0
  return store_script_in_file_cache(new_persistent_script);
1586
0
}
1587
1588
static zend_persistent_script *cache_script_in_shared_memory(zend_persistent_script *new_persistent_script, zend_string *key, bool *from_shared_memory)
1589
61.8k
{
1590
61.8k
  zend_accel_hash_entry *bucket;
1591
61.8k
  uint32_t memory_used;
1592
61.8k
  uint32_t orig_compiler_options;
1593
1594
61.8k
  orig_compiler_options = CG(compiler_options);
1595
61.8k
  if (ZCG(accel_directives).file_cache) {
1596
0
    CG(compiler_options) |= ZEND_COMPILE_WITH_FILE_CACHE;
1597
0
  }
1598
61.8k
  zend_optimize_script(&new_persistent_script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level);
1599
61.8k
  zend_accel_finalize_delayed_early_binding_list(new_persistent_script);
1600
61.8k
  CG(compiler_options) = orig_compiler_options;
1601
1602
  /* exclusive lock */
1603
61.8k
  zend_shared_alloc_lock();
1604
1605
  /* Check if we still need to put the file into the cache (may be it was
1606
   * already stored by another process. This final check is done under
1607
   * exclusive lock) */
1608
61.8k
  bucket = zend_accel_hash_find_entry(&ZCSG(hash), new_persistent_script->script.filename);
1609
61.8k
  if (bucket) {
1610
60.9k
    zend_persistent_script *existing_persistent_script = (zend_persistent_script *)bucket->data;
1611
1612
60.9k
    if (!existing_persistent_script->corrupted) {
1613
0
      if (key &&
1614
0
          (!ZCG(accel_directives).validate_timestamps ||
1615
0
           (new_persistent_script->timestamp == existing_persistent_script->timestamp))) {
1616
0
        zend_accel_add_key(key, bucket);
1617
0
      }
1618
0
      zend_shared_alloc_unlock();
1619
0
#if 1
1620
      /* prefer the script already stored in SHM */
1621
0
      free_persistent_script(new_persistent_script, 1);
1622
0
      *from_shared_memory = true;
1623
0
      return existing_persistent_script;
1624
#else
1625
      return new_persistent_script;
1626
#endif
1627
0
    }
1628
60.9k
  }
1629
1630
61.8k
  if (zend_accel_hash_is_full(&ZCSG(hash))) {
1631
0
    zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1632
0
    ZSMMG(memory_exhausted) = true;
1633
0
    zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
1634
0
    zend_shared_alloc_unlock();
1635
0
    if (ZCG(accel_directives).file_cache) {
1636
0
      new_persistent_script = store_script_in_file_cache(new_persistent_script);
1637
0
      *from_shared_memory = true;
1638
0
    }
1639
0
    return new_persistent_script;
1640
0
  }
1641
1642
61.8k
  zend_shared_alloc_init_xlat_table();
1643
1644
  /* Calculate the required memory size */
1645
61.8k
  memory_used = zend_accel_script_persist_calc(new_persistent_script, 1);
1646
1647
  /* Allocate shared memory */
1648
61.8k
  ZCG(mem) = zend_shared_alloc_aligned(memory_used);
1649
61.8k
  if (!ZCG(mem)) {
1650
0
    zend_shared_alloc_destroy_xlat_table();
1651
0
    zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
1652
0
    zend_shared_alloc_unlock();
1653
0
    if (ZCG(accel_directives).file_cache) {
1654
0
      new_persistent_script = store_script_in_file_cache(new_persistent_script);
1655
0
      *from_shared_memory = true;
1656
0
    }
1657
0
    return new_persistent_script;
1658
0
  }
1659
1660
61.8k
  bzero_aligned(ZCG(mem), memory_used);
1661
1662
61.8k
  zend_shared_alloc_clear_xlat_table();
1663
1664
  /* Copy into shared memory */
1665
61.8k
  new_persistent_script = zend_accel_script_persist(new_persistent_script, 1);
1666
1667
61.8k
  zend_shared_alloc_destroy_xlat_table();
1668
1669
61.8k
  new_persistent_script->is_phar = is_phar_file(new_persistent_script->script.filename);
1670
1671
  /* Consistency check */
1672
61.8k
  if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
1673
0
    zend_accel_error(
1674
0
      ((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
1675
0
      "Internal error: wrong size calculation: %s start=" ZEND_ADDR_FMT ", end=" ZEND_ADDR_FMT ", real=" ZEND_ADDR_FMT "\n",
1676
0
      ZSTR_VAL(new_persistent_script->script.filename),
1677
0
      (size_t)new_persistent_script->mem,
1678
0
      (size_t)((char *)new_persistent_script->mem + new_persistent_script->size),
1679
0
      (size_t)ZCG(mem));
1680
0
  }
1681
1682
  /* store script structure in the hash table */
1683
61.8k
  bucket = zend_accel_hash_update(&ZCSG(hash), new_persistent_script->script.filename, 0, new_persistent_script);
1684
61.8k
  if (bucket) {
1685
61.8k
    zend_accel_error(ACCEL_LOG_INFO, "Cached script '%s'", ZSTR_VAL(new_persistent_script->script.filename));
1686
61.8k
    if (key &&
1687
        /* key may contain non-persistent PHAR aliases (see issues #115 and #149) */
1688
60.9k
        !zend_string_starts_with_literal(key, "phar://") &&
1689
60.9k
        !zend_string_equals(new_persistent_script->script.filename, key)) {
1690
      /* link key to the same persistent script in hash table */
1691
0
      zend_string *new_key = accel_new_interned_key(key);
1692
1693
0
      if (new_key) {
1694
0
        if (zend_accel_hash_update(&ZCSG(hash), new_key, 1, bucket)) {
1695
0
          zend_accel_error(ACCEL_LOG_INFO, "Added key '%s'", ZSTR_VAL(key));
1696
0
        } else {
1697
0
          zend_accel_error(ACCEL_LOG_DEBUG, "No more entries in hash table!");
1698
0
          ZSMMG(memory_exhausted) = true;
1699
0
          zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_HASH);
1700
0
        }
1701
0
      } else {
1702
0
        zend_accel_schedule_restart_if_necessary(ACCEL_RESTART_OOM);
1703
0
      }
1704
0
    }
1705
61.8k
  }
1706
1707
61.8k
  new_persistent_script->dynamic_members.memory_consumption = ZEND_ALIGNED_SIZE(new_persistent_script->size);
1708
1709
61.8k
  zend_shared_alloc_unlock();
1710
1711
61.8k
  if (ZCG(accel_directives).file_cache) {
1712
0
    SHM_PROTECT();
1713
0
    zend_file_cache_script_store(new_persistent_script, /* is_shm */ true);
1714
0
    SHM_UNPROTECT();
1715
0
  }
1716
1717
61.8k
  *from_shared_memory = true;
1718
61.8k
  return new_persistent_script;
1719
61.8k
}
1720
1721
172
#define ZEND_AUTOGLOBAL_MASK_SERVER  (1 << 0)
1722
99
#define ZEND_AUTOGLOBAL_MASK_ENV     (1 << 1)
1723
93
#define ZEND_AUTOGLOBAL_MASK_REQUEST (1 << 2)
1724
1725
static int zend_accel_get_auto_globals(void)
1726
60.9k
{
1727
60.9k
  int mask = 0;
1728
60.9k
  if (zend_hash_exists(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_SERVER))) {
1729
79
    mask |= ZEND_AUTOGLOBAL_MASK_SERVER;
1730
79
  }
1731
60.9k
  if (zend_hash_exists(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_ENV))) {
1732
6
    mask |= ZEND_AUTOGLOBAL_MASK_ENV;
1733
6
  }
1734
60.9k
  if (zend_hash_exists(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_REQUEST))) {
1735
0
    mask |= ZEND_AUTOGLOBAL_MASK_REQUEST;
1736
0
  }
1737
60.9k
  return mask;
1738
60.9k
}
1739
1740
static void zend_accel_set_auto_globals(int mask)
1741
93
{
1742
93
  if (mask & ZEND_AUTOGLOBAL_MASK_SERVER) {
1743
85
    zend_is_auto_global(ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_SERVER));
1744
85
  }
1745
93
  if (mask & ZEND_AUTOGLOBAL_MASK_ENV) {
1746
8
    zend_is_auto_global(ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_ENV));
1747
8
  }
1748
93
  if (mask & ZEND_AUTOGLOBAL_MASK_REQUEST) {
1749
0
    zend_is_auto_global(ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_REQUEST));
1750
0
  }
1751
93
  ZCG(auto_globals_mask) |= mask;
1752
93
}
1753
1754
static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handle, int type, zend_op_array **op_array_p)
1755
124k
{
1756
124k
  zend_persistent_script *new_persistent_script;
1757
124k
  uint32_t orig_functions_count, orig_class_count;
1758
124k
  zend_op_array *orig_active_op_array;
1759
124k
  zend_op_array *op_array;
1760
124k
  bool do_bailout = false;
1761
124k
  accel_time_t timestamp = 0;
1762
124k
  uint32_t orig_compiler_options = 0;
1763
1764
  /* Try to open file */
1765
124k
  if (file_handle->type == ZEND_HANDLE_FILENAME) {
1766
6
    if (accelerator_orig_zend_stream_open_function(file_handle) != SUCCESS) {
1767
0
      *op_array_p = NULL;
1768
0
      if (!EG(exception)) {
1769
0
        if (type == ZEND_REQUIRE) {
1770
0
          zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, ZSTR_VAL(file_handle->filename));
1771
0
        } else {
1772
0
          zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, ZSTR_VAL(file_handle->filename));
1773
0
        }
1774
0
      }
1775
0
      return NULL;
1776
0
    }
1777
6
  }
1778
1779
  /* check blacklist right after ensuring that file was opened */
1780
124k
  if (file_handle->opened_path && zend_accel_blacklist_is_blacklisted(&accel_blacklist, ZSTR_VAL(file_handle->opened_path), ZSTR_LEN(file_handle->opened_path))) {
1781
0
    SHM_UNPROTECT();
1782
0
    ZCSG(blacklist_misses)++;
1783
0
    SHM_PROTECT();
1784
0
    *op_array_p = accelerator_orig_compile_file(file_handle, type);
1785
0
    return NULL;
1786
0
  }
1787
1788
124k
  if (ZCG(accel_directives).validate_timestamps ||
1789
76.5k
      ZCG(accel_directives).file_update_protection ||
1790
76.5k
      ZCG(accel_directives).max_file_size > 0) {
1791
47.6k
    size_t size = 0;
1792
1793
    /* Obtain the file timestamps, *before* actually compiling them,
1794
     * otherwise we have a race-condition.
1795
     */
1796
47.6k
    timestamp = zend_get_file_handle_timestamp(file_handle, ZCG(accel_directives).max_file_size > 0 ? &size : NULL);
1797
1798
    /* If we can't obtain a timestamp (that means file is possibly socket)
1799
     *  we won't cache it
1800
     */
1801
47.6k
    if (timestamp == 0) {
1802
47.6k
      *op_array_p = accelerator_orig_compile_file(file_handle, type);
1803
47.6k
      return NULL;
1804
47.6k
    }
1805
1806
    /* check if file is too new (may be it's not written completely yet) */
1807
4
    if (ZCG(accel_directives).file_update_protection &&
1808
4
        ((accel_time_t)(ZCG(request_time) - ZCG(accel_directives).file_update_protection) < timestamp)) {
1809
3
      *op_array_p = accelerator_orig_compile_file(file_handle, type);
1810
3
      return NULL;
1811
3
    }
1812
1813
1
    if (ZCG(accel_directives).max_file_size > 0 && size > (size_t)ZCG(accel_directives).max_file_size) {
1814
0
      SHM_UNPROTECT();
1815
0
      ZCSG(blacklist_misses)++;
1816
0
      SHM_PROTECT();
1817
0
      *op_array_p = accelerator_orig_compile_file(file_handle, type);
1818
0
      return NULL;
1819
0
    }
1820
1
  }
1821
1822
  /* Save the original values for the op_array, function table and class table */
1823
76.5k
  orig_active_op_array = CG(active_op_array);
1824
76.5k
  orig_functions_count = EG(function_table)->nNumUsed;
1825
76.5k
  orig_class_count = EG(class_table)->nNumUsed;
1826
1827
76.5k
  zend_try {
1828
76.5k
    orig_compiler_options = CG(compiler_options);
1829
76.5k
    CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
1830
76.5k
    CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING;
1831
76.5k
    CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION;
1832
76.5k
    CG(compiler_options) |= ZEND_COMPILE_IGNORE_OTHER_FILES;
1833
76.5k
    CG(compiler_options) |= ZEND_COMPILE_IGNORE_OBSERVER;
1834
#ifdef ZEND_WIN32
1835
    /* On Windows, don't compile with internal classes. Shm may be attached from different
1836
     * processes with internal classes living in different addresses. */
1837
    CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES;
1838
#endif
1839
76.5k
    if (ZCG(accel_directives).file_cache) {
1840
0
      CG(compiler_options) |= ZEND_COMPILE_WITH_FILE_CACHE;
1841
      /* Don't compile with internal classes for file cache, in case some extension is removed
1842
       * later on. We cannot assume it is there in the future. */
1843
0
      CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES;
1844
0
    }
1845
76.5k
    op_array = *op_array_p = accelerator_orig_compile_file(file_handle, type);
1846
76.5k
    CG(compiler_options) = orig_compiler_options;
1847
76.5k
  } zend_catch {
1848
2.78k
    op_array = NULL;
1849
2.78k
    do_bailout = true;
1850
2.78k
    CG(compiler_options) = orig_compiler_options;
1851
76.5k
  } zend_end_try();
1852
1853
  /* Restore originals */
1854
76.5k
  CG(active_op_array) = orig_active_op_array;
1855
1856
76.5k
  if (!op_array) {
1857
    /* compilation failed */
1858
15.6k
    if (do_bailout) {
1859
2.78k
      EG(record_errors) = false;
1860
2.78k
      zend_free_recorded_errors();
1861
2.78k
      zend_bailout();
1862
2.78k
    }
1863
12.8k
    return NULL;
1864
15.6k
  }
1865
1866
  /* Build the persistent_script structure.
1867
     Here we aren't sure we would store it, but we will need it
1868
     further anyway.
1869
  */
1870
60.9k
  new_persistent_script = create_persistent_script();
1871
60.9k
  new_persistent_script->script.main_op_array = *op_array;
1872
60.9k
  zend_accel_move_user_functions(CG(function_table), CG(function_table)->nNumUsed - orig_functions_count, &new_persistent_script->script);
1873
60.9k
  zend_accel_move_user_classes(CG(class_table), CG(class_table)->nNumUsed - orig_class_count, &new_persistent_script->script);
1874
60.9k
  zend_accel_build_delayed_early_binding_list(new_persistent_script);
1875
1876
60.9k
  efree(op_array); /* we have valid persistent_script, so it's safe to free op_array */
1877
1878
  /* Fill in the ping_auto_globals_mask for the new script. If jit for auto globals is enabled we
1879
     will have to ping the used auto global variables before execution */
1880
60.9k
  if (PG(auto_globals_jit)) {
1881
60.9k
    new_persistent_script->ping_auto_globals_mask = zend_accel_get_auto_globals();
1882
60.9k
  }
1883
1884
60.9k
  if (ZCG(accel_directives).validate_timestamps) {
1885
    /* Obtain the file timestamps, *before* actually compiling them,
1886
     * otherwise we have a race-condition.
1887
     */
1888
1
    new_persistent_script->timestamp = timestamp;
1889
1
    new_persistent_script->dynamic_members.revalidate = ZCG(request_time) + ZCG(accel_directives).revalidate_freq;
1890
1
  }
1891
1892
60.9k
  if (file_handle->opened_path) {
1893
7
    new_persistent_script->script.filename = zend_string_copy(file_handle->opened_path);
1894
60.9k
  } else {
1895
60.9k
    new_persistent_script->script.filename = zend_string_copy(file_handle->filename);
1896
60.9k
  }
1897
60.9k
  zend_string_hash_val(new_persistent_script->script.filename);
1898
1899
  /* Now persistent_script structure is ready in process memory */
1900
60.9k
  return new_persistent_script;
1901
76.5k
}
1902
1903
static zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int type)
1904
0
{
1905
0
  zend_persistent_script *persistent_script;
1906
0
  zend_op_array *op_array = NULL;
1907
0
  bool from_memory; /* if the script we've got is stored in SHM */
1908
1909
0
  if (php_is_stream_path(ZSTR_VAL(file_handle->filename)) &&
1910
0
      !is_cacheable_stream_path(ZSTR_VAL(file_handle->filename))) {
1911
0
    return accelerator_orig_compile_file(file_handle, type);
1912
0
  }
1913
1914
0
  if (!file_handle->opened_path) {
1915
0
    if (file_handle->type == ZEND_HANDLE_FILENAME &&
1916
0
        accelerator_orig_zend_stream_open_function(file_handle) == FAILURE) {
1917
0
      if (!EG(exception)) {
1918
0
        if (type == ZEND_REQUIRE) {
1919
0
          zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, ZSTR_VAL(file_handle->filename));
1920
0
        } else {
1921
0
          zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, ZSTR_VAL(file_handle->filename));
1922
0
        }
1923
0
      }
1924
0
      return NULL;
1925
0
      }
1926
0
  }
1927
1928
0
  HANDLE_BLOCK_INTERRUPTIONS();
1929
0
  SHM_UNPROTECT();
1930
0
  persistent_script = zend_file_cache_script_load(file_handle);
1931
0
  SHM_PROTECT();
1932
0
  HANDLE_UNBLOCK_INTERRUPTIONS();
1933
0
  if (persistent_script) {
1934
    /* see bug #15471 (old BTS) */
1935
0
    if (persistent_script->script.filename) {
1936
0
      if (!EG(current_execute_data) || !EG(current_execute_data)->opline ||
1937
0
          !EG(current_execute_data)->func ||
1938
0
          !ZEND_USER_CODE(EG(current_execute_data)->func->common.type) ||
1939
0
          EG(current_execute_data)->opline->opcode != ZEND_INCLUDE_OR_EVAL ||
1940
0
          (EG(current_execute_data)->opline->extended_value != ZEND_INCLUDE_ONCE &&
1941
0
           EG(current_execute_data)->opline->extended_value != ZEND_REQUIRE_ONCE)) {
1942
0
        if (zend_hash_add_empty_element(&EG(included_files), persistent_script->script.filename) != NULL) {
1943
          /* ext/phar has to load phar's metadata into memory */
1944
0
          if (persistent_script->is_phar) {
1945
0
            php_stream_statbuf ssb;
1946
0
            char *fname = zend_cstr_concat(
1947
0
              "phar://", sizeof("phar://") - 1,
1948
0
              ZSTR_VAL(persistent_script->script.filename),
1949
0
              ZSTR_LEN(persistent_script->script.filename));
1950
0
            php_stream_stat_path(fname, &ssb);
1951
0
            efree(fname);
1952
0
          }
1953
0
        }
1954
0
      }
1955
0
    }
1956
0
    zend_emit_recorded_errors_ex(persistent_script->num_warnings, persistent_script->warnings);
1957
1958
0
      if (persistent_script->ping_auto_globals_mask & ~ZCG(auto_globals_mask)) {
1959
0
      zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask & ~ZCG(auto_globals_mask));
1960
0
    }
1961
1962
0
    return zend_accel_load_script(persistent_script, 1);
1963
0
  }
1964
1965
0
  zend_begin_record_errors();
1966
1967
0
  persistent_script = opcache_compile_file(file_handle, type, &op_array);
1968
1969
0
  if (persistent_script) {
1970
0
    if (ZCG(accel_directives).record_warnings) {
1971
0
      persistent_script->num_warnings = EG(errors).size;
1972
0
      persistent_script->warnings = EG(errors).errors;
1973
0
    }
1974
1975
0
    from_memory = false;
1976
0
    persistent_script = cache_script_in_file_cache(persistent_script, &from_memory);
1977
1978
0
    zend_emit_recorded_errors();
1979
0
    zend_free_recorded_errors();
1980
1981
0
    return zend_accel_load_script(persistent_script, from_memory);
1982
0
  }
1983
1984
0
  zend_emit_recorded_errors();
1985
0
  zend_free_recorded_errors();
1986
1987
0
  return op_array;
1988
0
}
1989
1990
static bool check_persistent_script_access(const zend_persistent_script *persistent_script)
1991
0
{
1992
0
  char *phar_path, *ptr;
1993
0
  if ((ZSTR_LEN(persistent_script->script.filename)<sizeof("phar://.phar")) ||
1994
0
      memcmp(ZSTR_VAL(persistent_script->script.filename), "phar://", sizeof("phar://")-1)) {
1995
1996
0
    return access(ZSTR_VAL(persistent_script->script.filename), R_OK) != 0;
1997
1998
0
  } else {
1999
    /* we got a cached file from .phar, so we have to strip prefix and path inside .phar to check access() */
2000
0
    phar_path = estrdup(ZSTR_VAL(persistent_script->script.filename)+sizeof("phar://")-1);
2001
0
    if ((ptr = strstr(phar_path, ".phar/")) != NULL)
2002
0
    {
2003
0
      *(ptr+sizeof(".phar/")-2) = 0; /* strip path inside .phar file */
2004
0
    }
2005
0
    bool ret = access(phar_path, R_OK) != 0;
2006
0
    efree(phar_path);
2007
0
    return ret;
2008
0
  }
2009
0
}
2010
2011
static const char hexchars[] = "0123456789abcdef";
2012
2013
static char *zend_accel_uintptr_hex(char *dest, uintptr_t n)
2014
5.04k
{
2015
53.6k
  do {
2016
53.6k
    *dest++ = hexchars[n & 0xf];
2017
53.6k
    n >>= 4;
2018
53.6k
  } while (n);
2019
2020
5.04k
  return dest;
2021
5.04k
}
2022
2023
/* Prevents collisions with real scripts, as we don't cache paths prefixed with
2024
 * a scheme, except file:// and phar://. */
2025
7.56k
#define PFA_KEY_PREFIX "pfa://"
2026
2027
static zend_string *zend_accel_pfa_key(const uint32_t *declaring_lineno_ptr,
2028
    const zend_function *called_function)
2029
2.52k
{
2030
2.52k
  const size_t max_key_len = strlen(PFA_KEY_PREFIX) + (sizeof(uintptr_t)*2) + strlen(":") + (sizeof(uintptr_t)*2);
2031
2.52k
  zend_string *key = zend_string_alloc(max_key_len, 0);
2032
2033
2.52k
  char *dest = ZSTR_VAL(key);
2034
2.52k
  dest = zend_mempcpy(dest, PFA_KEY_PREFIX, strlen(PFA_KEY_PREFIX));
2035
2.52k
  dest = zend_accel_uintptr_hex(dest, (uintptr_t)declaring_lineno_ptr);
2036
2.52k
  *dest++ = ':';
2037
2038
2.52k
  const void *ptr;
2039
2.52k
  if ((called_function->common.fn_flags & ZEND_ACC_CLOSURE)
2040
333
      && called_function->type == ZEND_USER_FUNCTION) {
2041
    /* Can not use 'called_function' as part of the key, as it's an inner
2042
     * pointer to a Closure, which may be freed. Use its opcodes instead.
2043
     * zend_accel_compile_pfa() ensures to extend the lifetime of opcodes
2044
     * in this case. */
2045
333
    ptr = called_function->op_array.opcodes;
2046
2.19k
  } else {
2047
2.19k
    ptr = called_function;
2048
2.19k
  }
2049
2.52k
  dest = zend_accel_uintptr_hex(dest, (uintptr_t)ptr);
2050
2051
2.52k
  *dest = '\0';
2052
2.52k
  ZSTR_LEN(key) = dest - ZSTR_VAL(key);
2053
2054
2.52k
  return key;
2055
2.52k
}
2056
2057
const zend_op_array *zend_accel_pfa_cache_get(
2058
    const uint32_t *declaring_lineno_ptr, const zend_function *called_function, bool cacheable_in_shm)
2059
1.47k
{
2060
1.47k
  zend_string *key = zend_accel_pfa_key(declaring_lineno_ptr, called_function);
2061
1.47k
  zend_op_array *op_array = NULL;
2062
2063
  /* A PFA is SHM-cacheable if the declaring op_array and called_function are
2064
   * cached. */
2065
1.47k
  if (ZCG(accelerator_enabled)
2066
1.47k
      && !file_cache_only
2067
1.47k
      && cacheable_in_shm /* declaring op_array is cached */
2068
1.35k
      && (called_function->type != ZEND_USER_FUNCTION || !called_function->op_array.refcount)) {
2069
1.35k
    zend_persistent_script *persistent_script = zend_accel_hash_find(&ZCSG(hash), key);
2070
1.35k
    if (persistent_script) {
2071
284
      op_array = persistent_script->script.main_op_array.dynamic_func_defs[0];
2072
284
      if (persistent_script->num_warnings) {
2073
0
        zend_emit_recorded_errors_ex(persistent_script->num_warnings,
2074
0
            persistent_script->warnings);
2075
0
      }
2076
284
      if (ZCSG(map_ptr_last) > CG(map_ptr_last)) {
2077
0
        zend_map_ptr_extend(ZCSG(map_ptr_last));
2078
0
      }
2079
284
    }
2080
1.35k
  } else {
2081
122
    op_array = zend_hash_find_ptr(&EG(partial_function_application_cache), key);
2082
122
  }
2083
2084
1.47k
  zend_string_release(key);
2085
2086
1.47k
  return op_array;
2087
1.47k
}
2088
2089
zend_op_array *zend_accel_compile_pfa(zend_ast *ast,
2090
    zend_string *declaring_filename,
2091
    const uint32_t *declaring_lineno_ptr,
2092
    const zend_function *called_function,
2093
    zend_string *pfa_func_name, bool cacheable_in_shm)
2094
1.04k
{
2095
1.04k
  ZEND_ASSERT(zend_accel_in_shm((void*)declaring_lineno_ptr) || !cacheable_in_shm);
2096
2097
1.04k
  zend_begin_record_errors();
2098
1.04k
  zend_op_array *op_array;
2099
2100
1.04k
  uint32_t orig_compiler_options = CG(compiler_options);
2101
2102
1.04k
  zend_try {
2103
1.04k
    CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
2104
1.04k
    CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING;
2105
1.04k
    CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION;
2106
1.04k
    CG(compiler_options) |= ZEND_COMPILE_IGNORE_OTHER_FILES;
2107
1.04k
    CG(compiler_options) |= ZEND_COMPILE_IGNORE_OBSERVER;
2108
#ifdef ZEND_WIN32
2109
    /* On Windows, don't compile with internal classes. Shm may be attached from different
2110
     * processes with internal classes living in different addresses. */
2111
    CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES;
2112
#endif
2113
2114
1.04k
    op_array = zend_compile_ast(ast, ZEND_USER_FUNCTION, declaring_filename);
2115
2116
1.04k
    CG(compiler_options) = orig_compiler_options;
2117
1.04k
  } zend_catch {
2118
0
    CG(compiler_options) = orig_compiler_options;
2119
0
    zend_emit_recorded_errors();
2120
0
    zend_free_recorded_errors();
2121
0
    zend_bailout();
2122
1.04k
  } zend_end_try();
2123
2124
1.04k
  ZEND_ASSERT(op_array->num_dynamic_func_defs == 1);
2125
2126
1.04k
  zend_string_release(op_array->dynamic_func_defs[0]->function_name);
2127
1.04k
  op_array->dynamic_func_defs[0]->function_name = zend_string_copy(pfa_func_name);
2128
2129
1.04k
  zend_string *key = zend_accel_pfa_key(declaring_lineno_ptr, called_function);
2130
2131
  /* Cache op_array only if the declaring op_array and the called function
2132
   * are cached */
2133
1.04k
  if (!ZCG(accelerator_enabled)
2134
1.04k
      || file_cache_only
2135
1.04k
      || !cacheable_in_shm /* declaring op_array is not in SHM */
2136
932
      || (called_function->type == ZEND_USER_FUNCTION && called_function->op_array.refcount)
2137
932
      || (ZCSG(restart_in_progress) && accel_restart_is_active())
2138
932
      || (!ZCG(counted) && accel_activate_add() == FAILURE)) {
2139
114
    zend_op_array *script_op_array = op_array;
2140
114
    zend_op_array *op_array = script_op_array->dynamic_func_defs[0];
2141
114
    GC_TRY_ADDREF(op_array->function_name);
2142
114
    (*op_array->refcount)++;
2143
114
    destroy_op_array(script_op_array);
2144
114
    efree(script_op_array);
2145
2146
114
    if ((called_function->common.fn_flags & ZEND_ACC_CLOSURE)
2147
23
        && called_function->type == ZEND_USER_FUNCTION
2148
23
        && called_function->op_array.refcount) {
2149
      /* Extend the lifetime of the called opcodes if
2150
       * the called function is a closure.
2151
       * See comment in zend_accel_pfa_key(). */
2152
23
      zend_op_array *copy = zend_arena_alloc(&CG(arena), sizeof(*copy));
2153
23
      memcpy(copy, called_function, sizeof(*copy));
2154
23
      function_add_ref((zend_function *) copy);
2155
      /* Reference the copy in op_array->dynamic_func_defs so that it's
2156
       * destroyed when op_array is destroyed. */
2157
23
      ZEND_ASSERT(!op_array->dynamic_func_defs && !op_array->num_dynamic_func_defs);
2158
23
      op_array->dynamic_func_defs = safe_emalloc(1, sizeof(*op_array->dynamic_func_defs), 0);
2159
23
      op_array->dynamic_func_defs[0] = copy;
2160
23
      op_array->num_dynamic_func_defs = 1;
2161
23
    }
2162
2163
114
    zend_hash_add_new_ptr(&EG(partial_function_application_cache), key, op_array);
2164
114
    zend_string_release(key);
2165
2166
114
    zend_emit_recorded_errors();
2167
114
    zend_free_recorded_errors();
2168
2169
114
    return op_array;
2170
114
  }
2171
2172
932
  zend_persistent_script *new_persistent_script = create_persistent_script();
2173
932
  new_persistent_script->script.main_op_array = *op_array;
2174
932
  efree_size(op_array, sizeof(*op_array));
2175
932
  new_persistent_script->script.filename = key;
2176
2177
932
  if (ZCG(accel_directives).record_warnings) {
2178
0
    new_persistent_script->num_warnings = EG(errors).size;
2179
0
    new_persistent_script->warnings = EG(errors).errors;
2180
0
  }
2181
2182
932
  HANDLE_BLOCK_INTERRUPTIONS();
2183
932
  SHM_UNPROTECT();
2184
2185
932
  bool from_shared_memory;
2186
  /* See GH-17246: we disable GC so that user code cannot be executed during the optimizer run. */
2187
932
  bool orig_gc_state = gc_enable(false);
2188
932
  char *orig_file_cache = ZCG(accel_directives).file_cache;
2189
  /* Disable file_cache temporarily, as we can't guarantee consistency. */
2190
932
  ZCG(accel_directives).file_cache = NULL;
2191
932
  new_persistent_script = cache_script_in_shared_memory(new_persistent_script, NULL, &from_shared_memory);
2192
932
  ZCG(accel_directives).file_cache = orig_file_cache;
2193
932
  gc_enable(orig_gc_state);
2194
2195
932
  SHM_PROTECT();
2196
932
  HANDLE_UNBLOCK_INTERRUPTIONS();
2197
2198
  /* We may have switched to an existing persistent script that was persisted in
2199
   * the meantime. Make sure to use its warnings if available. */
2200
932
  if (ZCG(accel_directives).record_warnings) {
2201
0
    EG(record_errors) = false;
2202
0
    zend_emit_recorded_errors_ex(new_persistent_script->num_warnings, new_persistent_script->warnings);
2203
932
  } else {
2204
932
    zend_emit_recorded_errors();
2205
932
  }
2206
932
  zend_free_recorded_errors();
2207
2208
932
  return new_persistent_script->script.main_op_array.dynamic_func_defs[0];
2209
1.04k
}
2210
2211
/* zend_compile() replacement */
2212
zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
2213
278k
{
2214
278k
  zend_persistent_script *persistent_script = NULL;
2215
278k
  zend_string *key = NULL;
2216
278k
  bool from_shared_memory; /* if the script we've got is stored in SHM */
2217
2218
278k
  if (!file_handle->filename || !ZCG(accelerator_enabled)) {
2219
    /* The Accelerator is disabled, act as if without the Accelerator */
2220
0
    ZCG(cache_opline) = NULL;
2221
0
    ZCG(cache_persistent_script) = NULL;
2222
0
    if (file_handle->filename
2223
0
     && ZCG(accel_directives).file_cache
2224
0
     && ZCG(enabled) && accel_startup_ok) {
2225
0
      return file_cache_compile_file(file_handle, type);
2226
0
    }
2227
0
    return accelerator_orig_compile_file(file_handle, type);
2228
278k
  } else if (file_cache_only) {
2229
0
    ZCG(cache_opline) = NULL;
2230
0
    ZCG(cache_persistent_script) = NULL;
2231
0
    return file_cache_compile_file(file_handle, type);
2232
278k
  } else if ((ZCSG(restart_in_progress) && accel_restart_is_active())) {
2233
0
    if (ZCG(accel_directives).file_cache) {
2234
0
      return file_cache_compile_file(file_handle, type);
2235
0
    }
2236
0
    ZCG(cache_opline) = NULL;
2237
0
    ZCG(cache_persistent_script) = NULL;
2238
0
    return accelerator_orig_compile_file(file_handle, type);
2239
0
  }
2240
2241
  /* In case this callback is called from include_once, require_once or it's
2242
   * a main FastCGI request, the key must be already calculated, and cached
2243
   * persistent script already found */
2244
278k
  if (ZCG(cache_persistent_script) &&
2245
10
      ((!EG(current_execute_data) &&
2246
0
        file_handle->primary_script &&
2247
0
        ZCG(cache_opline) == NULL) ||
2248
10
       (EG(current_execute_data) &&
2249
10
        EG(current_execute_data)->func &&
2250
10
        ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
2251
10
        ZCG(cache_opline) == EG(current_execute_data)->opline))) {
2252
2253
10
    persistent_script = ZCG(cache_persistent_script);
2254
10
    if (ZSTR_LEN(ZCG(key))) {
2255
0
      key = ZCG(key);
2256
0
    }
2257
2258
278k
  } else {
2259
278k
    if (!ZCG(accel_directives).revalidate_path) {
2260
      /* try to find cached script by key */
2261
278k
      key = accel_make_persistent_key(file_handle->filename);
2262
278k
      if (!key) {
2263
1.59k
        ZCG(cache_opline) = NULL;
2264
1.59k
        ZCG(cache_persistent_script) = NULL;
2265
1.59k
        return accelerator_orig_compile_file(file_handle, type);
2266
1.59k
      }
2267
276k
      persistent_script = zend_accel_hash_find(&ZCSG(hash), key);
2268
276k
    } else if (UNEXPECTED(php_is_stream_path(ZSTR_VAL(file_handle->filename)) && !is_cacheable_stream_path(ZSTR_VAL(file_handle->filename)))) {
2269
0
      ZCG(cache_opline) = NULL;
2270
0
      ZCG(cache_persistent_script) = NULL;
2271
0
      return accelerator_orig_compile_file(file_handle, type);
2272
0
    }
2273
2274
276k
    if (!persistent_script) {
2275
      /* try to find cached script by full real path */
2276
50.7k
      zend_accel_hash_entry *bucket;
2277
2278
      /* open file to resolve the path */
2279
50.7k
        if (file_handle->type == ZEND_HANDLE_FILENAME
2280
3.09k
         && accelerator_orig_zend_stream_open_function(file_handle) == FAILURE) {
2281
1.10k
        if (!EG(exception)) {
2282
1.09k
          if (type == ZEND_REQUIRE) {
2283
203
            zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, ZSTR_VAL(file_handle->filename));
2284
888
          } else {
2285
888
            zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, ZSTR_VAL(file_handle->filename));
2286
888
          }
2287
1.09k
        }
2288
1.10k
        return NULL;
2289
1.10k
        }
2290
2291
49.6k
      if (file_handle->opened_path) {
2292
4
        bucket = zend_accel_hash_find_entry(&ZCSG(hash), file_handle->opened_path);
2293
2294
4
        if (bucket) {
2295
0
          persistent_script = (zend_persistent_script *)bucket->data;
2296
2297
0
          if (key && !persistent_script->corrupted) {
2298
0
            HANDLE_BLOCK_INTERRUPTIONS();
2299
0
            SHM_UNPROTECT();
2300
0
            zend_shared_alloc_lock();
2301
0
            zend_accel_add_key(key, bucket);
2302
0
            zend_shared_alloc_unlock();
2303
0
            SHM_PROTECT();
2304
0
            HANDLE_UNBLOCK_INTERRUPTIONS();
2305
0
          }
2306
0
        }
2307
4
      }
2308
49.6k
    }
2309
276k
  }
2310
2311
  /* clear cache */
2312
275k
  ZCG(cache_opline) = NULL;
2313
275k
  ZCG(cache_persistent_script) = NULL;
2314
2315
275k
  if (persistent_script && persistent_script->corrupted) {
2316
76.5k
    persistent_script = NULL;
2317
76.5k
  }
2318
2319
  /* Make sure we only increase the currently running processes semaphore
2320
     * once each execution (this function can be called more than once on
2321
     * each execution)
2322
     */
2323
275k
  if (!ZCG(counted)) {
2324
143k
    if (accel_activate_add() == FAILURE) {
2325
0
      if (ZCG(accel_directives).file_cache) {
2326
0
        return file_cache_compile_file(file_handle, type);
2327
0
      }
2328
0
      return accelerator_orig_compile_file(file_handle, type);
2329
0
    }
2330
143k
    ZCG(counted) = true;
2331
143k
  }
2332
2333
  /* Revalidate accessibility of cached file */
2334
275k
  if (EXPECTED(persistent_script != NULL) &&
2335
149k
      UNEXPECTED(ZCG(accel_directives).validate_permission) &&
2336
0
      file_handle->type == ZEND_HANDLE_FILENAME &&
2337
0
      UNEXPECTED(check_persistent_script_access(persistent_script))) {
2338
0
    if (!EG(exception)) {
2339
0
      if (type == ZEND_REQUIRE) {
2340
0
        zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, ZSTR_VAL(file_handle->filename));
2341
0
      } else {
2342
0
        zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, ZSTR_VAL(file_handle->filename));
2343
0
      }
2344
0
    }
2345
0
    return NULL;
2346
0
  }
2347
2348
275k
  HANDLE_BLOCK_INTERRUPTIONS();
2349
275k
  SHM_UNPROTECT();
2350
2351
  /* If script is found then validate_timestamps if option is enabled */
2352
275k
  if (persistent_script && ZCG(accel_directives).validate_timestamps) {
2353
40.3k
    if (validate_timestamp_and_record(persistent_script, file_handle) == FAILURE) {
2354
0
      zend_accel_lock_discard_script(persistent_script);
2355
0
      persistent_script = NULL;
2356
0
    }
2357
40.3k
  }
2358
2359
  /* Check the second level cache */
2360
275k
  if (!persistent_script && ZCG(accel_directives).file_cache) {
2361
0
    persistent_script = zend_file_cache_script_load(file_handle);
2362
0
  }
2363
2364
  /* If script was not found or invalidated by validate_timestamps */
2365
275k
  if (!persistent_script) {
2366
124k
    uint32_t old_const_num = zend_hash_next_free_element(EG(zend_constants));
2367
124k
    zend_op_array *op_array;
2368
2369
    /* Cache miss.. */
2370
124k
    ZCSG(misses)++;
2371
2372
    /* No memory left. Behave like without the Accelerator */
2373
124k
    if (ZSMMG(memory_exhausted) || ZCSG(restart_pending)) {
2374
0
      SHM_PROTECT();
2375
0
      HANDLE_UNBLOCK_INTERRUPTIONS();
2376
0
      if (ZCG(accel_directives).file_cache) {
2377
0
        return file_cache_compile_file(file_handle, type);
2378
0
      }
2379
0
      return accelerator_orig_compile_file(file_handle, type);
2380
0
    }
2381
2382
124k
    zend_begin_record_errors();
2383
2384
124k
    SHM_PROTECT();
2385
124k
    HANDLE_UNBLOCK_INTERRUPTIONS();
2386
124k
    persistent_script = opcache_compile_file(file_handle, type, &op_array);
2387
124k
    HANDLE_BLOCK_INTERRUPTIONS();
2388
124k
    SHM_UNPROTECT();
2389
2390
    /* Try and cache the script and assume that it is returned from_shared_memory.
2391
     * If it isn't compile_and_cache_file() changes the flag to 0
2392
     */
2393
124k
    from_shared_memory = false;
2394
124k
    if (persistent_script) {
2395
60.9k
      if (ZCG(accel_directives).record_warnings) {
2396
0
        persistent_script->num_warnings = EG(errors).size;
2397
0
        persistent_script->warnings = EG(errors).errors;
2398
0
      }
2399
2400
      /* See GH-17246: we disable GC so that user code cannot be executed during the optimizer run. */
2401
60.9k
      bool orig_gc_state = gc_enable(false);
2402
60.9k
      persistent_script = cache_script_in_shared_memory(persistent_script, key, &from_shared_memory);
2403
60.9k
      gc_enable(orig_gc_state);
2404
60.9k
    }
2405
2406
    /* Caching is disabled, returning op_array;
2407
     * or something went wrong during compilation, returning NULL
2408
     */
2409
124k
    if (!persistent_script) {
2410
57.8k
      SHM_PROTECT();
2411
57.8k
      HANDLE_UNBLOCK_INTERRUPTIONS();
2412
57.8k
      zend_emit_recorded_errors();
2413
57.8k
      zend_free_recorded_errors();
2414
57.8k
      return op_array;
2415
57.8k
    }
2416
66.2k
    if (from_shared_memory) {
2417
      /* Delete immutable arrays moved into SHM */
2418
60.9k
      uint32_t new_const_num = zend_hash_next_free_element(EG(zend_constants));
2419
60.9k
      while (new_const_num > old_const_num) {
2420
0
        new_const_num--;
2421
0
        zend_hash_index_del(EG(zend_constants), new_const_num);
2422
0
      }
2423
60.9k
    }
2424
66.2k
    persistent_script->dynamic_members.last_used = ZCG(request_time);
2425
66.2k
    SHM_PROTECT();
2426
66.2k
    HANDLE_UNBLOCK_INTERRUPTIONS();
2427
2428
    /* We may have switched to an existing persistent script that was persisted in
2429
     * the meantime. Make sure to use its warnings if available. */
2430
66.2k
    if (ZCG(accel_directives).record_warnings) {
2431
0
      EG(record_errors) = false;
2432
0
      zend_emit_recorded_errors_ex(persistent_script->num_warnings, persistent_script->warnings);
2433
66.2k
    } else {
2434
66.2k
      zend_emit_recorded_errors();
2435
66.2k
    }
2436
66.2k
    zend_free_recorded_errors();
2437
151k
  } else {
2438
2439
151k
#ifndef ZEND_WIN32
2440
151k
    ZCSG(hits)++; /* TBFixed: may lose one hit */
2441
151k
    persistent_script->dynamic_members.hits++; /* see above */
2442
#else
2443
#ifdef ZEND_ENABLE_ZVAL_LONG64
2444
    InterlockedIncrement64(&ZCSG(hits));
2445
    InterlockedIncrement64(&persistent_script->dynamic_members.hits);
2446
#else
2447
    InterlockedIncrement(&ZCSG(hits));
2448
    InterlockedIncrement(&persistent_script->dynamic_members.hits);
2449
#endif
2450
#endif
2451
2452
    /* see bug #15471 (old BTS) */
2453
151k
    if (persistent_script->script.filename) {
2454
149k
      if (!EG(current_execute_data) ||
2455
95.9k
          !EG(current_execute_data)->func ||
2456
95.9k
          !ZEND_USER_CODE(EG(current_execute_data)->func->common.type) ||
2457
95.9k
          !EG(current_execute_data)->opline ||
2458
95.9k
          EG(current_execute_data)->opline->opcode != ZEND_INCLUDE_OR_EVAL ||
2459
95.9k
          (EG(current_execute_data)->opline->extended_value != ZEND_INCLUDE_ONCE &&
2460
149k
           EG(current_execute_data)->opline->extended_value != ZEND_REQUIRE_ONCE)) {
2461
149k
        if (zend_hash_add_empty_element(&EG(included_files), persistent_script->script.filename) != NULL) {
2462
          /* ext/phar has to load phar's metadata into memory */
2463
57.4k
          if (persistent_script->is_phar) {
2464
0
            php_stream_statbuf ssb;
2465
0
            char *fname = zend_cstr_concat(
2466
0
              "phar://", sizeof("phar://") - 1,
2467
0
              ZSTR_VAL(persistent_script->script.filename),
2468
0
              ZSTR_LEN(persistent_script->script.filename));
2469
0
            php_stream_stat_path(fname, &ssb);
2470
0
            efree(fname);
2471
0
          }
2472
57.4k
        }
2473
149k
      }
2474
149k
    }
2475
151k
    persistent_script->dynamic_members.last_used = ZCG(request_time);
2476
151k
    SHM_PROTECT();
2477
151k
    HANDLE_UNBLOCK_INTERRUPTIONS();
2478
2479
151k
    zend_emit_recorded_errors_ex(persistent_script->num_warnings, persistent_script->warnings);
2480
151k
    from_shared_memory = true;
2481
151k
  }
2482
2483
  /* Fetch jit auto globals used in the script before execution */
2484
217k
  if (persistent_script->ping_auto_globals_mask & ~ZCG(auto_globals_mask)) {
2485
93
    zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask & ~ZCG(auto_globals_mask));
2486
93
  }
2487
2488
217k
  return zend_accel_load_script(persistent_script, from_shared_memory);
2489
275k
}
2490
2491
static zend_always_inline zend_inheritance_cache_entry* zend_accel_inheritance_cache_find(zend_inheritance_cache_entry *entry, const zend_class_entry *ce, const zend_class_entry *parent, zend_class_entry **traits_and_interfaces, bool *needs_autoload_ptr)
2492
882
{
2493
882
  uint32_t i;
2494
2495
882
  ZEND_ASSERT(ce->ce_flags & ZEND_ACC_IMMUTABLE);
2496
882
  ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_LINKED));
2497
2498
882
  while (entry) {
2499
882
    bool found = true;
2500
882
    bool needs_autoload = false;
2501
2502
882
    if (entry->parent != parent) {
2503
0
      found = false;
2504
882
    } else {
2505
1.79k
      for (i = 0; i < ce->num_traits + ce->num_interfaces; i++) {
2506
911
        if (entry->traits_and_interfaces[i] != traits_and_interfaces[i]) {
2507
0
          found = false;
2508
0
          break;
2509
0
        }
2510
911
      }
2511
882
      if (found && entry->dependencies) {
2512
123
        for (i = 0; i < entry->dependencies_count; i++) {
2513
85
          const zend_class_entry *dependency_ce = zend_lookup_class_ex(entry->dependencies[i].name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD);
2514
2515
85
          if (dependency_ce != entry->dependencies[i].ce) {
2516
20
            if (!dependency_ce) {
2517
20
              needs_autoload = true;
2518
20
            } else {
2519
0
              found = false;
2520
0
              break;
2521
0
            }
2522
20
          }
2523
85
        }
2524
38
      }
2525
882
    }
2526
882
    if (found) {
2527
882
      *needs_autoload_ptr = needs_autoload;
2528
882
      return entry;
2529
882
    }
2530
0
    entry = entry->next;
2531
0
  }
2532
2533
0
  return NULL;
2534
882
}
2535
2536
static zend_class_entry* zend_accel_inheritance_cache_get(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces)
2537
5.33k
{
2538
5.33k
  bool needs_autoload;
2539
5.33k
  zend_inheritance_cache_entry *entry = ce->inheritance_cache;
2540
2541
5.33k
  while (entry) {
2542
878
    entry = zend_accel_inheritance_cache_find(entry, ce, parent, traits_and_interfaces, &needs_autoload);
2543
878
    if (entry) {
2544
878
      if (!needs_autoload) {
2545
866
        zend_emit_recorded_errors_ex(entry->num_warnings, entry->warnings);
2546
866
        if (ZCSG(map_ptr_last) > CG(map_ptr_last)) {
2547
0
          zend_map_ptr_extend(ZCSG(map_ptr_last));
2548
0
        }
2549
866
        ce = entry->ce;
2550
866
        if (ZSTR_HAS_CE_CACHE(ce->name)) {
2551
830
          ZSTR_SET_CE_CACHE_EX(ce->name, ce, 0);
2552
830
        }
2553
866
        return ce;
2554
866
      }
2555
2556
16
      for (uint32_t i = 0; i < entry->dependencies_count; i++) {
2557
14
        const zend_class_entry *dependency_ce = zend_lookup_class_ex(entry->dependencies[i].name, NULL, 0);
2558
2559
14
        if (dependency_ce == NULL) {
2560
10
          return NULL;
2561
10
        }
2562
14
      }
2563
12
    }
2564
878
  }
2565
2566
4.45k
  return NULL;
2567
5.33k
}
2568
2569
static zend_class_entry* zend_accel_inheritance_cache_add(zend_class_entry *ce, zend_class_entry *proto, zend_class_entry *parent, zend_class_entry **traits_and_interfaces, HashTable *dependencies)
2570
3.41k
{
2571
3.41k
  zend_persistent_script dummy;
2572
3.41k
  size_t size;
2573
3.41k
  uint32_t i;
2574
3.41k
  bool needs_autoload;
2575
3.41k
  zend_class_entry *new_ce;
2576
3.41k
  zend_inheritance_cache_entry *entry;
2577
2578
3.41k
  ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_IMMUTABLE));
2579
3.41k
  ZEND_ASSERT(ce->ce_flags & ZEND_ACC_LINKED);
2580
2581
3.41k
  if (!ZCG(accelerator_enabled) ||
2582
3.41k
      (ZCSG(restart_in_progress) && accel_restart_is_active())) {
2583
0
    return NULL;
2584
0
  }
2585
2586
3.41k
  if (traits_and_interfaces && dependencies) {
2587
104
    for (i = 0; i < proto->num_traits + proto->num_interfaces; i++) {
2588
52
      if (traits_and_interfaces[i]) {
2589
52
        zend_hash_del(dependencies, traits_and_interfaces[i]->name);
2590
52
      }
2591
52
    }
2592
52
  }
2593
2594
3.41k
  SHM_UNPROTECT();
2595
3.41k
  zend_shared_alloc_lock();
2596
2597
3.41k
  entry = proto->inheritance_cache;
2598
3.41k
  while (entry) {
2599
4
    entry = zend_accel_inheritance_cache_find(entry, proto, parent, traits_and_interfaces, &needs_autoload);
2600
4
    if (entry) {
2601
4
      zend_shared_alloc_unlock();
2602
4
      SHM_PROTECT();
2603
4
      if (!needs_autoload) {
2604
0
        zend_map_ptr_extend(ZCSG(map_ptr_last));
2605
0
        return entry->ce;
2606
4
      } else {
2607
4
        return NULL;
2608
4
      }
2609
4
    }
2610
4
  }
2611
2612
3.40k
  zend_shared_alloc_init_xlat_table();
2613
2614
3.40k
  memset(&dummy, 0, sizeof(dummy));
2615
3.40k
  dummy.size = ZEND_ALIGNED_SIZE(
2616
3.40k
    sizeof(zend_inheritance_cache_entry) -
2617
3.40k
    sizeof(void*) +
2618
3.40k
    (sizeof(void*) * (proto->num_traits + proto->num_interfaces)));
2619
3.40k
  if (dependencies) {
2620
104
    dummy.size += ZEND_ALIGNED_SIZE(zend_hash_num_elements(dependencies) * sizeof(zend_class_dependency));
2621
104
  }
2622
3.40k
  ZCG(current_persistent_script) = &dummy;
2623
3.40k
  zend_persist_class_entry_calc(ce);
2624
3.40k
  zend_persist_warnings_calc(EG(errors).size, EG(errors).errors);
2625
3.40k
  size = dummy.size;
2626
2627
3.40k
  zend_shared_alloc_clear_xlat_table();
2628
2629
#if ZEND_MM_NEED_EIGHT_BYTE_REALIGNMENT
2630
  /* Align to 8-byte boundary */
2631
  ZCG(mem) = zend_shared_alloc(size + 8);
2632
#else
2633
3.40k
  ZCG(mem) = zend_shared_alloc(size);
2634
3.40k
#endif
2635
2636
3.40k
  if (!ZCG(mem)) {
2637
0
    zend_shared_alloc_destroy_xlat_table();
2638
0
    zend_shared_alloc_unlock();
2639
0
    SHM_PROTECT();
2640
0
    return NULL;
2641
0
  }
2642
2643
3.40k
  zend_map_ptr_extend(ZCSG(map_ptr_last));
2644
2645
#if ZEND_MM_NEED_EIGHT_BYTE_REALIGNMENT
2646
  /* Align to 8-byte boundary */
2647
  ZCG(mem) = (void*)(((uintptr_t)ZCG(mem) + 7L) & ~7L);
2648
#endif
2649
2650
3.40k
  memset(ZCG(mem), 0, size);
2651
3.40k
  entry = (zend_inheritance_cache_entry*)ZCG(mem);
2652
3.40k
  ZCG(mem) = (char*)ZCG(mem) +
2653
3.40k
    ZEND_ALIGNED_SIZE(
2654
3.40k
      (sizeof(zend_inheritance_cache_entry) -
2655
3.40k
       sizeof(void*) +
2656
3.40k
       (sizeof(void*) * (proto->num_traits + proto->num_interfaces))));
2657
3.40k
  entry->parent = parent;
2658
7.00k
  for (i = 0; i < proto->num_traits + proto->num_interfaces; i++) {
2659
3.59k
    entry->traits_and_interfaces[i] = traits_and_interfaces[i];
2660
3.59k
  }
2661
3.40k
  if (dependencies && zend_hash_num_elements(dependencies)) {
2662
94
    zend_string *dep_name;
2663
94
    zend_class_entry *dep_ce;
2664
2665
94
    i = 0;
2666
94
    entry->dependencies_count = zend_hash_num_elements(dependencies);
2667
94
    entry->dependencies = (zend_class_dependency*)ZCG(mem);
2668
648
    ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(dependencies, dep_name, dep_ce) {
2669
648
#if ZEND_DEBUG
2670
648
      ZEND_ASSERT(zend_accel_in_shm(dep_name));
2671
648
#endif
2672
648
      entry->dependencies[i].name = dep_name;
2673
230
      entry->dependencies[i].ce = dep_ce;
2674
230
      i++;
2675
230
    } ZEND_HASH_FOREACH_END();
2676
94
    ZCG(mem) = (char*)ZCG(mem) + zend_hash_num_elements(dependencies) * sizeof(zend_class_dependency);
2677
94
  }
2678
2679
  /* See GH-15657: `zend_persist_class_entry` can JIT property hook code via
2680
   * `zend_persist_property_info`, but the inheritance cache should not
2681
   * JIT those at this point in time. */
2682
3.40k
#ifdef HAVE_JIT
2683
3.40k
  bool jit_on_old = JIT_G(on);
2684
3.40k
  JIT_G(on) = false;
2685
3.40k
#endif
2686
2687
3.40k
  entry->ce = new_ce = zend_persist_class_entry(ce);
2688
3.40k
  zend_update_parent_ce(new_ce);
2689
2690
3.40k
#ifdef HAVE_JIT
2691
3.40k
  JIT_G(on) = jit_on_old;
2692
3.40k
#endif
2693
2694
3.40k
  entry->num_warnings = EG(errors).size;
2695
3.40k
  entry->warnings = zend_persist_warnings(EG(errors).size, EG(errors).errors);
2696
3.40k
  entry->next = proto->inheritance_cache;
2697
3.40k
  proto->inheritance_cache = entry;
2698
2699
3.40k
  ZCSG(map_ptr_last) = CG(map_ptr_last);
2700
2701
3.40k
  zend_shared_alloc_destroy_xlat_table();
2702
2703
3.40k
  zend_shared_alloc_unlock();
2704
3.40k
  SHM_PROTECT();
2705
2706
  /* Consistency check */
2707
3.40k
  if ((char*)entry + size != (char*)ZCG(mem)) {
2708
0
    zend_accel_error(
2709
0
      ((char*)entry + size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
2710
0
      "Internal error: wrong class size calculation: %s start=" ZEND_ADDR_FMT ", end=" ZEND_ADDR_FMT ", real=" ZEND_ADDR_FMT "\n",
2711
0
      ZSTR_VAL(ce->name),
2712
0
      (size_t)entry,
2713
0
      (size_t)((char *)entry + size),
2714
0
      (size_t)ZCG(mem));
2715
0
  }
2716
2717
3.40k
  zend_map_ptr_extend(ZCSG(map_ptr_last));
2718
2719
3.40k
  return new_ce;
2720
3.40k
}
2721
2722
#ifdef ZEND_WIN32
2723
static zend_result accel_gen_uname_id(void)
2724
{
2725
  PHP_MD5_CTX ctx;
2726
  unsigned char digest[16];
2727
  wchar_t uname[UNLEN + 1];
2728
  DWORD unsize = UNLEN;
2729
2730
  if (!GetUserNameW(uname, &unsize)) {
2731
    return FAILURE;
2732
  }
2733
  PHP_MD5Init(&ctx);
2734
  PHP_MD5Update(&ctx, (void *) uname, (unsize - 1) * sizeof(wchar_t));
2735
  PHP_MD5Update(&ctx, ZCG(accel_directives).cache_id, strlen(ZCG(accel_directives).cache_id));
2736
  PHP_MD5Final(digest, &ctx);
2737
  zend_bin2hex(accel_uname_id, digest, sizeof digest);
2738
  return SUCCESS;
2739
}
2740
#endif
2741
2742
/* zend_stream_open_function() replacement for PHP 5.3 and above */
2743
static zend_result persistent_stream_open_function(zend_file_handle *handle)
2744
1.80k
{
2745
1.80k
  if (ZCG(cache_persistent_script)) {
2746
    /* check if callback is called from include_once or it's a main request */
2747
10
    if ((!EG(current_execute_data) &&
2748
0
         handle->primary_script &&
2749
0
         ZCG(cache_opline) == NULL) ||
2750
10
        (EG(current_execute_data) &&
2751
10
         EG(current_execute_data)->func &&
2752
10
         ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
2753
10
         ZCG(cache_opline) == EG(current_execute_data)->opline)) {
2754
2755
      /* we are in include_once or FastCGI request */
2756
10
      handle->opened_path = zend_string_copy(ZCG(cache_persistent_script)->script.filename);
2757
10
      return SUCCESS;
2758
10
    }
2759
0
    ZCG(cache_opline) = NULL;
2760
0
    ZCG(cache_persistent_script) = NULL;
2761
0
  }
2762
1.79k
  return accelerator_orig_zend_stream_open_function(handle);
2763
1.80k
}
2764
2765
/* zend_resolve_path() replacement for PHP 5.3 and above */
2766
static zend_string* persistent_zend_resolve_path(zend_string *filename)
2767
7.10k
{
2768
7.10k
  if (!file_cache_only &&
2769
7.10k
      ZCG(accelerator_enabled)) {
2770
2771
    /* check if callback is called from include_once or it's a main request */
2772
7.10k
    if ((!EG(current_execute_data)) ||
2773
7.10k
        (EG(current_execute_data) &&
2774
7.10k
         EG(current_execute_data)->func &&
2775
7.10k
         ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
2776
7.08k
         EG(current_execute_data)->opline->opcode == ZEND_INCLUDE_OR_EVAL &&
2777
7.08k
         (EG(current_execute_data)->opline->extended_value == ZEND_INCLUDE_ONCE ||
2778
7.04k
          EG(current_execute_data)->opline->extended_value == ZEND_REQUIRE_ONCE))) {
2779
2780
      /* we are in include_once or FastCGI request */
2781
2.40k
      zend_string *resolved_path;
2782
2.40k
      zend_string *key = NULL;
2783
2784
2.40k
      if (!ZCG(accel_directives).revalidate_path) {
2785
        /* lookup by "not-real" path */
2786
2.40k
        key = accel_make_persistent_key(filename);
2787
2.40k
        if (key) {
2788
2.40k
          const zend_accel_hash_entry *bucket = zend_accel_hash_find_entry(&ZCSG(hash), key);
2789
2.40k
          if (bucket != NULL) {
2790
13
            zend_persistent_script *persistent_script = (zend_persistent_script *)bucket->data;
2791
13
            if (!persistent_script->corrupted) {
2792
13
              ZCG(cache_opline) = EG(current_execute_data) ? EG(current_execute_data)->opline : NULL;
2793
13
              ZCG(cache_persistent_script) = persistent_script;
2794
13
              return zend_string_copy(persistent_script->script.filename);
2795
13
            }
2796
13
          }
2797
2.40k
        } else {
2798
0
          ZCG(cache_opline) = NULL;
2799
0
          ZCG(cache_persistent_script) = NULL;
2800
0
          return accelerator_orig_zend_resolve_path(filename);
2801
0
        }
2802
2.40k
      }
2803
2804
      /* find the full real path */
2805
2.38k
      resolved_path = accelerator_orig_zend_resolve_path(filename);
2806
2807
2.38k
      if (resolved_path) {
2808
        /* lookup by real path */
2809
44
        zend_accel_hash_entry *bucket = zend_accel_hash_find_entry(&ZCSG(hash), resolved_path);
2810
44
        if (bucket) {
2811
0
          zend_persistent_script *persistent_script = (zend_persistent_script *)bucket->data;
2812
0
          if (!persistent_script->corrupted) {
2813
0
            if (key) {
2814
              /* add another "key" for the same bucket */
2815
0
              HANDLE_BLOCK_INTERRUPTIONS();
2816
0
              SHM_UNPROTECT();
2817
0
              zend_shared_alloc_lock();
2818
0
              zend_accel_add_key(key, bucket);
2819
0
              zend_shared_alloc_unlock();
2820
0
              SHM_PROTECT();
2821
0
              HANDLE_UNBLOCK_INTERRUPTIONS();
2822
0
            } else {
2823
0
              ZSTR_LEN(ZCG(key)) = 0;
2824
0
            }
2825
0
            ZCG(cache_opline) = EG(current_execute_data) ? EG(current_execute_data)->opline : NULL;
2826
0
            ZCG(cache_persistent_script) = persistent_script;
2827
0
            return resolved_path;
2828
0
          }
2829
0
        }
2830
44
      }
2831
2832
2.38k
      ZCG(cache_opline) = NULL;
2833
2.38k
      ZCG(cache_persistent_script) = NULL;
2834
2.38k
      return resolved_path;
2835
2.38k
    }
2836
7.10k
  }
2837
4.70k
  ZCG(cache_opline) = NULL;
2838
4.70k
  ZCG(cache_persistent_script) = NULL;
2839
4.70k
  return accelerator_orig_zend_resolve_path(filename);
2840
7.10k
}
2841
2842
static void zend_reset_cache_vars(void)
2843
16
{
2844
16
  ZSMMG(memory_exhausted) = false;
2845
16
  ZCSG(hits) = 0;
2846
16
  ZCSG(misses) = 0;
2847
16
  ZCSG(blacklist_misses) = 0;
2848
16
  ZSMMG(wasted_shared_memory) = 0;
2849
16
  ZCSG(restart_pending) = false;
2850
16
  ZCSG(force_restart_time) = 0;
2851
16
  ZCSG(map_ptr_last) = CG(map_ptr_last);
2852
16
  ZCSG(map_ptr_static_last) = zend_map_ptr_static_last;
2853
16
}
2854
2855
static void accel_reset_pcre_cache(void)
2856
0
{
2857
0
  Bucket *p;
2858
2859
0
  ZEND_HASH_MAP_FOREACH_BUCKET(&PCRE_G(pcre_cache), p) {
2860
    /* Remove PCRE cache entries with inconsistent keys */
2861
0
    if (zend_accel_in_shm(p->key)) {
2862
0
      p->key = NULL;
2863
0
      zend_hash_del_bucket(&PCRE_G(pcre_cache), p);
2864
0
    }
2865
0
  } ZEND_HASH_FOREACH_END();
2866
0
}
2867
2868
ZEND_RINIT_FUNCTION(zend_accelerator)
2869
295k
{
2870
295k
  if (!ZCG(enabled) || !accel_startup_ok) {
2871
0
    ZCG(accelerator_enabled) = false;
2872
0
    return SUCCESS;
2873
0
  }
2874
2875
  /* PHP-5.4 and above return "double", but we use 1 sec precision */
2876
295k
  ZCG(auto_globals_mask) = 0;
2877
295k
  ZCG(request_time) = (time_t)sapi_get_request_time();
2878
295k
  ZCG(cache_opline) = NULL;
2879
295k
  ZCG(cache_persistent_script) = NULL;
2880
295k
  ZCG(include_path_key_len) = 0;
2881
295k
  ZCG(include_path_check) = true;
2882
2883
295k
  ZCG(cwd) = NULL;
2884
295k
  ZCG(cwd_key_len) = 0;
2885
295k
  ZCG(cwd_check) = true;
2886
2887
295k
  if (file_cache_only) {
2888
0
    ZCG(accelerator_enabled) = false;
2889
0
    return SUCCESS;
2890
0
  }
2891
2892
295k
#ifndef ZEND_WIN32
2893
295k
  if (ZCG(accel_directives).validate_root) {
2894
0
    struct stat buf;
2895
2896
0
    if (stat("/", &buf) != 0) {
2897
0
      ZCG(root_hash) = 0;
2898
0
    } else {
2899
0
      ZCG(root_hash) = buf.st_ino;
2900
0
      if (sizeof(buf.st_ino) > sizeof(ZCG(root_hash))) {
2901
0
        if (ZCG(root_hash) != buf.st_ino) {
2902
0
          zend_string *key = ZSTR_INIT_LITERAL("opcache.enable", 0);
2903
0
          zend_alter_ini_entry_chars(key, "0", 1, ZEND_INI_SYSTEM, ZEND_INI_STAGE_RUNTIME);
2904
0
          zend_string_release_ex(key, 0);
2905
0
          zend_accel_error(ACCEL_LOG_WARNING, "Can't cache files in chroot() directory with too big inode");
2906
0
          return SUCCESS;
2907
0
        }
2908
0
      }
2909
0
    }
2910
295k
  } else {
2911
295k
    ZCG(root_hash) = 0;
2912
295k
  }
2913
295k
#endif
2914
2915
295k
  HANDLE_BLOCK_INTERRUPTIONS();
2916
295k
  SHM_UNPROTECT();
2917
2918
295k
  if (ZCG(counted)) {
2919
#ifdef ZTS
2920
    zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for thread id %lu", (unsigned long) tsrm_thread_id());
2921
#else
2922
0
    zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for pid %d", getpid());
2923
0
#endif
2924
0
    accel_unlock_all();
2925
0
    ZCG(counted) = false;
2926
0
  }
2927
2928
295k
  if (ZCSG(restart_pending)) {
2929
0
    zend_shared_alloc_lock();
2930
0
    if (ZCSG(restart_pending)) { /* check again, to ensure that the cache wasn't already cleaned by another process */
2931
0
      if (accel_is_inactive()) {
2932
0
        zend_accel_error(ACCEL_LOG_DEBUG, "Restarting!");
2933
0
        ZCSG(restart_pending) = false;
2934
0
        switch ZCSG(restart_reason) {
2935
0
          case ACCEL_RESTART_OOM:
2936
0
            ZCSG(oom_restarts)++;
2937
0
            break;
2938
0
          case ACCEL_RESTART_HASH:
2939
0
            ZCSG(hash_restarts)++;
2940
0
            break;
2941
0
          case ACCEL_RESTART_USER:
2942
0
            ZCSG(manual_restarts)++;
2943
0
            break;
2944
0
        }
2945
0
        accel_restart_enter();
2946
2947
0
        zend_map_ptr_reset();
2948
0
        zend_reset_cache_vars();
2949
0
        zend_accel_hash_clean(&ZCSG(hash));
2950
2951
0
        if (ZCSG(interned_strings).saved_top) {
2952
0
          accel_interned_strings_restore_state();
2953
0
        }
2954
2955
0
        zend_shared_alloc_restore_state();
2956
0
#ifdef PRELOAD_SUPPORT
2957
0
        if (ZCSG(preload_script)) {
2958
0
          preload_restart();
2959
0
        }
2960
0
#endif
2961
2962
0
#ifdef HAVE_JIT
2963
0
        zend_jit_restart();
2964
0
#endif
2965
2966
0
        ZCSG(accelerator_enabled) = ZCSG(cache_status_before_restart);
2967
0
        if (ZCSG(last_restart_time) < ZCG(request_time)) {
2968
0
          ZCSG(last_restart_time) = ZCG(request_time);
2969
0
        } else {
2970
0
          ZCSG(last_restart_time)++;
2971
0
        }
2972
0
        accel_restart_leave();
2973
0
      }
2974
0
    }
2975
0
    zend_shared_alloc_unlock();
2976
0
  }
2977
2978
295k
  ZCG(accelerator_enabled) = ZCSG(accelerator_enabled);
2979
2980
295k
  SHM_PROTECT();
2981
295k
  HANDLE_UNBLOCK_INTERRUPTIONS();
2982
2983
295k
  if (ZCG(accelerator_enabled) && ZCSG(last_restart_time) != ZCG(last_restart_time)) {
2984
    /* SHM was reinitialized. */
2985
0
    ZCG(last_restart_time) = ZCSG(last_restart_time);
2986
2987
    /* Reset in-process realpath cache */
2988
0
    realpath_cache_clean();
2989
2990
0
    accel_reset_pcre_cache();
2991
0
    ZCG(pcre_reseted) = false;
2992
295k
  } else if (!ZCG(accelerator_enabled) && !ZCG(pcre_reseted)) {
2993
0
    accel_reset_pcre_cache();
2994
0
    ZCG(pcre_reseted) = true;
2995
0
  }
2996
2997
2998
295k
#ifdef HAVE_JIT
2999
295k
  zend_jit_activate();
3000
295k
#endif
3001
3002
295k
#ifdef PRELOAD_SUPPORT
3003
295k
  if (ZCSG(preload_script)) {
3004
0
    preload_activate();
3005
0
  }
3006
295k
#endif
3007
3008
295k
  return SUCCESS;
3009
295k
}
3010
3011
#ifdef HAVE_JIT
3012
void accel_deactivate(void)
3013
295k
{
3014
295k
  zend_jit_deactivate();
3015
295k
}
3016
#endif
3017
3018
zend_result accel_post_deactivate(void)
3019
295k
{
3020
295k
  if (ZCG(cwd)) {
3021
34.4k
    zend_string_release_ex(ZCG(cwd), 0);
3022
34.4k
    ZCG(cwd) = NULL;
3023
34.4k
  }
3024
3025
295k
  if (!ZCG(enabled) || !accel_startup_ok) {
3026
0
    return SUCCESS;
3027
0
  }
3028
3029
295k
  zend_shared_alloc_safe_unlock(); /* be sure we didn't leave cache locked */
3030
295k
  accel_unlock_all();
3031
295k
  ZCG(counted) = false;
3032
3033
295k
  return SUCCESS;
3034
295k
}
3035
3036
static int accelerator_remove_cb(zend_extension *element1, zend_extension *element2)
3037
0
{
3038
0
  (void)element2; /* keep the compiler happy */
3039
3040
0
  if (!strcmp(element1->name, ACCELERATOR_PRODUCT_NAME )) {
3041
0
    element1->startup = NULL;
3042
#if 0
3043
    /* We have to call shutdown callback it to free TS resources */
3044
    element1->shutdown = NULL;
3045
#endif
3046
0
    element1->activate = NULL;
3047
0
    element1->deactivate = NULL;
3048
0
    element1->op_array_handler = NULL;
3049
3050
#ifdef __DEBUG_MESSAGES__
3051
    fprintf(stderr, ACCELERATOR_PRODUCT_NAME " is disabled: %s\n", (zps_failure_reason ? zps_failure_reason : "unknown error"));
3052
    fflush(stderr);
3053
#endif
3054
0
  }
3055
3056
0
  return 0;
3057
0
}
3058
3059
static void zps_startup_failure(const char *reason, const char *api_reason, int (*cb)(zend_extension *, zend_extension *))
3060
0
{
3061
0
  accel_startup_ok = false;
3062
0
  zps_failure_reason = reason;
3063
0
  zps_api_failure_reason = api_reason?api_reason:reason;
3064
0
  zend_llist_del_element(&zend_extensions, NULL, (int (*)(void *, void *))cb);
3065
0
}
3066
3067
/* Return whether we are running a CLI (Command LIne) SAPI for which Opcache is
3068
 * disabled when `opcache.enable_cli=0` */
3069
static inline bool accel_sapi_is_cli(void)
3070
16
{
3071
16
  return strcmp(sapi_module.name, "cli") == 0
3072
16
    || strcmp(sapi_module.name, "phpdbg") == 0;
3073
16
}
3074
3075
static zend_result zend_accel_init_shm(void)
3076
16
{
3077
16
  int i;
3078
16
  size_t accel_shared_globals_size;
3079
3080
16
  zend_shared_alloc_lock();
3081
3082
16
  if (ZCG(accel_directives).interned_strings_buffer) {
3083
16
    accel_shared_globals_size = sizeof(zend_accel_shared_globals) + ZCG(accel_directives).interned_strings_buffer * 1024 * 1024;
3084
16
  } else {
3085
    /* Make sure there is always at least one interned string hash slot,
3086
     * so the table can be queried unconditionally. */
3087
0
    accel_shared_globals_size = sizeof(zend_accel_shared_globals) + sizeof(zend_string_table_pos_t);
3088
0
  }
3089
3090
16
  accel_shared_globals = zend_shared_alloc(accel_shared_globals_size);
3091
16
  if (!accel_shared_globals) {
3092
0
    zend_shared_alloc_unlock();
3093
0
    zend_accel_error_noreturn(ACCEL_LOG_FATAL,
3094
0
        "Insufficient shared memory for interned strings buffer! (tried to allocate %zu bytes)",
3095
0
        accel_shared_globals_size);
3096
0
    return FAILURE;
3097
0
  }
3098
16
  memset(accel_shared_globals, 0, sizeof(zend_accel_shared_globals));
3099
16
  ZSMMG(app_shared_globals) = accel_shared_globals;
3100
3101
16
  zend_accel_hash_init(&ZCSG(hash), ZCG(accel_directives).max_accelerated_files);
3102
3103
16
  if (ZCG(accel_directives).interned_strings_buffer) {
3104
16
    uint32_t hash_size;
3105
3106
    /* must be a power of two */
3107
16
    hash_size = ZCG(accel_directives).interned_strings_buffer * (32 * 1024);
3108
16
    hash_size |= (hash_size >> 1);
3109
16
    hash_size |= (hash_size >> 2);
3110
16
    hash_size |= (hash_size >> 4);
3111
16
    hash_size |= (hash_size >> 8);
3112
16
    hash_size |= (hash_size >> 16);
3113
3114
16
    ZCSG(interned_strings).nTableMask =
3115
16
      hash_size * sizeof(zend_string_table_pos_t);
3116
16
    ZCSG(interned_strings).nNumOfElements = 0;
3117
16
    ZCSG(interned_strings).start =
3118
16
      (zend_string*)((char*)&ZCSG(interned_strings) +
3119
16
        sizeof(zend_string_table) +
3120
16
        ((hash_size + 1) * sizeof(zend_string_table_pos_t))) +
3121
16
        8;
3122
16
    ZEND_ASSERT(((uintptr_t)ZCSG(interned_strings).start & 0x7) == 0); /* should be 8 byte aligned */
3123
3124
16
    ZCSG(interned_strings).top =
3125
16
      ZCSG(interned_strings).start;
3126
16
    ZCSG(interned_strings).end =
3127
16
      (zend_string*)((char*)(accel_shared_globals + 1) + /* table data is stored after accel_shared_globals */
3128
16
        ZCG(accel_directives).interned_strings_buffer * 1024 * 1024);
3129
16
    ZEND_ASSERT(((uintptr_t)ZCSG(interned_strings).end - (uintptr_t)&ZCSG(interned_strings)) / ZEND_STRING_TABLE_POS_ALIGNMENT < ZEND_STRING_TABLE_POS_MAX);
3130
16
    ZCSG(interned_strings).saved_top = NULL;
3131
3132
16
    memset((char*)&ZCSG(interned_strings) + sizeof(zend_string_table),
3133
16
      STRTAB_INVALID_POS,
3134
16
      (char*)ZCSG(interned_strings).start -
3135
16
        ((char*)&ZCSG(interned_strings) + sizeof(zend_string_table)));
3136
16
  } else {
3137
0
    *STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), 0) = STRTAB_INVALID_POS;
3138
0
  }
3139
3140
  /* We can reuse init_interned_string_for_php for the "init_existing_interned" case,
3141
   * because the function does not create new interned strings at runtime. */
3142
16
  zend_interned_strings_set_request_storage_handlers(
3143
16
    accel_new_interned_string_for_php,
3144
16
    accel_init_interned_string_for_php,
3145
16
    accel_init_interned_string_for_php);
3146
3147
16
  zend_reset_cache_vars();
3148
3149
16
  ZCSG(oom_restarts) = 0;
3150
16
  ZCSG(hash_restarts) = 0;
3151
16
  ZCSG(manual_restarts) = 0;
3152
3153
16
  ZCSG(accelerator_enabled) = true;
3154
16
  ZCSG(start_time) = zend_accel_get_time();
3155
16
  ZCSG(last_restart_time) = 0;
3156
16
  ZCSG(restart_in_progress) = false;
3157
3158
48
  for (i = 0; i < -HT_MIN_MASK; i++) {
3159
32
    ZCSG(uninitialized_bucket)[i] = HT_INVALID_IDX;
3160
32
  }
3161
3162
16
  zend_shared_alloc_unlock();
3163
3164
16
  return SUCCESS;
3165
16
}
3166
3167
static void accel_globals_ctor(zend_accel_globals *accel_globals)
3168
16
{
3169
16
  memset(accel_globals, 0, sizeof(zend_accel_globals));
3170
16
  accel_globals->key = zend_string_alloc(ZCG_KEY_LEN, true);
3171
16
  GC_MAKE_PERSISTENT_LOCAL(accel_globals->key);
3172
16
}
3173
3174
static void accel_globals_dtor(zend_accel_globals *accel_globals)
3175
0
{
3176
0
  zend_string_free(accel_globals->key);
3177
0
  if (accel_globals->preloaded_internal_run_time_cache) {
3178
0
    pefree(accel_globals->preloaded_internal_run_time_cache, 1);
3179
0
  }
3180
0
}
3181
3182
#ifdef HAVE_HUGE_CODE_PAGES
3183
# ifndef _WIN32
3184
#  include <sys/mman.h>
3185
#  ifndef MAP_ANON
3186
#   ifdef MAP_ANONYMOUS
3187
#    define MAP_ANON MAP_ANONYMOUS
3188
#   endif
3189
#  endif
3190
#  ifndef MAP_FAILED
3191
#   define MAP_FAILED ((void*)-1)
3192
#  endif
3193
#  ifdef MAP_ALIGNED_SUPER
3194
#   include <sys/types.h>
3195
#   include <sys/sysctl.h>
3196
#   include <sys/user.h>
3197
#   define MAP_HUGETLB MAP_ALIGNED_SUPER
3198
#  endif
3199
#  if __has_include(<link.h>)
3200
#   include <link.h>
3201
#  endif
3202
#  if __has_include(<elf.h>)
3203
#   include <elf.h>
3204
#  endif
3205
# endif
3206
3207
0
# define ZEND_HUGE_PAGE_SIZE (2UL * 1024 * 1024)
3208
3209
# if (defined(__linux__) || defined(__FreeBSD__)) && (defined(MAP_HUGETLB) || defined(MADV_HUGEPAGE)) && defined(HAVE_ATTRIBUTE_ALIGNED) && defined(HAVE_ATTRIBUTE_SECTION) && __has_include(<link.h>) && __has_include(<elf.h>)
3210
static zend_result
3211
__attribute__((section(".remap_stub")))
3212
__attribute__((aligned(ZEND_HUGE_PAGE_SIZE)))
3213
zend_never_inline
3214
accel_remap_huge_pages(void *start, size_t size, size_t real_size)
3215
0
{
3216
0
  void *ret = MAP_FAILED;
3217
0
  void *mem;
3218
3219
0
  mem = mmap(NULL, size,
3220
0
    PROT_READ | PROT_WRITE,
3221
0
    MAP_PRIVATE | MAP_ANONYMOUS,
3222
0
    -1, 0);
3223
0
  if (mem == MAP_FAILED) {
3224
0
    zend_error(E_WARNING,
3225
0
      ACCELERATOR_PRODUCT_NAME " huge_code_pages: mmap failed: %s (%d)",
3226
0
      strerror(errno), errno);
3227
0
    return FAILURE;
3228
0
  }
3229
0
  memcpy(mem, start, real_size);
3230
3231
0
#  ifdef MAP_HUGETLB
3232
0
  ret = mmap(start, size,
3233
0
    PROT_READ | PROT_WRITE | PROT_EXEC,
3234
0
    MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_HUGETLB,
3235
0
    -1, 0);
3236
0
#  endif
3237
0
  if (ret == MAP_FAILED) {
3238
0
    ret = mmap(start, size,
3239
0
      PROT_READ | PROT_WRITE | PROT_EXEC,
3240
0
      MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
3241
0
      -1, 0);
3242
    /* this should never happen? */
3243
0
    ZEND_ASSERT(ret != MAP_FAILED);
3244
0
#  ifdef MADV_HUGEPAGE
3245
0
    if (-1 == madvise(start, size, MADV_HUGEPAGE)) {
3246
0
      memcpy(start, mem, real_size);
3247
0
      mprotect(start, size, PROT_READ | PROT_EXEC);
3248
0
      munmap(mem, size);
3249
0
      zend_error(E_WARNING,
3250
0
        ACCELERATOR_PRODUCT_NAME " huge_code_pages: madvise(HUGEPAGE) failed: %s (%d)",
3251
0
        strerror(errno), errno);
3252
0
      return FAILURE;
3253
0
    }
3254
#  else
3255
    memcpy(start, mem, real_size);
3256
    mprotect(start, size, PROT_READ | PROT_EXEC);
3257
    munmap(mem, size);
3258
    zend_error(E_WARNING,
3259
      ACCELERATOR_PRODUCT_NAME " huge_code_pages: mmap(HUGETLB) failed: %s (%d)",
3260
      strerror(errno), errno);
3261
    return FAILURE;
3262
#  endif
3263
0
  }
3264
3265
  // Given the MAP_FIXED flag the address can never diverge
3266
0
  ZEND_ASSERT(ret == start);
3267
3268
0
  memcpy(start, mem, real_size);
3269
0
  mprotect(start, size, PROT_READ | PROT_EXEC);
3270
0
  zend_mmap_set_name(start, size, "zend_huge_code_pages");
3271
3272
0
  munmap(mem, size);
3273
3274
0
  return SUCCESS;
3275
0
}
3276
3277
0
static int accel_dl_iterate_phdr_callback(struct dl_phdr_info *info, size_t size, void *data) {
3278
0
  if (info->dlpi_name == NULL || strcmp(info->dlpi_name, "") == 0) {
3279
0
    *((uintptr_t*)data) = info->dlpi_addr;
3280
0
    return 1;
3281
0
  }
3282
0
  return 0;
3283
0
}
3284
3285
0
static zend_result accel_find_program_section(ElfW(Shdr) *section) {
3286
3287
0
  uintptr_t base_addr;
3288
0
  if (dl_iterate_phdr(accel_dl_iterate_phdr_callback, &base_addr) != 1) {
3289
0
    zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: executable base address not found");
3290
0
    return FAILURE;
3291
0
  }
3292
3293
0
#if defined(__linux__)
3294
0
  FILE *f = fopen("/proc/self/exe", "r");
3295
#elif defined(__FreeBSD__)
3296
  char path[4096];
3297
  int mib[4];
3298
  size_t len = sizeof(path);
3299
3300
  mib[0] = CTL_KERN;
3301
  mib[1] = KERN_PROC;
3302
  mib[2] = KERN_PROC_PATHNAME;
3303
  mib[3] = -1; /* Current process */
3304
3305
  if (sysctl(mib, 4, path, &len, NULL, 0) == -1) {
3306
    zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: sysctl(KERN_PROC_PATHNAME) failed: %s (%d)",
3307
        strerror(errno), errno);
3308
    return FAILURE;
3309
  }
3310
3311
  FILE *f = fopen(path, "r");
3312
#endif
3313
0
  if (!f) {
3314
0
    zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: fopen(/proc/self/exe) failed: %s (%d)",
3315
0
        strerror(errno), errno);
3316
0
    return FAILURE;
3317
0
  }
3318
3319
  /* Read ELF header */
3320
0
  ElfW(Ehdr) ehdr;
3321
0
  if (!fread(&ehdr, sizeof(ehdr), 1, f)) {
3322
0
    zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: fread() failed: %s (%d)",
3323
0
        strerror(errno), errno);
3324
0
    fclose(f);
3325
0
    return FAILURE;
3326
0
  }
3327
3328
  /* Read section headers */
3329
0
  ElfW(Shdr) shdrs[ehdr.e_shnum];
3330
0
  if (fseek(f, ehdr.e_shoff, SEEK_SET) != 0) {
3331
0
    zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: fseek() failed: %s (%d)",
3332
0
        strerror(errno), errno);
3333
0
    fclose(f);
3334
0
    return FAILURE;
3335
0
  }
3336
0
  if (fread(shdrs, sizeof(shdrs[0]), ehdr.e_shnum, f) != ehdr.e_shnum) {
3337
0
    zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: fread() failed: %s (%d)",
3338
0
        strerror(errno), errno);
3339
0
    fclose(f);
3340
0
    return FAILURE;
3341
0
  }
3342
3343
0
  fclose(f);
3344
3345
  /* Find the program section */
3346
0
  for (ElfW(Half) idx = 0; idx < ehdr.e_shnum; idx++) {
3347
0
    ElfW(Shdr) *sh = &shdrs[idx];
3348
0
    uintptr_t start = (uintptr_t)sh->sh_addr + base_addr;
3349
0
    zend_accel_error(ACCEL_LOG_DEBUG, "considering section %016" PRIxPTR "-%016" PRIxPTR " vs %016" PRIxPTR "\n", start, start + sh->sh_size, (uintptr_t)accel_find_program_section);
3350
0
    if ((uintptr_t)accel_find_program_section >= start && (uintptr_t)accel_find_program_section < start + sh->sh_size) {
3351
0
      *section = *sh;
3352
0
      section->sh_addr = (ElfW(Addr))start;
3353
0
      return SUCCESS;
3354
0
    }
3355
0
  }
3356
3357
0
  return FAILURE;
3358
0
}
3359
3360
static void accel_move_code_to_huge_pages(void)
3361
0
{
3362
0
  ElfW(Shdr) section;
3363
0
  if (accel_find_program_section(&section) == FAILURE) {
3364
0
    zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: program section not found");
3365
0
    return;
3366
0
  }
3367
3368
0
  uintptr_t start = ZEND_MM_ALIGNED_SIZE_EX(section.sh_addr, ZEND_HUGE_PAGE_SIZE);
3369
0
  uintptr_t end = (section.sh_addr + section.sh_size) & ~(ZEND_HUGE_PAGE_SIZE-1UL);
3370
0
  if (end > start) {
3371
0
    zend_accel_error(ACCEL_LOG_DEBUG, "remap to huge page %" PRIxPTR "-%" PRIxPTR "\n", start, end);
3372
0
    accel_remap_huge_pages((void*)start, end - start, end - start);
3373
0
  }
3374
0
}
3375
# else
3376
static void accel_move_code_to_huge_pages(void)
3377
{
3378
  zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages has no affect as huge page is not supported");
3379
  return;
3380
}
3381
# endif /* defined(MAP_HUGETLB) || defined(MADV_HUGEPAGE) */
3382
#endif /* HAVE_HUGE_CODE_PAGES */
3383
3384
void start_accel_extension(void)
3385
16
{
3386
16
  zend_register_extension(&opcache_extension_entry, NULL);
3387
16
}
3388
3389
static int accel_startup(zend_extension *extension)
3390
16
{
3391
#ifdef ZTS
3392
  accel_globals_id = ts_allocate_fast_id(&accel_globals_id, &accel_globals_offset, sizeof(zend_accel_globals), (ts_allocate_ctor) accel_globals_ctor, (ts_allocate_dtor) accel_globals_dtor);
3393
#else
3394
16
  accel_globals_ctor(&accel_globals);
3395
16
#endif
3396
3397
16
#ifdef HAVE_JIT
3398
16
  zend_jit_init();
3399
16
#endif
3400
3401
#ifdef ZEND_WIN32
3402
# if !defined(__has_feature) || !__has_feature(address_sanitizer)
3403
  _setmaxstdio(2048); /* The default configuration is limited to 512 stdio files */
3404
# endif
3405
#endif
3406
3407
16
  zend_accel_register_ini_entries();
3408
3409
#ifdef ZEND_WIN32
3410
  if (UNEXPECTED(accel_gen_uname_id() == FAILURE)) {
3411
    zps_startup_failure("Unable to get user name", NULL, accelerator_remove_cb);
3412
    return SUCCESS;
3413
  }
3414
#endif
3415
3416
16
#ifdef HAVE_HUGE_CODE_PAGES
3417
16
  if (ZCG(accel_directives).huge_code_pages &&
3418
0
      (strcmp(sapi_module.name, "cli") == 0 ||
3419
0
       strcmp(sapi_module.name, "cli-server") == 0 ||
3420
0
     strcmp(sapi_module.name, "cgi-fcgi") == 0 ||
3421
0
     strcmp(sapi_module.name, "fpm-fcgi") == 0)) {
3422
0
    accel_move_code_to_huge_pages();
3423
0
  }
3424
16
#endif
3425
3426
16
  if (!ZCG(accel_directives).enable_cli && accel_sapi_is_cli()) {
3427
0
    accel_startup_ok = false;
3428
0
    zps_startup_failure("Opcode Caching is disabled for CLI", NULL, accelerator_remove_cb);
3429
0
    return SUCCESS;
3430
0
  }
3431
3432
16
  if (ZCG(enabled) == 0) {
3433
0
    return SUCCESS ;
3434
0
  }
3435
3436
16
  orig_post_startup_cb = zend_post_startup_cb;
3437
16
  zend_post_startup_cb = accel_post_startup;
3438
3439
  /* Prevent unloading */
3440
16
  extension->handle = 0;
3441
3442
16
  return SUCCESS;
3443
16
}
3444
3445
static zend_result accel_post_startup(void)
3446
16
{
3447
16
  zend_function *func;
3448
16
  zend_ini_entry *ini_entry;
3449
3450
16
  if (orig_post_startup_cb) {
3451
0
    zend_result (*cb)(void) = orig_post_startup_cb;
3452
3453
0
    orig_post_startup_cb = NULL;
3454
0
    if (cb() != SUCCESS) {
3455
0
      return FAILURE;
3456
0
    }
3457
0
  }
3458
3459
/********************************************/
3460
/* End of non-SHM dependent initializations */
3461
/********************************************/
3462
16
  file_cache_only = ZCG(accel_directives).file_cache_only;
3463
16
  if (!file_cache_only) {
3464
16
    size_t shm_size = ZCG(accel_directives).memory_consumption;
3465
16
    size_t jit_size = 0;
3466
16
#ifdef HAVE_JIT
3467
16
    size_t jit_buffer_size = 0;
3468
16
    bool reattached = false;
3469
3470
16
    if (JIT_G(enabled) && JIT_G(buffer_size)
3471
0
     && zend_jit_check_support() == SUCCESS) {
3472
0
      size_t page_size;
3473
3474
0
      page_size = zend_get_page_size();
3475
0
      if (!page_size || (page_size & (page_size - 1))) {
3476
0
        zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - can't get page size.");
3477
0
        abort();
3478
0
      }
3479
0
      jit_buffer_size = JIT_G(buffer_size);
3480
0
      jit_buffer_size = ZEND_MM_ALIGNED_SIZE_EX(jit_buffer_size, page_size);
3481
0
# ifndef ZEND_JIT_USE_APPLE_MAP_JIT
3482
0
      jit_size = jit_buffer_size;
3483
0
      shm_size += jit_size;
3484
0
# endif
3485
0
    }
3486
16
#endif
3487
3488
16
    switch (zend_shared_alloc_startup(shm_size, jit_size)) {
3489
16
      case ALLOC_SUCCESS:
3490
16
        if (zend_accel_init_shm() == FAILURE) {
3491
0
          accel_startup_ok = false;
3492
0
          return FAILURE;
3493
0
        }
3494
16
        break;
3495
16
      case ALLOC_FAILURE:
3496
0
        accel_startup_ok = false;
3497
0
        zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - probably not enough shared memory.");
3498
0
        return SUCCESS;
3499
0
      case NO_SHM_BACKEND:
3500
0
        zend_accel_error(ACCEL_LOG_INFO, "Opcode Caching is disabled: No available SHM backend. Set opcache.enable=0 to hide this message.");
3501
0
        zps_startup_failure("No available SHM backend", NULL, accelerator_remove_cb);
3502
        /* Do not abort PHP startup */
3503
0
        return SUCCESS;
3504
3505
0
      case SUCCESSFULLY_REATTACHED:
3506
0
#ifdef HAVE_JIT
3507
0
        reattached = true;
3508
0
#endif
3509
0
        zend_shared_alloc_lock();
3510
0
        accel_shared_globals = (zend_accel_shared_globals *) ZSMMG(app_shared_globals);
3511
0
        zend_interned_strings_set_request_storage_handlers(
3512
0
          accel_new_interned_string_for_php,
3513
0
          accel_init_interned_string_for_php,
3514
0
          accel_init_interned_string_for_php);
3515
0
        zend_shared_alloc_unlock();
3516
0
        break;
3517
0
      case FAILED_REATTACHED:
3518
0
        accel_startup_ok = false;
3519
0
        zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - cannot reattach to exiting shared memory.");
3520
0
        return SUCCESS;
3521
#if ENABLE_FILE_CACHE_FALLBACK
3522
      case ALLOC_FALLBACK:
3523
        zend_shared_alloc_lock();
3524
        file_cache_only = true;
3525
        fallback_process = true;
3526
        zend_shared_alloc_unlock();
3527
        goto file_cache_fallback;
3528
#endif
3529
16
    }
3530
3531
    /* from this point further, shared memory is supposed to be OK */
3532
3533
    /* remember the last restart time in the process memory */
3534
16
    ZCG(last_restart_time) = ZCSG(last_restart_time);
3535
3536
16
    zend_shared_alloc_lock();
3537
16
#ifdef HAVE_JIT
3538
16
    if (JIT_G(enabled)) {
3539
0
      if (JIT_G(buffer_size) == 0) {
3540
0
        JIT_G(enabled) = false;
3541
0
        JIT_G(on) = false;
3542
0
      } else {
3543
# ifdef ZEND_JIT_USE_APPLE_MAP_JIT
3544
        zend_jit_startup(NULL, jit_buffer_size, reattached);
3545
# else
3546
0
        if (!ZSMMG(reserved)) {
3547
0
          zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Could not enable JIT: could not use reserved buffer!");
3548
0
        }
3549
0
        zend_jit_startup(ZSMMG(reserved), jit_buffer_size, reattached);
3550
0
# endif
3551
0
        zend_jit_startup_ok = true;
3552
0
      }
3553
0
    }
3554
16
#endif
3555
16
    zend_shared_alloc_save_state();
3556
16
    zend_shared_alloc_unlock();
3557
3558
16
    SHM_PROTECT();
3559
16
  } else if (!ZCG(accel_directives).file_cache) {
3560
0
    accel_startup_ok = false;
3561
0
    zend_accel_error_noreturn(ACCEL_LOG_FATAL, "opcache.file_cache_only is set without a proper setting of opcache.file_cache");
3562
0
    return SUCCESS;
3563
0
  } else {
3564
0
#ifdef HAVE_JIT
3565
0
    JIT_G(enabled) = false;
3566
0
    JIT_G(on) = false;
3567
0
#endif
3568
0
    accel_shared_globals = calloc(1, sizeof(zend_accel_shared_globals));
3569
0
  }
3570
3571
  /* opcache.file_cache_read_only should only be enabled when all script files are read-only */
3572
16
  int file_cache_access_mode = 0;
3573
3574
16
  if (ZCG(accel_directives).file_cache_read_only) {
3575
0
    zend_accel_error(ACCEL_LOG_INFO, "opcache.file_cache is in read-only mode");
3576
3577
0
    if (!ZCG(accel_directives).file_cache) {
3578
0
      accel_startup_ok = false;
3579
0
      zend_accel_error_noreturn(ACCEL_LOG_FATAL, "opcache.file_cache_read_only is set without a proper setting of opcache.file_cache");
3580
0
      return SUCCESS;
3581
0
    }
3582
3583
    /* opcache.file_cache is read only, so ensure the directory is readable */
3584
0
#ifndef ZEND_WIN32
3585
0
    file_cache_access_mode = R_OK | X_OK;
3586
#else
3587
    file_cache_access_mode = 04; // Read access
3588
#endif
3589
16
  } else {
3590
    /* opcache.file_cache isn't read only, so ensure the directory is writable */
3591
16
#ifndef ZEND_WIN32
3592
16
    file_cache_access_mode = R_OK | W_OK | X_OK;
3593
#else
3594
    file_cache_access_mode = 06; // Read and write access
3595
#endif
3596
16
  }
3597
3598
16
  if ( ZCG(accel_directives).file_cache ) {
3599
0
    zend_accel_error(ACCEL_LOG_INFO, "opcache.file_cache running with PHP build ID: %.32s", zend_system_id);
3600
3601
0
    zend_stat_t buf = {0};
3602
3603
0
    if (!IS_ABSOLUTE_PATH(ZCG(accel_directives).file_cache, strlen(ZCG(accel_directives).file_cache)) ||
3604
0
      zend_stat(ZCG(accel_directives).file_cache, &buf) != 0 ||
3605
0
      !S_ISDIR(buf.st_mode) ||
3606
0
#ifndef ZEND_WIN32
3607
0
      access(ZCG(accel_directives).file_cache, file_cache_access_mode) != 0
3608
#else
3609
      _access(ZCG(accel_directives).file_cache, file_cache_access_mode) != 0
3610
#endif
3611
0
      ) {
3612
0
      accel_startup_ok = false;
3613
0
      zend_accel_error_noreturn(ACCEL_LOG_FATAL, "opcache.file_cache must be a full path of an accessible directory");
3614
0
      return SUCCESS;
3615
0
    }
3616
0
  }
3617
3618
#if ENABLE_FILE_CACHE_FALLBACK
3619
file_cache_fallback:
3620
#endif
3621
3622
  /* Override compiler */
3623
16
  accelerator_orig_compile_file = zend_compile_file;
3624
16
  zend_compile_file = persistent_compile_file;
3625
3626
  /* Override stream opener function (to eliminate open() call caused by
3627
   * include/require statements ) */
3628
16
  accelerator_orig_zend_stream_open_function = zend_stream_open_function;
3629
16
  zend_stream_open_function = persistent_stream_open_function;
3630
3631
  /* Override path resolver function (to eliminate stat() calls caused by
3632
   * include_once/require_once statements */
3633
16
  accelerator_orig_zend_resolve_path = zend_resolve_path;
3634
16
  zend_resolve_path = persistent_zend_resolve_path;
3635
3636
  /* Override chdir() function */
3637
16
  if ((func = zend_hash_str_find_ptr(CG(function_table), "chdir", sizeof("chdir")-1)) != NULL &&
3638
0
      func->type == ZEND_INTERNAL_FUNCTION) {
3639
0
    orig_chdir = func->internal_function.handler;
3640
0
    func->internal_function.handler = ZEND_FN(accel_chdir);
3641
0
  }
3642
16
  ZCG(cwd) = NULL;
3643
16
  ZCG(include_path) = NULL;
3644
3645
  /* Override "include_path" modifier callback */
3646
16
  if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) {
3647
16
    ZCG(include_path) = ini_entry->value;
3648
16
    orig_include_path_on_modify = ini_entry->on_modify;
3649
16
    ini_entry->on_modify = accel_include_path_on_modify;
3650
16
  }
3651
3652
16
  accel_startup_ok = true;
3653
3654
  /* Override file_exists(), is_file() and is_readable() */
3655
16
  zend_accel_override_file_functions();
3656
3657
  /* Load black list */
3658
16
  accel_blacklist.entries = NULL;
3659
16
  if (ZCG(enabled) && accel_startup_ok &&
3660
16
      ZCG(accel_directives).user_blacklist_filename &&
3661
16
      *ZCG(accel_directives.user_blacklist_filename)) {
3662
0
    zend_accel_blacklist_init(&accel_blacklist);
3663
0
    zend_accel_blacklist_load(&accel_blacklist, ZCG(accel_directives.user_blacklist_filename));
3664
0
  }
3665
3666
16
  if (!file_cache_only && ZCG(accel_directives).interned_strings_buffer) {
3667
16
    accel_use_shm_interned_strings();
3668
16
  }
3669
3670
16
  if (accel_finish_startup() != SUCCESS) {
3671
0
    return FAILURE;
3672
0
  }
3673
3674
16
  if (ZCG(enabled) && accel_startup_ok) {
3675
    /* Override inheritance cache callbaks */
3676
16
    accelerator_orig_inheritance_cache_get = zend_inheritance_cache_get;
3677
16
    accelerator_orig_inheritance_cache_add = zend_inheritance_cache_add;
3678
16
    zend_inheritance_cache_get = zend_accel_inheritance_cache_get;
3679
16
    zend_inheritance_cache_add = zend_accel_inheritance_cache_add;
3680
16
  }
3681
3682
16
  return SUCCESS;
3683
16
}
3684
3685
static void (*orig_post_shutdown_cb)(void);
3686
3687
static void accel_post_shutdown(void)
3688
0
{
3689
0
  zend_shared_alloc_shutdown();
3690
0
}
3691
3692
void accel_shutdown(void)
3693
0
{
3694
0
  zend_ini_entry *ini_entry;
3695
0
  bool _file_cache_only = false;
3696
3697
0
#ifdef HAVE_JIT
3698
0
  zend_jit_shutdown();
3699
0
#endif
3700
3701
0
  zend_accel_blacklist_shutdown(&accel_blacklist);
3702
3703
0
  if (!ZCG(enabled) || !accel_startup_ok) {
3704
#ifdef ZTS
3705
    ts_free_id(accel_globals_id);
3706
#else
3707
0
    accel_globals_dtor(&accel_globals);
3708
0
#endif
3709
0
    return;
3710
0
  }
3711
3712
0
#ifdef PRELOAD_SUPPORT
3713
0
  if (ZCSG(preload_script)) {
3714
0
    preload_shutdown();
3715
0
  }
3716
0
#endif
3717
3718
0
  _file_cache_only = file_cache_only;
3719
3720
0
  accel_reset_pcre_cache();
3721
3722
#ifdef ZTS
3723
  ts_free_id(accel_globals_id);
3724
#else
3725
0
  accel_globals_dtor(&accel_globals);
3726
0
#endif
3727
3728
0
  if (!_file_cache_only) {
3729
    /* Delay SHM detach */
3730
0
    orig_post_shutdown_cb = zend_post_shutdown_cb;
3731
0
    zend_post_shutdown_cb = accel_post_shutdown;
3732
0
  } else {
3733
0
    free(accel_shared_globals);
3734
0
  }
3735
3736
0
  zend_compile_file = accelerator_orig_compile_file;
3737
0
  zend_inheritance_cache_get = accelerator_orig_inheritance_cache_get;
3738
0
  zend_inheritance_cache_add = accelerator_orig_inheritance_cache_add;
3739
3740
0
  if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) {
3741
0
    ini_entry->on_modify = orig_include_path_on_modify;
3742
0
  }
3743
3744
0
  accel_startup_ok = false;
3745
0
}
3746
3747
void zend_accel_schedule_restart(zend_accel_restart_reason reason)
3748
0
{
3749
0
  const char *zend_accel_restart_reason_text[ACCEL_RESTART_USER + 1] = {
3750
0
    "out of memory",
3751
0
    "hash overflow",
3752
0
    "user",
3753
0
  };
3754
3755
0
  if (ZCSG(restart_pending)) {
3756
    /* don't schedule twice */
3757
0
    return;
3758
0
  }
3759
3760
0
  if (UNEXPECTED(zend_accel_schedule_restart_hook)) {
3761
0
    zend_accel_schedule_restart_hook(reason);
3762
0
  }
3763
3764
0
  zend_accel_error(ACCEL_LOG_DEBUG, "Restart Scheduled! Reason: %s",
3765
0
      zend_accel_restart_reason_text[reason]);
3766
3767
0
  HANDLE_BLOCK_INTERRUPTIONS();
3768
0
  SHM_UNPROTECT();
3769
0
  ZCSG(restart_pending) = true;
3770
0
  ZCSG(restart_reason) = reason;
3771
0
  ZCSG(cache_status_before_restart) = ZCSG(accelerator_enabled);
3772
0
  ZCSG(accelerator_enabled) = false;
3773
3774
0
  if (ZCG(accel_directives).force_restart_timeout) {
3775
0
    ZCSG(force_restart_time) = zend_accel_get_time() + ZCG(accel_directives).force_restart_timeout;
3776
0
  } else {
3777
0
    ZCSG(force_restart_time) = 0;
3778
0
  }
3779
0
  SHM_PROTECT();
3780
0
  HANDLE_UNBLOCK_INTERRUPTIONS();
3781
0
}
3782
3783
static void accel_deactivate_now(void)
3784
0
{
3785
  /* this is needed because on WIN32 lock is not decreased unless ZCG(counted) is set */
3786
#ifdef ZEND_WIN32
3787
  ZCG(counted) = true;
3788
#endif
3789
0
  accel_deactivate_sub();
3790
0
}
3791
3792
/* ensures it is OK to read SHM
3793
  if it's not OK (restart in progress) returns FAILURE
3794
  if OK returns SUCCESS
3795
  MUST call accelerator_shm_read_unlock after done lock operations
3796
*/
3797
zend_result accelerator_shm_read_lock(void)
3798
72.6k
{
3799
72.6k
  if (ZCG(counted)) {
3800
    /* counted means we are holding read lock for SHM, so that nothing bad can happen */
3801
72.6k
    return SUCCESS;
3802
72.6k
  } else {
3803
    /* here accelerator is active but we do not hold SHM lock. This means restart was scheduled
3804
      or is in progress now */
3805
0
    if (accel_activate_add() == FAILURE) { /* acquire usage lock */
3806
0
      return FAILURE;
3807
0
    }
3808
    /* Now if we weren't inside restart, restart would not begin until we remove usage lock */
3809
0
    if (ZCSG(restart_in_progress)) {
3810
      /* we already were inside restart this means it's not safe to touch shm */
3811
0
      accel_deactivate_now(); /* drop usage lock */
3812
0
      return FAILURE;
3813
0
    }
3814
0
    ZCG(counted) = true;
3815
0
  }
3816
0
  return SUCCESS;
3817
72.6k
}
3818
3819
/* must be called ONLY after SUCCESSFUL accelerator_shm_read_lock */
3820
void accelerator_shm_read_unlock(void)
3821
72.6k
{
3822
72.6k
  if (!ZCG(counted)) {
3823
    /* counted is false - meaning we had to readlock manually, release readlock now */
3824
0
    accel_deactivate_now();
3825
0
  }
3826
72.6k
}
3827
3828
/* Preloading */
3829
#ifdef PRELOAD_SUPPORT
3830
static HashTable *preload_scripts = NULL;
3831
static zend_op_array *(*preload_orig_compile_file)(zend_file_handle *file_handle, int type);
3832
3833
static void preload_shutdown(void)
3834
0
{
3835
0
  zval *zv;
3836
3837
#if 0
3838
  if (EG(zend_constants)) {
3839
    ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(zend_constants), zv) {
3840
      zend_constant *c = Z_PTR_P(zv);
3841
      if (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT) {
3842
        break;
3843
      }
3844
    } ZEND_HASH_MAP_FOREACH_END_DEL();
3845
  }
3846
#endif
3847
3848
0
  if (EG(function_table)) {
3849
0
    ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(function_table), zv) {
3850
0
      const zend_function *func = Z_PTR_P(zv);
3851
0
      if (func->type == ZEND_INTERNAL_FUNCTION) {
3852
0
        break;
3853
0
      }
3854
0
    } ZEND_HASH_MAP_FOREACH_END_DEL();
3855
0
  }
3856
3857
0
  if (EG(class_table)) {
3858
0
    ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(class_table), zv) {
3859
0
      const zend_class_entry *ce = Z_PTR_P(zv);
3860
0
      if (ce->type == ZEND_INTERNAL_CLASS && Z_TYPE_P(zv) != IS_ALIAS_PTR) {
3861
0
        break;
3862
0
      }
3863
0
    } ZEND_HASH_MAP_FOREACH_END_DEL();
3864
0
  }
3865
0
}
3866
3867
static void preload_activate(void)
3868
0
{
3869
0
  if (ZCSG(preload_script)->ping_auto_globals_mask & ~ZCG(auto_globals_mask)) {
3870
0
    zend_accel_set_auto_globals(ZCSG(preload_script)->ping_auto_globals_mask & ~ZCG(auto_globals_mask));
3871
0
  }
3872
0
}
3873
3874
static void preload_restart(void)
3875
0
{
3876
0
  zend_accel_hash_update(&ZCSG(hash), ZCSG(preload_script)->script.filename, 0, ZCSG(preload_script));
3877
0
  if (ZCSG(saved_scripts)) {
3878
0
    zend_persistent_script **p = ZCSG(saved_scripts);
3879
0
    while (*p) {
3880
0
      zend_accel_hash_update(&ZCSG(hash), (*p)->script.filename, 0, *p);
3881
0
      p++;
3882
0
    }
3883
0
  }
3884
0
}
3885
3886
0
static size_t preload_try_strip_filename(const zend_string *filename) {
3887
  /*FIXME: better way to handle eval()'d code? see COMPILED_STRING_DESCRIPTION_FORMAT */
3888
0
  if (ZSTR_LEN(filename) > sizeof(" eval()'d code")
3889
0
    && *(ZSTR_VAL(filename) + ZSTR_LEN(filename) - sizeof(" eval()'d code")) == ':') {
3890
0
    const char *cfilename = ZSTR_VAL(filename);
3891
0
    size_t cfilenamelen = ZSTR_LEN(filename) - sizeof(" eval()'d code") - 1 /*:*/;
3892
0
    while (cfilenamelen && cfilename[--cfilenamelen] != '(');
3893
0
    return cfilenamelen;
3894
0
  }
3895
0
  return 0;
3896
0
}
3897
3898
static void preload_move_user_functions(HashTable *src, HashTable *dst)
3899
0
{
3900
0
  Bucket *p;
3901
0
  dtor_func_t orig_dtor = src->pDestructor;
3902
0
  zend_string *filename = NULL;
3903
0
  bool copy = false;
3904
3905
0
  src->pDestructor = NULL;
3906
0
  zend_hash_extend(dst, dst->nNumUsed + src->nNumUsed, 0);
3907
0
  ZEND_HASH_MAP_REVERSE_FOREACH_BUCKET(src, p) {
3908
0
    zend_function *function = Z_PTR(p->val);
3909
3910
0
    if (EXPECTED(function->type == ZEND_USER_FUNCTION)) {
3911
0
      if (function->op_array.filename != filename) {
3912
0
        filename = function->op_array.filename;
3913
0
        if (filename) {
3914
0
          if (!(copy = zend_hash_exists(preload_scripts, filename))) {
3915
0
            size_t eval_len = preload_try_strip_filename(filename);
3916
0
            if (eval_len) {
3917
0
              copy = zend_hash_str_exists(preload_scripts, ZSTR_VAL(filename), eval_len);
3918
0
            }
3919
0
          }
3920
0
        } else {
3921
0
          copy = false;
3922
0
        }
3923
0
      }
3924
0
      if (copy) {
3925
0
        _zend_hash_append_ptr(dst, p->key, function);
3926
0
      } else {
3927
0
        orig_dtor(&p->val);
3928
0
      }
3929
0
      zend_hash_del_bucket(src, p);
3930
0
    } else {
3931
0
      break;
3932
0
    }
3933
0
  } ZEND_HASH_FOREACH_END();
3934
0
  src->pDestructor = orig_dtor;
3935
0
}
3936
3937
static void preload_move_user_classes(HashTable *src, HashTable *dst)
3938
0
{
3939
0
  Bucket *p;
3940
0
  dtor_func_t orig_dtor = src->pDestructor;
3941
0
  zend_string *filename = NULL;
3942
0
  bool copy = false;
3943
3944
0
  src->pDestructor = NULL;
3945
0
  zend_hash_extend(dst, dst->nNumUsed + src->nNumUsed, 0);
3946
0
  ZEND_HASH_MAP_FOREACH_BUCKET_FROM(src, p, EG(persistent_classes_count)) {
3947
0
    zend_class_entry *ce = Z_PTR(p->val);
3948
3949
    /* Possible with internal class aliases */
3950
0
    if (ce->type == ZEND_INTERNAL_CLASS) {
3951
0
      ZEND_ASSERT(Z_TYPE(p->val) == IS_ALIAS_PTR);
3952
0
      _zend_hash_append(dst, p->key, &p->val);
3953
0
      zend_hash_del_bucket(src, p);
3954
0
      continue;
3955
0
    }
3956
3957
0
    if (ce->info.user.filename != filename) {
3958
0
      filename = ce->info.user.filename;
3959
0
      if (filename) {
3960
0
        if (!(copy = zend_hash_exists(preload_scripts, filename))) {
3961
0
          size_t eval_len = preload_try_strip_filename(filename);
3962
0
          if (eval_len) {
3963
0
            copy = zend_hash_str_exists(preload_scripts, ZSTR_VAL(filename), eval_len);
3964
0
          }
3965
0
        }
3966
0
      } else {
3967
0
        copy = false;
3968
0
      }
3969
0
    }
3970
0
    if (copy) {
3971
0
      _zend_hash_append(dst, p->key, &p->val);
3972
0
    } else {
3973
0
      orig_dtor(&p->val);
3974
0
    }
3975
0
    zend_hash_del_bucket(src, p);
3976
0
  } ZEND_HASH_FOREACH_END();
3977
0
  src->pDestructor = orig_dtor;
3978
0
}
3979
3980
static zend_op_array *preload_compile_file(zend_file_handle *file_handle, int type)
3981
0
{
3982
0
  zend_op_array *op_array = preload_orig_compile_file(file_handle, type);
3983
3984
0
  if (op_array && op_array->refcount) {
3985
0
    zend_persistent_script *script;
3986
3987
0
    script = create_persistent_script();
3988
0
    script->script.filename = zend_string_copy(op_array->filename);
3989
0
    zend_string_hash_val(script->script.filename);
3990
0
    script->script.main_op_array = *op_array;
3991
3992
//???   efree(op_array->refcount);
3993
0
    op_array->refcount = NULL;
3994
3995
0
    zend_hash_add_ptr(preload_scripts, script->script.filename, script);
3996
0
  }
3997
3998
0
  return op_array;
3999
0
}
4000
4001
static void preload_sort_classes(void *base, size_t count, size_t siz, compare_func_t compare, swap_func_t swp)
4002
0
{
4003
0
  Bucket *b1 = base;
4004
0
  Bucket *b2;
4005
0
  Bucket *end = b1 + count;
4006
0
  Bucket tmp;
4007
0
  const zend_class_entry *ce, *p;
4008
4009
0
  while (b1 < end) {
4010
0
try_again:
4011
0
    ce = Z_PTR(b1->val);
4012
0
    if (ce->parent && (ce->ce_flags & ZEND_ACC_LINKED)) {
4013
0
      p = ce->parent;
4014
0
      if (p->type == ZEND_USER_CLASS) {
4015
0
        b2 = b1 + 1;
4016
0
        while (b2 < end) {
4017
0
          if (p == Z_PTR(b2->val)) {
4018
0
            tmp = *b1;
4019
0
            *b1 = *b2;
4020
0
            *b2 = tmp;
4021
0
            goto try_again;
4022
0
          }
4023
0
          b2++;
4024
0
        }
4025
0
      }
4026
0
    }
4027
0
    if (ce->num_interfaces && (ce->ce_flags & ZEND_ACC_LINKED)) {
4028
0
      uint32_t i = 0;
4029
0
      for (i = 0; i < ce->num_interfaces; i++) {
4030
0
        p = ce->interfaces[i];
4031
0
        if (p->type == ZEND_USER_CLASS) {
4032
0
          b2 = b1 + 1;
4033
0
          while (b2 < end) {
4034
0
            if (p == Z_PTR(b2->val)) {
4035
0
              tmp = *b1;
4036
0
              *b1 = *b2;
4037
0
              *b2 = tmp;
4038
0
              goto try_again;
4039
0
            }
4040
0
            b2++;
4041
0
          }
4042
0
        }
4043
0
      }
4044
0
    }
4045
0
    b1++;
4046
0
  }
4047
0
}
4048
4049
typedef struct {
4050
  const char *kind;
4051
  const char *name;
4052
} preload_error;
4053
4054
static zend_result preload_resolve_deps(preload_error *error, const zend_class_entry *ce)
4055
0
{
4056
0
  memset(error, 0, sizeof(preload_error));
4057
4058
0
  if (ce->parent_name) {
4059
0
    const zend_class_entry *parent = zend_hash_find_ptr_lc(EG(class_table), ce->parent_name);
4060
0
    if (!parent) {
4061
0
      error->kind = "Unknown parent ";
4062
0
      error->name = ZSTR_VAL(ce->parent_name);
4063
0
      return FAILURE;
4064
0
    }
4065
0
  }
4066
4067
0
  if (ce->num_interfaces) {
4068
0
    for (uint32_t i = 0; i < ce->num_interfaces; i++) {
4069
0
      const zend_class_entry *interface =
4070
0
        zend_hash_find_ptr(EG(class_table), ce->interface_names[i].lc_name);
4071
0
      if (!interface) {
4072
0
        error->kind = "Unknown interface ";
4073
0
        error->name = ZSTR_VAL(ce->interface_names[i].name);
4074
0
        return FAILURE;
4075
0
      }
4076
0
    }
4077
0
  }
4078
4079
0
  if (ce->num_traits) {
4080
0
    for (uint32_t i = 0; i < ce->num_traits; i++) {
4081
0
      const zend_class_entry *trait =
4082
0
        zend_hash_find_ptr(EG(class_table), ce->trait_names[i].lc_name);
4083
0
      if (!trait) {
4084
0
        error->kind = "Unknown trait ";
4085
0
        error->name = ZSTR_VAL(ce->trait_names[i].name);
4086
0
        return FAILURE;
4087
0
      }
4088
0
    }
4089
0
  }
4090
4091
0
  return SUCCESS;
4092
0
}
4093
4094
static bool preload_try_resolve_constants(zend_class_entry *ce)
4095
0
{
4096
0
  bool ok, changed, was_changed = false;
4097
0
  zend_class_constant *c;
4098
0
  zval *val;
4099
0
  zend_string *key;
4100
4101
0
  EG(exception) = (void*)(uintptr_t)-1; /* prevent error reporting */
4102
0
  do {
4103
0
    ok = true;
4104
0
    changed = false;
4105
0
    ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) {
4106
0
      val = &c->value;
4107
0
      if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
4108
        /* For deprecated constants, we need to flag the zval for recursion
4109
         * detection. Make sure the zval is separated out of shm. */
4110
0
        if (ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED) {
4111
0
          ok = false;
4112
0
        }
4113
0
        if (EXPECTED(zend_update_class_constant(c, key, c->ce) == SUCCESS)) {
4114
0
          was_changed = changed = true;
4115
0
        } else {
4116
0
          ok = false;
4117
0
        }
4118
0
      }
4119
0
    } ZEND_HASH_FOREACH_END();
4120
0
    if (ok) {
4121
0
      ce->ce_flags &= ~ZEND_ACC_HAS_AST_CONSTANTS;
4122
0
    }
4123
0
    if (ce->default_properties_count) {
4124
0
      bool resolved = true;
4125
4126
0
      for (uint32_t i = 0; i < ce->default_properties_count; i++) {
4127
0
        zend_property_info *prop = ce->properties_info_table[i];
4128
0
        if (!prop) {
4129
0
          continue;
4130
0
        }
4131
4132
0
        val = &ce->default_properties_table[OBJ_PROP_TO_NUM(prop->offset)];
4133
0
        if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
4134
0
          if (UNEXPECTED(zval_update_constant_ex(val, prop->ce) != SUCCESS)) {
4135
0
            resolved = ok = false;
4136
0
          }
4137
0
        }
4138
0
      }
4139
0
      if (resolved) {
4140
0
        ce->ce_flags &= ~ZEND_ACC_HAS_AST_PROPERTIES;
4141
0
      }
4142
0
    }
4143
0
    if (ce->default_static_members_count) {
4144
0
      uint32_t count = ce->parent ? ce->default_static_members_count - ce->parent->default_static_members_count : ce->default_static_members_count;
4145
0
      bool resolved = true;
4146
4147
0
      val = ce->default_static_members_table + ce->default_static_members_count - 1;
4148
0
      while (count) {
4149
0
        if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
4150
0
          if (UNEXPECTED(zval_update_constant_ex(val, ce) != SUCCESS)) {
4151
0
            resolved = ok = false;
4152
0
          }
4153
0
        }
4154
0
        val--;
4155
0
        count--;
4156
0
      }
4157
0
      if (resolved) {
4158
0
        ce->ce_flags &= ~ZEND_ACC_HAS_AST_STATICS;
4159
0
      }
4160
0
    }
4161
0
  } while (changed && !ok);
4162
0
  EG(exception) = NULL;
4163
0
  CG(in_compilation) = false;
4164
4165
0
  if (ok) {
4166
0
    ce->ce_flags |= ZEND_ACC_CONSTANTS_UPDATED;
4167
0
  }
4168
4169
0
  return ok || was_changed;
4170
0
}
4171
4172
static void (*orig_error_cb)(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message);
4173
4174
static void preload_error_cb(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message)
4175
0
{
4176
  /* Suppress printing of the error, only bail out for fatal errors. */
4177
0
  if (type & E_FATAL_ERRORS) {
4178
0
    zend_bailout();
4179
0
  }
4180
0
}
4181
4182
/* Remove DECLARE opcodes and dynamic defs. */
4183
static void preload_remove_declares(zend_op_array *op_array)
4184
0
{
4185
0
  zend_op *opline = op_array->opcodes;
4186
0
  const zend_op *end = opline + op_array->last;
4187
0
  uint32_t skip_dynamic_func_count = 0;
4188
0
  zend_string *key;
4189
0
  zend_op_array *func;
4190
4191
0
  while (opline != end) {
4192
0
    switch (opline->opcode) {
4193
0
      case ZEND_DECLARE_CLASS:
4194
0
      case ZEND_DECLARE_CLASS_DELAYED:
4195
0
        key = Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1);
4196
0
        if (!zend_hash_exists(CG(class_table), key)) {
4197
0
          MAKE_NOP(opline);
4198
0
        }
4199
0
        break;
4200
0
      case ZEND_DECLARE_FUNCTION:
4201
0
        opline->op2.num -= skip_dynamic_func_count;
4202
0
        key = Z_STR_P(RT_CONSTANT(opline, opline->op1));
4203
0
        func = zend_hash_find_ptr(EG(function_table), key);
4204
0
        if (func && func == op_array->dynamic_func_defs[opline->op2.num]) {
4205
0
          zend_op_array **dynamic_func_defs;
4206
4207
0
          op_array->num_dynamic_func_defs--;
4208
0
          if (op_array->num_dynamic_func_defs == 0) {
4209
0
            dynamic_func_defs = NULL;
4210
0
          } else {
4211
0
            dynamic_func_defs = emalloc(sizeof(zend_op_array*) * op_array->num_dynamic_func_defs);
4212
0
            if (opline->op2.num > 0) {
4213
0
              memcpy(
4214
0
                dynamic_func_defs,
4215
0
                op_array->dynamic_func_defs,
4216
0
                sizeof(zend_op_array*) * opline->op2.num);
4217
0
            }
4218
0
            if (op_array->num_dynamic_func_defs - opline->op2.num > 0) {
4219
0
              memcpy(
4220
0
                dynamic_func_defs + opline->op2.num,
4221
0
                op_array->dynamic_func_defs + (opline->op2.num + 1),
4222
0
                sizeof(zend_op_array*) * (op_array->num_dynamic_func_defs - opline->op2.num));
4223
0
            }
4224
0
          }
4225
0
          efree(op_array->dynamic_func_defs);
4226
0
          op_array->dynamic_func_defs = dynamic_func_defs;
4227
0
          skip_dynamic_func_count++;
4228
0
          MAKE_NOP(opline);
4229
0
        }
4230
0
        break;
4231
0
      case ZEND_DECLARE_LAMBDA_FUNCTION:
4232
0
        opline->op2.num -= skip_dynamic_func_count;
4233
0
        break;
4234
0
    }
4235
0
    opline++;
4236
0
  }
4237
0
}
4238
4239
static void preload_link(void)
4240
0
{
4241
0
  zval *zv;
4242
0
  zend_persistent_script *script;
4243
0
  zend_class_entry *ce;
4244
0
  zend_string *key;
4245
0
  bool changed;
4246
4247
0
  HashTable errors;
4248
0
  zend_hash_init(&errors, 0, NULL, NULL, 0);
4249
4250
  /* Resolve class dependencies */
4251
0
  do {
4252
0
    changed = false;
4253
4254
0
    ZEND_HASH_MAP_FOREACH_STR_KEY_VAL_FROM(EG(class_table), key, zv, EG(persistent_classes_count)) {
4255
0
      ce = Z_PTR_P(zv);
4256
4257
      /* Possible with internal class aliases */
4258
0
      if (ce->type == ZEND_INTERNAL_CLASS) {
4259
0
        ZEND_ASSERT(Z_TYPE_P(zv) == IS_ALIAS_PTR);
4260
0
        continue;
4261
0
      }
4262
4263
0
      if (!(ce->ce_flags & (ZEND_ACC_TOP_LEVEL|ZEND_ACC_ANON_CLASS))
4264
0
          || (ce->ce_flags & ZEND_ACC_LINKED)) {
4265
0
        continue;
4266
0
      }
4267
4268
0
      zend_string *lcname = zend_string_tolower(ce->name);
4269
0
      if (!(ce->ce_flags & ZEND_ACC_ANON_CLASS)) {
4270
0
        if (zend_hash_exists(EG(class_table), lcname)) {
4271
0
          zend_string_release(lcname);
4272
0
          continue;
4273
0
        }
4274
0
      }
4275
4276
0
      preload_error error_info;
4277
0
      if (preload_resolve_deps(&error_info, ce) == FAILURE) {
4278
0
        zend_string_release(lcname);
4279
0
        continue;
4280
0
      }
4281
4282
0
      zv = zend_hash_set_bucket_key(EG(class_table), (Bucket*)zv, lcname);
4283
0
      ZEND_ASSERT(zv && "We already checked above that the class doesn't exist yet");
4284
4285
      /* Set the FILE_CACHED flag to force a lazy load, and the CACHED flag to
4286
       * prevent freeing of interface names. */
4287
0
      void *checkpoint = zend_arena_checkpoint(CG(arena));
4288
0
      zend_class_entry *orig_ce = ce;
4289
0
      uint32_t temporary_flags = ZEND_ACC_FILE_CACHED|ZEND_ACC_CACHED;
4290
0
      ce->ce_flags |= temporary_flags;
4291
0
      if (ce->parent_name) {
4292
0
        zend_string_addref(ce->parent_name);
4293
0
      }
4294
4295
      /* Record and suppress errors during inheritance. */
4296
0
      orig_error_cb = zend_error_cb;
4297
0
      zend_error_cb = preload_error_cb;
4298
0
      zend_begin_record_errors();
4299
4300
      /* Set filename & lineno information for inheritance errors */
4301
0
      CG(in_compilation) = true;
4302
0
      CG(compiled_filename) = ce->info.user.filename;
4303
0
      CG(zend_lineno) = ce->info.user.line_start;
4304
0
      zend_try {
4305
0
        ce = zend_do_link_class(ce, NULL, lcname);
4306
0
        if (!ce) {
4307
0
          ZEND_ASSERT(0 && "Class linking failed?");
4308
0
        }
4309
0
        ce->ce_flags &= ~temporary_flags;
4310
0
        changed = true;
4311
4312
        /* Inheritance successful, print out any warnings. */
4313
0
        zend_error_cb = orig_error_cb;
4314
0
        zend_emit_recorded_errors();
4315
0
      } zend_catch {
4316
        /* Clear variance obligations that were left behind on bailout. */
4317
0
        if (CG(delayed_variance_obligations)) {
4318
0
          zend_hash_index_del(
4319
0
            CG(delayed_variance_obligations), (uintptr_t) Z_CE_P(zv));
4320
0
        }
4321
4322
        /* Restore the original class. */
4323
0
        zv = zend_hash_set_bucket_key(EG(class_table), (Bucket*)zv, key);
4324
0
        Z_CE_P(zv) = orig_ce;
4325
0
        orig_ce->ce_flags &= ~temporary_flags;
4326
0
        zend_arena_release(&CG(arena), checkpoint);
4327
4328
        /* Remember the last error. */
4329
0
        zend_error_cb = orig_error_cb;
4330
0
        EG(record_errors) = false;
4331
0
        ZEND_ASSERT(EG(errors).size > 0);
4332
0
        zend_hash_update_ptr(&errors, key, EG(errors).errors[EG(errors).size-1]);
4333
0
        EG(errors).size--;
4334
0
      } zend_end_try();
4335
0
      CG(in_compilation) = false;
4336
0
      CG(compiled_filename) = NULL;
4337
0
      zend_free_recorded_errors();
4338
0
      zend_string_release(lcname);
4339
0
    } ZEND_HASH_FOREACH_END();
4340
0
  } while (changed);
4341
4342
0
  do {
4343
0
    changed = false;
4344
4345
0
    ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(class_table), zv) {
4346
0
      ce = Z_PTR_P(zv);
4347
4348
      /* Possible with internal class aliases */
4349
0
      if (ce->type == ZEND_INTERNAL_CLASS) {
4350
0
        if (Z_TYPE_P(zv) != IS_ALIAS_PTR) {
4351
0
          break; /* can stop already */
4352
0
        }
4353
0
        continue;
4354
0
      }
4355
4356
0
      if ((ce->ce_flags & ZEND_ACC_LINKED) && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
4357
0
        if (!(ce->ce_flags & ZEND_ACC_TRAIT)) { /* don't update traits */
4358
0
          CG(in_compilation) = true; /* prevent autoloading */
4359
0
          if (preload_try_resolve_constants(ce)) {
4360
0
            changed = true;
4361
0
          }
4362
0
          CG(in_compilation) = false;
4363
0
        }
4364
0
      }
4365
0
    } ZEND_HASH_FOREACH_END();
4366
0
  } while (changed);
4367
4368
  /* Warn for classes that could not be linked. */
4369
0
  ZEND_HASH_MAP_FOREACH_STR_KEY_VAL_FROM(
4370
0
      EG(class_table), key, zv, EG(persistent_classes_count)) {
4371
0
    ce = Z_PTR_P(zv);
4372
4373
    /* Possible with internal class aliases */
4374
0
    if (ce->type == ZEND_INTERNAL_CLASS) {
4375
0
      ZEND_ASSERT(Z_TYPE_P(zv) == IS_ALIAS_PTR);
4376
0
      continue;
4377
0
    }
4378
4379
0
    if ((ce->ce_flags & (ZEND_ACC_TOP_LEVEL|ZEND_ACC_ANON_CLASS))
4380
0
        && !(ce->ce_flags & ZEND_ACC_LINKED)) {
4381
0
      zend_string *lcname = zend_string_tolower(ce->name);
4382
0
      preload_error error;
4383
0
      if (!(ce->ce_flags & ZEND_ACC_ANON_CLASS)
4384
0
       && zend_hash_exists(EG(class_table), lcname)) {
4385
0
        zend_error_at(
4386
0
          E_WARNING, ce->info.user.filename, ce->info.user.line_start,
4387
0
          "Can't preload already declared class %s", ZSTR_VAL(ce->name));
4388
0
      } else if (preload_resolve_deps(&error, ce) == FAILURE) {
4389
0
        zend_error_at(
4390
0
          E_WARNING, ce->info.user.filename, ce->info.user.line_start,
4391
0
          "Can't preload unlinked class %s: %s%s",
4392
0
          ZSTR_VAL(ce->name), error.kind, error.name);
4393
0
      } else {
4394
0
        zend_error_info *error = zend_hash_find_ptr(&errors, key);
4395
0
        zend_error_at(
4396
0
          E_WARNING, error->filename, error->lineno,
4397
0
          "Can't preload unlinked class %s: %s",
4398
0
          ZSTR_VAL(ce->name), ZSTR_VAL(error->message));
4399
0
      }
4400
0
      zend_string_release(lcname);
4401
0
    }
4402
0
  } ZEND_HASH_FOREACH_END();
4403
4404
0
  zend_hash_destroy(&errors);
4405
4406
0
  ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4407
0
    zend_op_array *op_array = &script->script.main_op_array;
4408
0
    preload_remove_declares(op_array);
4409
4410
0
    if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) {
4411
0
      zend_accel_free_delayed_early_binding_list(script);
4412
0
      zend_accel_build_delayed_early_binding_list(script);
4413
0
      if (!script->num_early_bindings) {
4414
0
        op_array->fn_flags &= ~ZEND_ACC_EARLY_BINDING;
4415
0
      }
4416
0
    }
4417
0
  } ZEND_HASH_FOREACH_END();
4418
4419
  /* Dynamic defs inside functions and methods need to be removed as well. */
4420
0
  zend_op_array *op_array;
4421
0
  ZEND_HASH_MAP_FOREACH_PTR_FROM(EG(function_table), op_array, EG(persistent_functions_count)) {
4422
0
    ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION);
4423
0
    preload_remove_declares(op_array);
4424
0
  } ZEND_HASH_FOREACH_END();
4425
0
  ZEND_HASH_MAP_FOREACH_PTR_FROM(EG(class_table), ce, EG(persistent_classes_count)) {
4426
0
    ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
4427
0
      if (op_array->type == ZEND_USER_FUNCTION) {
4428
0
        preload_remove_declares(op_array);
4429
0
      }
4430
0
    } ZEND_HASH_FOREACH_END();
4431
4432
0
    if (ce->num_hooked_props > 0) {
4433
0
      zend_property_info *info;
4434
4435
0
      ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, info) {
4436
0
        if (info->hooks) {
4437
0
          for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) {
4438
0
            if (info->hooks[i]) {
4439
0
              op_array = &info->hooks[i]->op_array;
4440
0
              ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION);
4441
0
              if (!(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
4442
0
                preload_remove_declares(op_array);
4443
0
              }
4444
0
            }
4445
0
          }
4446
0
        }
4447
0
      } ZEND_HASH_FOREACH_END();
4448
0
    }
4449
0
  } ZEND_HASH_FOREACH_END();
4450
0
}
4451
4452
static zend_string *preload_resolve_path(zend_string *filename)
4453
0
{
4454
0
  if (php_is_stream_path(ZSTR_VAL(filename))) {
4455
0
    return NULL;
4456
0
  }
4457
0
  return zend_resolve_path(filename);
4458
0
}
4459
4460
static void preload_remove_empty_includes(void)
4461
0
{
4462
0
  zend_persistent_script *script;
4463
0
  bool changed;
4464
4465
  /* mark all as empty */
4466
0
  ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4467
0
    script->empty = true;
4468
0
  } ZEND_HASH_FOREACH_END();
4469
4470
  /* find non empty scripts */
4471
0
  do {
4472
0
    changed = false;
4473
0
    ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4474
0
      if (script->empty) {
4475
0
        bool empty = true;
4476
0
        zend_op *opline = script->script.main_op_array.opcodes;
4477
0
        const zend_op *end = opline + script->script.main_op_array.last;
4478
4479
0
        while (opline < end) {
4480
0
          if (opline->opcode == ZEND_INCLUDE_OR_EVAL &&
4481
0
              opline->extended_value != ZEND_EVAL &&
4482
0
              opline->op1_type == IS_CONST &&
4483
0
              Z_TYPE_P(RT_CONSTANT(opline, opline->op1)) == IS_STRING &&
4484
0
              opline->result_type == IS_UNUSED) {
4485
4486
0
            zend_string *resolved_path = preload_resolve_path(Z_STR_P(RT_CONSTANT(opline, opline->op1)));
4487
4488
0
            if (resolved_path) {
4489
0
              zend_persistent_script *incl = zend_hash_find_ptr(preload_scripts, resolved_path);
4490
0
              zend_string_release(resolved_path);
4491
0
              if (!incl || !incl->empty) {
4492
0
                empty = false;
4493
0
                break;
4494
0
              }
4495
0
            } else {
4496
0
              empty = false;
4497
0
              break;
4498
0
            }
4499
0
          } else if (opline->opcode != ZEND_NOP &&
4500
0
                     opline->opcode != ZEND_RETURN &&
4501
0
                     opline->opcode != ZEND_HANDLE_EXCEPTION) {
4502
0
            empty = false;
4503
0
            break;
4504
0
          }
4505
0
          opline++;
4506
0
        }
4507
0
        if (!empty) {
4508
0
          script->empty = false;
4509
0
          changed = true;
4510
0
        }
4511
0
      }
4512
0
    } ZEND_HASH_FOREACH_END();
4513
0
  } while (changed);
4514
4515
  /* remove empty includes */
4516
0
  ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4517
0
    zend_op *opline = script->script.main_op_array.opcodes;
4518
0
    const zend_op *end = opline + script->script.main_op_array.last;
4519
4520
0
    while (opline < end) {
4521
0
      if (opline->opcode == ZEND_INCLUDE_OR_EVAL &&
4522
0
          opline->extended_value != ZEND_EVAL &&
4523
0
          opline->op1_type == IS_CONST &&
4524
0
          Z_TYPE_P(RT_CONSTANT(opline, opline->op1)) == IS_STRING) {
4525
4526
0
        zend_string *resolved_path = preload_resolve_path(Z_STR_P(RT_CONSTANT(opline, opline->op1)));
4527
4528
0
        if (resolved_path) {
4529
0
          const zend_persistent_script *incl = zend_hash_find_ptr(preload_scripts, resolved_path);
4530
0
          if (incl && incl->empty && opline->result_type == IS_UNUSED) {
4531
0
            MAKE_NOP(opline);
4532
0
          } else {
4533
0
            if (!IS_ABSOLUTE_PATH(Z_STRVAL_P(RT_CONSTANT(opline, opline->op1)), Z_STRLEN_P(RT_CONSTANT(opline, opline->op1)))) {
4534
              /* replace relative patch with absolute one */
4535
0
              zend_string_release(Z_STR_P(RT_CONSTANT(opline, opline->op1)));
4536
0
              ZVAL_STR_COPY(RT_CONSTANT(opline, opline->op1), resolved_path);
4537
0
            }
4538
0
          }
4539
0
          zend_string_release(resolved_path);
4540
0
        }
4541
0
      }
4542
0
      opline++;
4543
0
    }
4544
0
  } ZEND_HASH_FOREACH_END();
4545
0
}
4546
4547
0
static void preload_register_trait_methods(const zend_class_entry *ce) {
4548
0
  zend_op_array *op_array;
4549
4550
0
  ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
4551
0
    if (!(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
4552
0
      ZEND_ASSERT(op_array->refcount && "Must have refcount pointer");
4553
0
      zend_shared_alloc_register_xlat_entry(op_array->refcount, op_array);
4554
0
    }
4555
0
  } ZEND_HASH_FOREACH_END();
4556
4557
0
  if (ce->num_hooked_props > 0) {
4558
0
    ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, zend_property_info *info) {
4559
0
      if (info->hooks) {
4560
0
        for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) {
4561
0
          if (info->hooks[i]) {
4562
0
            op_array = &info->hooks[i]->op_array;
4563
0
            if (!(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
4564
0
              ZEND_ASSERT(op_array->refcount && "Must have refcount pointer");
4565
0
              zend_shared_alloc_register_xlat_entry(op_array->refcount, op_array);
4566
0
            }
4567
0
          }
4568
0
        }
4569
0
      }
4570
0
    } ZEND_HASH_FOREACH_END();
4571
0
  }
4572
0
}
4573
4574
static void preload_fix_trait_op_array(zend_op_array *op_array)
4575
0
{
4576
0
  if (!(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
4577
0
    return;
4578
0
  }
4579
4580
0
  const zend_op_array *orig_op_array = zend_shared_alloc_get_xlat_entry(op_array->refcount);
4581
0
  ZEND_ASSERT(orig_op_array && "Must be in xlat table");
4582
4583
0
  zend_string *function_name = op_array->function_name;
4584
0
  zend_class_entry *scope = op_array->scope;
4585
0
  uint32_t fn_flags = op_array->fn_flags;
4586
0
  uint32_t fn_flags2 = op_array->fn_flags2;
4587
0
  zend_function *prototype = op_array->prototype;
4588
0
  HashTable *ht = op_array->static_variables;
4589
0
  const zend_property_info *prop_info = op_array->prop_info;
4590
0
  *op_array = *orig_op_array;
4591
0
  op_array->function_name = function_name;
4592
0
  op_array->scope = scope;
4593
0
  op_array->fn_flags = fn_flags;
4594
0
  op_array->fn_flags2 = fn_flags2;
4595
0
  op_array->prototype = prototype;
4596
0
  op_array->static_variables = ht;
4597
0
  op_array->prop_info = prop_info;
4598
0
}
4599
4600
static void preload_fix_trait_methods(const zend_class_entry *ce)
4601
0
{
4602
0
  zend_op_array *op_array;
4603
4604
0
  ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
4605
0
    preload_fix_trait_op_array(op_array);
4606
0
  } ZEND_HASH_FOREACH_END();
4607
4608
0
  if (ce->num_hooked_props > 0) {
4609
0
    zend_property_info *info;
4610
0
    ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, info) {
4611
0
      if (info->hooks) {
4612
0
        for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) {
4613
0
          if (info->hooks[i]) {
4614
0
            op_array = &info->hooks[i]->op_array;
4615
0
            preload_fix_trait_op_array(op_array);
4616
0
          }
4617
0
        }
4618
0
      }
4619
0
    } ZEND_HASH_FOREACH_END();
4620
0
  }
4621
0
}
4622
4623
static void preload_optimize(zend_persistent_script *script)
4624
0
{
4625
0
  const zend_class_entry *ce;
4626
0
  zend_persistent_script *tmp_script;
4627
4628
0
  zend_shared_alloc_init_xlat_table();
4629
4630
0
  ZEND_HASH_MAP_FOREACH_PTR(&script->script.class_table, ce) {
4631
0
    if (ce->ce_flags & ZEND_ACC_TRAIT) {
4632
0
      preload_register_trait_methods(ce);
4633
0
    }
4634
0
  } ZEND_HASH_FOREACH_END();
4635
4636
0
  ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, tmp_script) {
4637
0
    ZEND_HASH_MAP_FOREACH_PTR(&tmp_script->script.class_table, ce) {
4638
0
      if (ce->ce_flags & ZEND_ACC_TRAIT) {
4639
0
        preload_register_trait_methods(ce);
4640
0
      }
4641
0
    } ZEND_HASH_FOREACH_END();
4642
0
  } ZEND_HASH_FOREACH_END();
4643
4644
0
  zend_optimize_script(&script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level);
4645
0
  zend_accel_finalize_delayed_early_binding_list(script);
4646
4647
0
  ZEND_HASH_MAP_FOREACH_PTR(&script->script.class_table, ce) {
4648
0
    preload_fix_trait_methods(ce);
4649
0
  } ZEND_HASH_FOREACH_END();
4650
4651
0
  ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4652
0
    ZEND_HASH_MAP_FOREACH_PTR(&script->script.class_table, ce) {
4653
0
      preload_fix_trait_methods(ce);
4654
0
    } ZEND_HASH_FOREACH_END();
4655
0
  } ZEND_HASH_FOREACH_END();
4656
4657
0
  zend_shared_alloc_destroy_xlat_table();
4658
4659
0
  ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4660
0
    zend_optimize_script(&script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level);
4661
0
    zend_accel_finalize_delayed_early_binding_list(script);
4662
0
  } ZEND_HASH_FOREACH_END();
4663
0
}
4664
4665
static zend_persistent_script* preload_script_in_shared_memory(zend_persistent_script *new_persistent_script)
4666
0
{
4667
0
  zend_accel_hash_entry *bucket;
4668
0
  uint32_t memory_used;
4669
0
  uint32_t checkpoint;
4670
4671
0
  if (zend_accel_hash_is_full(&ZCSG(hash))) {
4672
0
    zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Not enough entries in hash table for preloading. Consider increasing the value for the opcache.max_accelerated_files directive in php.ini.");
4673
0
    return NULL;
4674
0
  }
4675
4676
0
  checkpoint = zend_shared_alloc_checkpoint_xlat_table();
4677
4678
  /* Calculate the required memory size */
4679
0
  memory_used = zend_accel_script_persist_calc(new_persistent_script, 1);
4680
4681
  /* Allocate shared memory */
4682
0
  ZCG(mem) = zend_shared_alloc_aligned(memory_used);
4683
0
  if (!ZCG(mem)) {
4684
0
    zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Not enough shared memory for preloading. Consider increasing the value for the opcache.memory_consumption directive in php.ini.");
4685
0
    return NULL;
4686
0
  }
4687
4688
0
  bzero_aligned(ZCG(mem), memory_used);
4689
4690
0
  zend_shared_alloc_restore_xlat_table(checkpoint);
4691
4692
  /* Copy into shared memory */
4693
0
  new_persistent_script = zend_accel_script_persist(new_persistent_script, 1);
4694
4695
0
  new_persistent_script->is_phar = is_phar_file(new_persistent_script->script.filename);
4696
4697
  /* Consistency check */
4698
0
  if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
4699
0
    zend_accel_error(
4700
0
      ((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
4701
0
      "Internal error: wrong size calculation: %s start=" ZEND_ADDR_FMT ", end=" ZEND_ADDR_FMT ", real=" ZEND_ADDR_FMT "\n",
4702
0
      ZSTR_VAL(new_persistent_script->script.filename),
4703
0
      (size_t)new_persistent_script->mem,
4704
0
      (size_t)((char *)new_persistent_script->mem + new_persistent_script->size),
4705
0
      (size_t)ZCG(mem));
4706
0
  }
4707
4708
  /* store script structure in the hash table */
4709
0
  bucket = zend_accel_hash_update(&ZCSG(hash), new_persistent_script->script.filename, 0, new_persistent_script);
4710
0
  if (bucket) {
4711
0
    zend_accel_error(ACCEL_LOG_INFO, "Cached script '%s'", ZSTR_VAL(new_persistent_script->script.filename));
4712
0
  }
4713
4714
0
  new_persistent_script->dynamic_members.memory_consumption = ZEND_ALIGNED_SIZE(new_persistent_script->size);
4715
4716
0
  return new_persistent_script;
4717
0
}
4718
4719
static void preload_load(size_t orig_map_ptr_static_last)
4720
0
{
4721
  /* Load into process tables */
4722
0
  const zend_script *script = &ZCSG(preload_script)->script;
4723
0
  if (zend_hash_num_elements(&script->function_table)) {
4724
0
    Bucket *p = script->function_table.arData;
4725
0
    const Bucket *end = p + script->function_table.nNumUsed;
4726
4727
0
    zend_hash_extend(CG(function_table),
4728
0
      CG(function_table)->nNumUsed + script->function_table.nNumUsed, 0);
4729
0
    for (; p != end; p++) {
4730
0
      _zend_hash_append_ptr_ex(CG(function_table), p->key, Z_PTR(p->val), 1);
4731
0
    }
4732
0
  }
4733
4734
0
  if (zend_hash_num_elements(&script->class_table)) {
4735
0
    Bucket *p = script->class_table.arData;
4736
0
    const Bucket *end = p + script->class_table.nNumUsed;
4737
4738
0
    zend_hash_extend(CG(class_table),
4739
0
      CG(class_table)->nNumUsed + script->class_table.nNumUsed, 0);
4740
0
    for (; p != end; p++) {
4741
0
      _zend_hash_append_ex(CG(class_table), p->key, &p->val, 1);
4742
0
    }
4743
0
  }
4744
4745
0
  size_t old_map_ptr_last = CG(map_ptr_last);
4746
0
  if (zend_map_ptr_static_last != ZCSG(map_ptr_static_last) || old_map_ptr_last != ZCSG(map_ptr_last)) {
4747
0
    CG(map_ptr_last) = ZCSG(map_ptr_last);
4748
0
    CG(map_ptr_size) = ZEND_MM_ALIGNED_SIZE_EX(ZCSG(map_ptr_last) + 1, ZEND_MAP_PTR_CHUNK_SIZE);
4749
0
    zend_map_ptr_static_last = ZCSG(map_ptr_static_last);
4750
4751
    /* Grow map_ptr table as needed, but allocate once for static + regular map_ptrs */
4752
0
    size_t new_static_size = ZEND_MM_ALIGNED_SIZE_EX(zend_map_ptr_static_last, ZEND_MAP_PTR_CHUNK_SIZE);
4753
0
    if (zend_map_ptr_static_size != new_static_size) {
4754
0
      void *new_base = pemalloc((new_static_size + CG(map_ptr_size)) * sizeof(void *), 1);
4755
0
      if (CG(map_ptr_real_base)) {
4756
0
        memcpy((void **) new_base + new_static_size - zend_map_ptr_static_size, CG(map_ptr_real_base), (old_map_ptr_last + zend_map_ptr_static_size) * sizeof(void *));
4757
0
        pefree(CG(map_ptr_real_base), 1);
4758
0
      }
4759
0
      CG(map_ptr_real_base) = new_base;
4760
0
      zend_map_ptr_static_size = new_static_size;
4761
0
    } else {
4762
0
      CG(map_ptr_real_base) = perealloc(CG(map_ptr_real_base), (zend_map_ptr_static_size + CG(map_ptr_size)) * sizeof(void *), 1);
4763
0
    }
4764
4765
0
    memset((void **) CG(map_ptr_real_base) + zend_map_ptr_static_size + old_map_ptr_last, 0, (CG(map_ptr_last) - old_map_ptr_last) * sizeof(void *));
4766
0
    CG(map_ptr_base) = ZEND_MAP_PTR_BIASED_BASE(CG(map_ptr_real_base));
4767
0
  }
4768
4769
0
  if (orig_map_ptr_static_last != zend_map_ptr_static_last) {
4770
    /* preloaded static entries currently are all runtime cache pointers, just assign them as such */
4771
0
    size_t runtime_cache_size = zend_internal_run_time_cache_reserved_size();
4772
0
    ZCG(preloaded_internal_run_time_cache_size) = (zend_map_ptr_static_last - orig_map_ptr_static_last) * runtime_cache_size;
4773
0
    char *cache = pemalloc(ZCG(preloaded_internal_run_time_cache_size), 1);
4774
0
    ZCG(preloaded_internal_run_time_cache) = cache;
4775
4776
0
    for (size_t cur_static_map_ptr = orig_map_ptr_static_last; cur_static_map_ptr < zend_map_ptr_static_last; ++cur_static_map_ptr) {
4777
0
            *ZEND_MAP_PTR_STATIC_NUM_TO_PTR(cur_static_map_ptr) = cache;
4778
0
      cache += runtime_cache_size;
4779
0
    }
4780
0
  }
4781
0
}
4782
4783
#if HAVE_JIT
4784
static void zend_accel_clear_call_graph_ptrs(const zend_op_array *op_array)
4785
0
{
4786
0
  ZEND_ASSERT(ZEND_USER_CODE(op_array->type));
4787
0
  zend_func_info *info = ZEND_FUNC_INFO(op_array);
4788
0
  if (info) {
4789
0
    info->caller_info = NULL;
4790
0
    info->callee_info = NULL;
4791
0
  }
4792
0
}
4793
4794
static void accel_reset_arena_info(const zend_persistent_script *script)
4795
0
{
4796
0
  const zend_op_array *op_array;
4797
0
  zend_class_entry *ce;
4798
4799
0
  zend_accel_clear_call_graph_ptrs(&script->script.main_op_array);
4800
0
  ZEND_HASH_MAP_FOREACH_PTR(&script->script.function_table, op_array) {
4801
0
    zend_accel_clear_call_graph_ptrs(op_array);
4802
0
  } ZEND_HASH_FOREACH_END();
4803
0
  ZEND_HASH_MAP_FOREACH_PTR(&script->script.class_table, ce) {
4804
0
    ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
4805
0
      if (op_array->scope == ce
4806
0
       && op_array->type == ZEND_USER_FUNCTION
4807
0
       && !(op_array->fn_flags & ZEND_ACC_ABSTRACT)
4808
0
       && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
4809
0
        zend_accel_clear_call_graph_ptrs(op_array);
4810
0
      }
4811
0
    } ZEND_HASH_FOREACH_END();
4812
0
  } ZEND_HASH_FOREACH_END();
4813
0
}
4814
#endif
4815
4816
static zend_result accel_preload(const char *config, bool in_child)
4817
0
{
4818
0
  zend_file_handle file_handle;
4819
0
  zend_result ret;
4820
0
  char *orig_open_basedir;
4821
0
  size_t orig_map_ptr_last, orig_map_ptr_static_last;
4822
0
  uint32_t orig_compiler_options;
4823
4824
0
  ZCG(enabled) = false;
4825
0
  ZCG(accelerator_enabled) = false;
4826
0
  orig_open_basedir = PG(open_basedir);
4827
0
  PG(open_basedir) = NULL;
4828
0
  preload_orig_compile_file = accelerator_orig_compile_file;
4829
0
  accelerator_orig_compile_file = preload_compile_file;
4830
4831
0
  orig_map_ptr_last = CG(map_ptr_last);
4832
0
  orig_map_ptr_static_last = zend_map_ptr_static_last;
4833
4834
  /* Compile and execute preloading script */
4835
0
  zend_stream_init_filename(&file_handle, (char *) config);
4836
4837
0
  preload_scripts = emalloc(sizeof(HashTable));
4838
0
  zend_hash_init(preload_scripts, 0, NULL, NULL, 0);
4839
4840
0
  orig_compiler_options = CG(compiler_options);
4841
0
  if (in_child) {
4842
0
    CG(compiler_options) |= ZEND_COMPILE_PRELOAD_IN_CHILD;
4843
0
  }
4844
0
  CG(compiler_options) |= ZEND_COMPILE_PRELOAD;
4845
0
  CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
4846
0
  CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING;
4847
0
  CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION;
4848
0
  CG(compiler_options) |= ZEND_COMPILE_IGNORE_OTHER_FILES;
4849
0
  CG(skip_shebang) = true;
4850
4851
0
  zend_try {
4852
0
    zend_op_array *op_array;
4853
4854
0
    ret = SUCCESS;
4855
0
    op_array = zend_compile_file(&file_handle, ZEND_REQUIRE);
4856
0
    if (file_handle.opened_path) {
4857
0
      zend_hash_add_empty_element(&EG(included_files), file_handle.opened_path);
4858
0
    }
4859
0
    zend_destroy_file_handle(&file_handle);
4860
0
    if (op_array) {
4861
0
      zend_execute(op_array, NULL);
4862
0
      if (UNEXPECTED(EG(exception))) {
4863
0
        if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) {
4864
0
          zend_user_exception_handler();
4865
0
        }
4866
0
        if (EG(exception)) {
4867
0
          ret = zend_exception_error(EG(exception), E_ERROR);
4868
0
          if (ret == FAILURE) {
4869
0
            CG(unclean_shutdown) = true;
4870
0
          }
4871
0
        }
4872
0
      }
4873
0
      destroy_op_array(op_array);
4874
0
      efree_size(op_array, sizeof(zend_op_array));
4875
0
    } else {
4876
0
      if (EG(exception)) {
4877
0
        zend_exception_error(EG(exception), E_ERROR);
4878
0
      }
4879
4880
0
      CG(unclean_shutdown) = true;
4881
0
      ret = FAILURE;
4882
0
    }
4883
0
  } zend_catch {
4884
0
    ret = FAILURE;
4885
0
  } zend_end_try();
4886
4887
0
  PG(open_basedir) = orig_open_basedir;
4888
0
  accelerator_orig_compile_file = preload_orig_compile_file;
4889
0
  ZCG(enabled) = true;
4890
4891
0
  zend_destroy_file_handle(&file_handle);
4892
4893
0
  if (ret == SUCCESS) {
4894
0
    zend_persistent_script *script;
4895
0
    int ping_auto_globals_mask;
4896
0
    int i;
4897
4898
0
    if (PG(auto_globals_jit)) {
4899
0
      ping_auto_globals_mask = zend_accel_get_auto_globals();
4900
0
    } else {
4901
0
      ping_auto_globals_mask = 0;
4902
0
    }
4903
4904
0
    if (EG(zend_constants)) {
4905
      /* Remember __COMPILER_HALT_OFFSET__(s). Do this early,
4906
       * as zend_shutdown_executor_values() destroys constants. */
4907
0
      ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4908
0
        zend_execute_data *orig_execute_data = EG(current_execute_data);
4909
0
        zend_execute_data fake_execute_data;
4910
0
        zval *offset;
4911
4912
0
        memset(&fake_execute_data, 0, sizeof(fake_execute_data));
4913
0
        fake_execute_data.func = (zend_function*)&script->script.main_op_array;
4914
0
        EG(current_execute_data) = &fake_execute_data;
4915
0
        if ((offset = zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1)) != NULL) {
4916
0
          script->compiler_halt_offset = Z_LVAL_P(offset);
4917
0
        }
4918
0
        EG(current_execute_data) = orig_execute_data;
4919
0
      } ZEND_HASH_FOREACH_END();
4920
0
    }
4921
4922
    /* Cleanup executor */
4923
0
    EG(flags) |= EG_FLAGS_IN_SHUTDOWN;
4924
4925
0
    php_call_shutdown_functions();
4926
0
    zend_call_destructors();
4927
0
    php_output_end_all();
4928
0
    php_free_shutdown_functions();
4929
4930
    /* Release stored values to avoid dangling pointers */
4931
0
    zend_shutdown_executor_values(/* fast_shutdown */ false);
4932
4933
    /* On ZTS we execute `executor_globals_ctor` which reset the freelist and p5s pointers, while on NTS we don't.
4934
     * We have to clean up the memory before the actual request takes place to avoid a memory leak. */
4935
#ifdef ZTS
4936
    zend_shutdown_strtod();
4937
#endif
4938
4939
    /* We don't want to preload constants.
4940
     * Check that  zend_shutdown_executor_values() also destroys constants. */
4941
0
    ZEND_ASSERT(zend_hash_num_elements(EG(zend_constants)) == EG(persistent_constants_count));
4942
4943
0
    zend_hash_init(&EG(symbol_table), 0, NULL, ZVAL_PTR_DTOR, 0);
4944
4945
0
    CG(map_ptr_last) = orig_map_ptr_last;
4946
4947
0
    if (EG(full_tables_cleanup)) {
4948
0
      zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading is not compatible with dl() function.");
4949
0
      ret = FAILURE;
4950
0
      goto finish;
4951
0
    }
4952
4953
    /* Inheritance errors may be thrown during linking */
4954
0
    zend_try {
4955
0
      preload_link();
4956
0
    } zend_catch {
4957
0
      CG(map_ptr_last) = orig_map_ptr_last;
4958
0
      ret = FAILURE;
4959
0
      goto finish;
4960
0
    } zend_end_try();
4961
4962
0
    preload_remove_empty_includes();
4963
4964
0
    script = create_persistent_script();
4965
0
    script->ping_auto_globals_mask = ping_auto_globals_mask;
4966
4967
    /* Store all functions and classes in a single pseudo-file */
4968
0
    CG(compiled_filename) = ZSTR_INIT_LITERAL("$PRELOAD$", 0);
4969
#if ZEND_USE_ABS_CONST_ADDR
4970
    init_op_array(&script->script.main_op_array, ZEND_USER_FUNCTION, 1);
4971
#else
4972
0
    init_op_array(&script->script.main_op_array, ZEND_USER_FUNCTION, 2);
4973
0
#endif
4974
0
    script->script.main_op_array.fn_flags |= ZEND_ACC_DONE_PASS_TWO;
4975
0
    script->script.main_op_array.last = 1;
4976
0
    script->script.main_op_array.last_literal = 1;
4977
0
    script->script.main_op_array.T = ZEND_OBSERVER_ENABLED;
4978
#if ZEND_USE_ABS_CONST_ADDR
4979
    script->script.main_op_array.literals = (zval*)emalloc(sizeof(zval));
4980
#else
4981
0
    script->script.main_op_array.literals = (zval*)(script->script.main_op_array.opcodes + 1);
4982
0
#endif
4983
0
    ZVAL_NULL(script->script.main_op_array.literals);
4984
0
    memset(script->script.main_op_array.opcodes, 0, sizeof(zend_op));
4985
0
    script->script.main_op_array.opcodes[0].opcode = ZEND_RETURN;
4986
0
    script->script.main_op_array.opcodes[0].op1_type = IS_CONST;
4987
0
    script->script.main_op_array.opcodes[0].op1.constant = 0;
4988
0
    ZEND_PASS_TWO_UPDATE_CONSTANT(&script->script.main_op_array, script->script.main_op_array.opcodes, script->script.main_op_array.opcodes[0].op1);
4989
0
    zend_vm_set_opcode_handler(script->script.main_op_array.opcodes);
4990
4991
0
    script->script.filename = CG(compiled_filename);
4992
0
    CG(compiled_filename) = NULL;
4993
4994
0
    preload_move_user_functions(CG(function_table), &script->script.function_table);
4995
0
    preload_move_user_classes(CG(class_table), &script->script.class_table);
4996
4997
0
    zend_hash_sort_ex(&script->script.class_table, preload_sort_classes, NULL, 0);
4998
4999
0
    preload_optimize(script);
5000
5001
0
    zend_shared_alloc_init_xlat_table();
5002
5003
0
    HANDLE_BLOCK_INTERRUPTIONS();
5004
0
    SHM_UNPROTECT();
5005
5006
0
    ZCSG(preload_script) = preload_script_in_shared_memory(script);
5007
5008
0
    SHM_PROTECT();
5009
0
    HANDLE_UNBLOCK_INTERRUPTIONS();
5010
5011
0
    preload_load(orig_map_ptr_static_last);
5012
5013
    /* Update persistent counts, as shutdown will discard anything past
5014
     * that, and these tables are aliases to global ones at this point. */
5015
0
    EG(persistent_functions_count) = EG(function_table)->nNumUsed;
5016
0
    EG(persistent_classes_count)   = EG(class_table)->nNumUsed;
5017
0
    EG(persistent_constants_count) = EG(zend_constants)->nNumUsed;
5018
5019
    /* Store individual scripts with unlinked classes */
5020
0
    HANDLE_BLOCK_INTERRUPTIONS();
5021
0
    SHM_UNPROTECT();
5022
5023
0
    i = 0;
5024
0
    ZCSG(saved_scripts) = zend_shared_alloc((zend_hash_num_elements(preload_scripts) + 1) * sizeof(void*));
5025
0
    ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
5026
0
      if (zend_hash_num_elements(&script->script.class_table) > 1) {
5027
0
        zend_hash_sort_ex(&script->script.class_table, preload_sort_classes, NULL, 0);
5028
0
      }
5029
0
      ZCSG(saved_scripts)[i++] = preload_script_in_shared_memory(script);
5030
0
    } ZEND_HASH_FOREACH_END();
5031
0
    ZCSG(saved_scripts)[i] = NULL;
5032
5033
0
#if HAVE_JIT
5034
    /* During persisting, the JIT may trigger and fill in the call graph.
5035
     * The call graph info is allocated on the arena which will be gone after preloading.
5036
     * To prevent invalid accesses during normal requests, the arena data should be cleared.
5037
     * This has to be done after all scripts have been persisted because shared op arrays between
5038
     * scripts can change the call graph. */
5039
0
    accel_reset_arena_info(ZCSG(preload_script));
5040
0
    for (zend_persistent_script **scripts = ZCSG(saved_scripts); *scripts; scripts++) {
5041
0
      accel_reset_arena_info(*scripts);
5042
0
    }
5043
0
#endif
5044
5045
0
    zend_shared_alloc_save_state();
5046
0
    accel_interned_strings_save_state();
5047
5048
0
    SHM_PROTECT();
5049
0
    HANDLE_UNBLOCK_INTERRUPTIONS();
5050
5051
0
    zend_shared_alloc_destroy_xlat_table();
5052
0
  } else {
5053
0
    CG(map_ptr_last) = orig_map_ptr_last;
5054
0
  }
5055
5056
0
finish:
5057
0
  CG(compiler_options) = orig_compiler_options;
5058
0
  zend_hash_destroy(preload_scripts);
5059
0
  efree(preload_scripts);
5060
0
  preload_scripts = NULL;
5061
5062
0
  return ret;
5063
0
}
5064
5065
static size_t preload_ub_write(const char *str, size_t str_length)
5066
0
{
5067
0
  return fwrite(str, 1, str_length, stdout);
5068
0
}
5069
5070
static void preload_flush(void *server_context)
5071
0
{
5072
0
  fflush(stdout);
5073
0
}
5074
5075
static int preload_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s)
5076
0
{
5077
0
  return 0;
5078
0
}
5079
5080
static int preload_send_headers(sapi_headers_struct *sapi_headers)
5081
0
{
5082
0
  return SAPI_HEADER_SENT_SUCCESSFULLY;
5083
0
}
5084
5085
static void preload_send_header(sapi_header_struct *sapi_header, void *server_context)
5086
0
{
5087
0
}
5088
5089
static zend_result accel_finish_startup_preload(bool in_child)
5090
0
{
5091
0
  zend_result ret = SUCCESS;
5092
0
  int orig_error_reporting;
5093
5094
0
  int (*orig_activate)(void) = sapi_module.activate;
5095
0
  int (*orig_deactivate)(void) = sapi_module.deactivate;
5096
0
  void (*orig_register_server_variables)(zval *track_vars_array) = sapi_module.register_server_variables;
5097
0
  int (*orig_header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers) = sapi_module.header_handler;
5098
0
  int (*orig_send_headers)(sapi_headers_struct *sapi_headers) = sapi_module.send_headers;
5099
0
  void (*orig_send_header)(sapi_header_struct *sapi_header, void *server_context)= sapi_module.send_header;
5100
0
  char *(*orig_getenv)(const char *name, size_t name_len) = sapi_module.getenv;
5101
0
  size_t (*orig_ub_write)(const char *str, size_t str_length) = sapi_module.ub_write;
5102
0
  void (*orig_flush)(void *server_context) = sapi_module.flush;
5103
0
#ifdef ZEND_SIGNALS
5104
0
  bool old_reset_signals = SIGG(reset);
5105
0
#endif
5106
5107
0
  ZCG(preloading) = true;
5108
5109
0
  sapi_module.activate = NULL;
5110
0
  sapi_module.deactivate = NULL;
5111
0
  sapi_module.register_server_variables = NULL;
5112
0
  sapi_module.header_handler = preload_header_handler;
5113
0
  sapi_module.send_headers = preload_send_headers;
5114
0
  sapi_module.send_header = preload_send_header;
5115
0
  sapi_module.getenv = NULL;
5116
0
  sapi_module.ub_write = preload_ub_write;
5117
0
  sapi_module.flush = preload_flush;
5118
5119
0
  zend_interned_strings_switch_storage(1);
5120
5121
0
#ifdef ZEND_SIGNALS
5122
0
  SIGG(reset) = false;
5123
0
#endif
5124
5125
0
  orig_error_reporting = EG(error_reporting);
5126
0
  EG(error_reporting) = 0;
5127
5128
0
  const zend_result rc = php_request_startup();
5129
5130
0
  EG(error_reporting) = orig_error_reporting;
5131
5132
0
  if (rc == SUCCESS) {
5133
0
    bool orig_report_memleaks;
5134
5135
    /* don't send headers */
5136
0
    SG(headers_sent) = true;
5137
0
    SG(request_info).no_headers = true;
5138
0
    php_output_set_status(0);
5139
5140
0
    ZCG(auto_globals_mask) = 0;
5141
0
    ZCG(request_time) = (time_t)sapi_get_request_time();
5142
0
    ZCG(cache_opline) = NULL;
5143
0
    ZCG(cache_persistent_script) = NULL;
5144
0
    ZCG(include_path_key_len) = 0;
5145
0
    ZCG(include_path_check) = true;
5146
5147
0
    ZCG(cwd) = NULL;
5148
0
    ZCG(cwd_key_len) = 0;
5149
0
    ZCG(cwd_check) = true;
5150
5151
0
    if (accel_preload(ZCG(accel_directives).preload, in_child) != SUCCESS) {
5152
0
      ret = FAILURE;
5153
0
    }
5154
0
    preload_flush(NULL);
5155
5156
0
    orig_report_memleaks = PG(report_memleaks);
5157
0
    PG(report_memleaks) = false;
5158
0
#ifdef ZEND_SIGNALS
5159
    /* We may not have registered signal handlers due to SIGG(reset)=0, so
5160
     * also disable the check that they are registered. */
5161
0
    SIGG(check) = false;
5162
0
#endif
5163
0
    php_request_shutdown(NULL); /* calls zend_shared_alloc_unlock(); */
5164
0
    EG(class_table) = NULL;
5165
0
    EG(function_table) = NULL;
5166
0
    PG(report_memleaks) = orig_report_memleaks;
5167
#ifdef ZTS
5168
    /* Reset the virtual CWD state back to the original state created by virtual_cwd_startup().
5169
     * This is necessary because the normal startup code assumes the CWD state is active. */
5170
    virtual_cwd_activate();
5171
#endif
5172
0
  } else {
5173
0
    zend_shared_alloc_unlock();
5174
0
    ret = FAILURE;
5175
0
  }
5176
0
#ifdef ZEND_SIGNALS
5177
0
  SIGG(reset) = old_reset_signals;
5178
0
#endif
5179
5180
0
  sapi_module.activate = orig_activate;
5181
0
  sapi_module.deactivate = orig_deactivate;
5182
0
  sapi_module.register_server_variables = orig_register_server_variables;
5183
0
  sapi_module.header_handler = orig_header_handler;
5184
0
  sapi_module.send_headers = orig_send_headers;
5185
0
  sapi_module.send_header = orig_send_header;
5186
0
  sapi_module.getenv = orig_getenv;
5187
0
  sapi_module.ub_write = orig_ub_write;
5188
0
  sapi_module.flush = orig_flush;
5189
5190
0
  ZCG(preloading) = false;
5191
5192
0
  sapi_activate();
5193
5194
0
  return ret;
5195
0
}
5196
5197
static zend_result accel_finish_startup_preload_subprocess(pid_t *pid)
5198
0
{
5199
0
  uid_t euid = geteuid();
5200
0
  if (euid != 0) {
5201
0
    if (ZCG(accel_directives).preload_user
5202
0
     && *ZCG(accel_directives).preload_user) {
5203
0
      zend_accel_error(ACCEL_LOG_WARNING, "\"opcache.preload_user\" is ignored because the current user is not \"root\"");
5204
0
    }
5205
5206
0
    *pid = -1;
5207
0
    return SUCCESS;
5208
0
  }
5209
5210
0
  if (!ZCG(accel_directives).preload_user
5211
0
   || !*ZCG(accel_directives).preload_user) {
5212
5213
0
    bool sapi_requires_preload_user = !(strcmp(sapi_module.name, "cli") == 0
5214
0
      || strcmp(sapi_module.name, "phpdbg") == 0);
5215
5216
0
    if (!sapi_requires_preload_user) {
5217
0
      *pid = -1;
5218
0
      return SUCCESS;
5219
0
    }
5220
5221
0
    zend_shared_alloc_unlock();
5222
0
    zend_accel_error_noreturn(ACCEL_LOG_FATAL, "\"opcache.preload\" requires \"opcache.preload_user\" when running under uid 0");
5223
0
    return FAILURE;
5224
0
  }
5225
5226
0
  struct passwd *pw = getpwnam(ZCG(accel_directives).preload_user);
5227
0
  if (pw == NULL) {
5228
0
    zend_shared_alloc_unlock();
5229
0
    zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to getpwnam(\"%s\")", ZCG(accel_directives).preload_user);
5230
0
    return FAILURE;
5231
0
  }
5232
5233
0
  if (pw->pw_uid == euid) {
5234
0
    *pid = -1;
5235
0
    return SUCCESS;
5236
0
  }
5237
5238
0
  *pid = fork();
5239
0
  if (*pid == -1) {
5240
0
    zend_shared_alloc_unlock();
5241
0
    zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to fork()");
5242
0
    return FAILURE;
5243
0
  }
5244
5245
0
  if (*pid == 0) { /* children */
5246
0
    if (setgid(pw->pw_gid) < 0) {
5247
0
      zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setgid(%d)", pw->pw_gid);
5248
0
      exit(1);
5249
0
    }
5250
0
    if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
5251
0
      zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to initgroups(\"%s\", %d)", pw->pw_name, pw->pw_uid);
5252
0
      exit(1);
5253
0
    }
5254
0
    if (setuid(pw->pw_uid) < 0) {
5255
0
      zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setuid(%d)", pw->pw_uid);
5256
0
      exit(1);
5257
0
    }
5258
0
    php_child_init();
5259
0
  }
5260
5261
0
  return SUCCESS;
5262
0
}
5263
#endif /* ZEND_WIN32 */
5264
5265
static zend_result accel_finish_startup(void)
5266
16
{
5267
16
  if (!ZCG(enabled) || !accel_startup_ok) {
5268
0
    return SUCCESS;
5269
0
  }
5270
5271
16
  if (!(ZCG(accel_directives).preload && *ZCG(accel_directives).preload)) {
5272
16
    return SUCCESS;
5273
16
  }
5274
5275
#ifdef ZEND_WIN32
5276
  zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Preloading is not supported on Windows");
5277
  return FAILURE;
5278
#else /* ZEND_WIN32 */
5279
5280
0
  if (UNEXPECTED(file_cache_only)) {
5281
0
    zend_accel_error(ACCEL_LOG_WARNING, "Preloading doesn't work in \"file_cache_only\" mode");
5282
0
    return SUCCESS;
5283
0
  }
5284
5285
  /* exclusive lock */
5286
0
  zend_shared_alloc_lock();
5287
5288
0
  if (ZCSG(preload_script)) {
5289
    /* Preloading was done in another process */
5290
0
    preload_load(zend_map_ptr_static_last);
5291
0
    zend_shared_alloc_unlock();
5292
0
    return SUCCESS;
5293
0
  }
5294
5295
5296
0
  pid_t pid;
5297
0
  if (accel_finish_startup_preload_subprocess(&pid) == FAILURE) {
5298
0
    zend_shared_alloc_unlock();
5299
0
    return FAILURE;
5300
0
  }
5301
5302
0
  if (pid == -1) { /* no subprocess was needed */
5303
    /* The called function unlocks the shared alloc lock */
5304
0
    return accel_finish_startup_preload(false);
5305
0
  } else if (pid == 0) { /* subprocess */
5306
0
    const zend_result ret = accel_finish_startup_preload(true);
5307
5308
0
    exit(ret == SUCCESS ? 0 : 1);
5309
0
  } else { /* parent */
5310
0
# ifdef HAVE_SIGPROCMASK
5311
    /* Interrupting the waitpid() call below with a signal would cause the
5312
     * process to exit. This is fine when the signal disposition is set to
5313
     * terminate the process, but not otherwise.
5314
     * When running the apache2handler, preloading is performed in the
5315
     * control process. SIGUSR1 and SIGHUP are used to tell the control
5316
     * process to restart children. Exiting when these signals are received
5317
     * would unexpectedly shutdown the server instead of restarting it.
5318
     * Block the USR1 and HUP signals from being delivered during the
5319
     * syscall when running the apache2handler SAPI, as these are not
5320
     * supposed to terminate the process. See GH-20051. */
5321
0
    bool is_apache2handler = strcmp(sapi_module.name, "apache2handler") == 0;
5322
0
    sigset_t set, oldset;
5323
0
    if (is_apache2handler) {
5324
0
      if (sigemptyset(&set)
5325
0
          || sigaddset(&set, SIGUSR1)
5326
0
          || sigaddset(&set, SIGHUP)) {
5327
0
        ZEND_UNREACHABLE();
5328
0
      }
5329
0
      if (sigprocmask(SIG_BLOCK, &set, &oldset)) {
5330
0
        ZEND_UNREACHABLE();
5331
0
      }
5332
0
    }
5333
0
# endif
5334
5335
0
    int status;
5336
0
    if (waitpid(pid, &status, 0) < 0) {
5337
0
      zend_shared_alloc_unlock();
5338
0
      zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to waitpid(%d)", pid);
5339
0
    }
5340
5341
0
# ifdef HAVE_SIGPROCMASK
5342
0
    if (is_apache2handler) {
5343
0
      if (sigprocmask(SIG_SETMASK, &oldset, NULL)) {
5344
0
        ZEND_UNREACHABLE();
5345
0
      }
5346
0
    }
5347
0
# endif
5348
5349
0
    if (ZCSG(preload_script)) {
5350
0
      preload_load(zend_map_ptr_static_last);
5351
0
    }
5352
5353
0
    zend_shared_alloc_unlock();
5354
5355
0
    if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
5356
0
      return SUCCESS;
5357
0
    } else {
5358
0
      return FAILURE;
5359
0
    }
5360
0
  }
5361
0
#endif /* ZEND_WIN32 */
5362
0
}
5363
5364
295k
static void accel_activate(void) {
5365
295k
  if (ZCG(preloaded_internal_run_time_cache)) {
5366
0
    memset(ZCG(preloaded_internal_run_time_cache), 0, ZCG(preloaded_internal_run_time_cache_size));
5367
0
  }
5368
295k
}
5369
5370
static zend_extension opcache_extension_entry = {
5371
  ACCELERATOR_PRODUCT_NAME,               /* name */
5372
  PHP_VERSION,              /* version */
5373
  "Zend by Perforce",         /* author */
5374
  "https://www.zend.com/",          /* URL */
5375
  "Copyright ©",           /* copyright */
5376
  accel_startup,                /* startup */
5377
  NULL,                 /* shutdown */
5378
  accel_activate,             /* per-script activation */
5379
#ifdef HAVE_JIT
5380
  accel_deactivate,                       /* per-script deactivation */
5381
#else
5382
  NULL,                 /* per-script deactivation */
5383
#endif
5384
  NULL,                 /* message handler */
5385
  NULL,                 /* op_array handler */
5386
  NULL,                 /* extended statement handler */
5387
  NULL,                 /* extended fcall begin handler */
5388
  NULL,                 /* extended fcall end handler */
5389
  NULL,                 /* op_array ctor */
5390
  NULL,                 /* op_array dtor */
5391
  STANDARD_ZEND_EXTENSION_PROPERTIES
5392
};