Coverage Report

Created: 2026-07-25 06:39

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
63.1k
{
164
63.1k
#if defined(__x86_64__)
165
63.1k
  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
63.1k
}
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.60k
{
207
1.60k
  return memcmp(filename, "file://", sizeof("file://") - 1) == 0 ||
208
1.60k
         memcmp(filename, "phar://", sizeof("phar://") - 1) == 0;
209
1.60k
}
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
35.2k
{
237
35.2k
  if (ZCG(cwd)) {
238
0
    return ZCG(cwd);
239
35.2k
  } else {
240
35.2k
    char cwd[MAXPATHLEN + 1];
241
242
35.2k
    if (!VCWD_GETCWD(cwd, MAXPATHLEN)) {
243
0
      return NULL;
244
0
    }
245
35.2k
    ZCG(cwd) = zend_string_init(cwd, strlen(cwd), 0);
246
35.2k
    ZCG(cwd_key_len) = 0;
247
35.2k
    ZCG(cwd_check) = true;
248
35.2k
    return ZCG(cwd);
249
35.2k
  }
250
35.2k
}
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
170
{
266
170
  int ret = orig_include_path_on_modify(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
267
268
170
  if (ret == SUCCESS) {
269
170
    ZCG(include_path) = new_value;
270
170
    ZCG(include_path_key_len) = 0;
271
170
    ZCG(include_path_check) = true;
272
170
  }
273
170
  return ret;
274
170
}
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
182k
{
346
#ifdef ZEND_WIN32
347
  SHM_UNPROTECT();
348
  INCREMENT(mem_usage);
349
  SHM_PROTECT();
350
#else
351
182k
  struct flock mem_usage_lock;
352
353
182k
  mem_usage_lock.l_type = F_RDLCK;
354
182k
  mem_usage_lock.l_whence = SEEK_SET;
355
182k
  mem_usage_lock.l_start = 1;
356
182k
  mem_usage_lock.l_len = 1;
357
358
182k
  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
182k
#endif
363
182k
  return SUCCESS;
364
182k
}
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
300k
{
392
#ifdef ZEND_WIN32
393
  accel_deactivate_sub();
394
#else
395
300k
  if (lock_file == -1) {
396
0
    return;
397
0
  }
398
399
300k
  struct flock mem_usage_unlock_all;
400
401
300k
  mem_usage_unlock_all.l_type = F_UNLCK;
402
300k
  mem_usage_unlock_all.l_whence = SEEK_SET;
403
300k
  mem_usage_unlock_all.l_start = 0;
404
300k
  mem_usage_unlock_all.l_len = 0;
405
406
300k
  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
300k
#endif
410
300k
}
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
3.51M
#define STRTAB_INVALID_POS 0
420
421
#define STRTAB_HASH_TO_SLOT(tab, h) \
422
53.8M
  ((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
8.14M
  ((zend_string*)((char*)(tab) + ((uintptr_t)(pos) * ZEND_STRING_TABLE_POS_ALIGNMENT)))
427
#define STRTAB_COLLISION(s) \
428
3.65M
  (*((zend_string_table_pos_t*)((char*)s - sizeof(zend_string_table_pos_t))))
429
#define STRTAB_STR_SIZE(s) \
430
136k
  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
136k
  ((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
56.1M
{
482
56.1M
  zend_ulong   h;
483
56.1M
  zend_string_table_pos_t pos;
484
56.1M
  zend_string *s;
485
486
56.1M
  if (IS_ACCEL_INTERNED(str)) {
487
    /* this is already an interned string */
488
3.02M
    return str;
489
3.02M
  }
490
491
53.1M
  if (!ZCG(counted)) {
492
34.8k
    if (!ZCG(accelerator_enabled) || accel_activate_add() == FAILURE) {
493
0
      return NULL;
494
0
    }
495
34.8k
    ZCG(counted) = true;
496
34.8k
  }
497
498
53.1M
  h = zend_string_hash_val(str);
499
500
  /* check for existing interned string */
501
53.1M
  pos = *STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), h);
502
53.1M
  if (EXPECTED(pos != STRTAB_INVALID_POS)) {
503
7.63M
    do {
504
7.63M
      s = STRTAB_POS_TO_STR(&ZCSG(interned_strings), pos);
505
7.63M
      if (EXPECTED(ZSTR_H(s) == h) && zend_string_equal_content(s, str)) {
506
4.24M
        return s;
507
4.24M
      }
508
3.38M
      pos = STRTAB_COLLISION(s);
509
3.38M
    } while (pos != STRTAB_INVALID_POS);
510
4.33M
  }
511
512
48.8M
  return NULL;
513
53.1M
}
514
515
zend_string* ZEND_FASTCALL accel_new_interned_string(zend_string *str)
516
559k
{
517
559k
  zend_ulong   h;
518
559k
  zend_string_table_pos_t pos, *hash_slot;
519
559k
  zend_string *s;
520
521
559k
  if (UNEXPECTED(file_cache_only)) {
522
0
    return str;
523
0
  }
524
525
559k
  if (IS_ACCEL_INTERNED(str)) {
526
    /* this is already an interned string */
527
6.41k
    return str;
528
6.41k
  }
529
530
552k
  h = zend_string_hash_val(str);
531
532
  /* check for existing interned string */
533
552k
  hash_slot = STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), h);
534
552k
  pos = *hash_slot;
535
552k
  if (EXPECTED(pos != STRTAB_INVALID_POS)) {
536
454k
    do {
537
454k
      s = STRTAB_POS_TO_STR(&ZCSG(interned_strings), pos);
538
454k
      if (EXPECTED(ZSTR_H(s) == h) && zend_string_equal_content(s, str)) {
539
338k
        goto finish;
540
338k
      }
541
116k
      pos = STRTAB_COLLISION(s);
542
116k
    } while (pos != STRTAB_INVALID_POS);
543
356k
  }
544
545
213k
  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
77.4k
    zend_accel_error(ACCEL_LOG_WARNING, "Interned string buffer overflow");
548
77.4k
    return str;
549
77.4k
  }
550
551
  /* create new interning string in shared interned strings buffer */
552
136k
  ZCSG(interned_strings).nNumOfElements++;
553
136k
  s = ZCSG(interned_strings).top;
554
136k
  hash_slot = STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), h);
555
136k
  STRTAB_COLLISION(s) = *hash_slot;
556
136k
  *hash_slot = STRTAB_STR_TO_POS(&ZCSG(interned_strings), s);
557
136k
  GC_SET_REFCOUNT(s, 2);
558
136k
  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
136k
  ZSTR_H(s) = h;
560
136k
  ZSTR_LEN(s) = ZSTR_LEN(str);
561
136k
  memcpy(ZSTR_VAL(s), ZSTR_VAL(str), ZSTR_LEN(s) + 1);
562
136k
  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.07k
    ZEND_ASSERT(GC_FLAGS(str) & IS_STR_PERMANENT);
569
3.07k
    GC_SET_REFCOUNT(s, GC_REFCOUNT(str));
570
3.07k
    GC_ADD_FLAGS(s, IS_STR_CLASS_NAME_MAP_PTR);
571
3.07k
  }
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
56.0M
{
579
56.0M
  zend_string_hash_val(str);
580
56.0M
  if (ZCG(counted)) {
581
56.0M
    zend_string *ret = accel_find_interned_string(str);
582
583
56.0M
    if (ret) {
584
7.19M
      zend_string_release(str);
585
7.19M
      return ret;
586
7.19M
    }
587
56.0M
  }
588
48.8M
  return str;
589
56.0M
}
590
591
static zend_always_inline zend_string *accel_find_interned_string_ex(zend_ulong h, const char *str, size_t size)
592
46.9k
{
593
46.9k
  zend_string_table_pos_t pos;
594
595
  /* check for existing interned string */
596
46.9k
  pos = *STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), h);
597
46.9k
  if (EXPECTED(pos != STRTAB_INVALID_POS)) {
598
55.7k
    do {
599
55.7k
      zend_string *s = STRTAB_POS_TO_STR(&ZCSG(interned_strings), pos);
600
55.7k
      if (EXPECTED(ZSTR_H(s) == h) && zend_string_equals_cstr(s, str, size)) {
601
39.9k
        return s;
602
39.9k
      }
603
15.8k
      pos = STRTAB_COLLISION(s);
604
15.8k
    } while (pos != STRTAB_INVALID_POS);
605
39.9k
  }
606
7.05k
  return NULL;
607
46.9k
}
608
609
static zend_string* ZEND_FASTCALL accel_init_interned_string_for_php(const char *str, size_t size, bool permanent)
610
1.39M
{
611
1.39M
  if (ZCG(counted)) {
612
46.9k
      zend_ulong h = zend_inline_hash_func(str, size);
613
46.9k
    zend_string *ret = accel_find_interned_string_ex(h, str, size);
614
615
46.9k
    if (!ret) {
616
7.05k
      ret = zend_string_init(str, size, permanent);
617
7.05k
      ZSTR_H(ret) = h;
618
7.05k
    }
619
620
46.9k
    return ret;
621
46.9k
  }
622
623
1.34M
  return zend_string_init(str, size, permanent);
624
1.39M
}
625
626
static inline void accel_copy_permanent_list_types(
627
  zend_new_interned_string_func_t new_interned_string, zend_type type)
628
54.4k
{
629
54.4k
  zend_type *single_type;
630
108k
  ZEND_TYPE_FOREACH_MUTABLE(type, single_type) {
631
108k
    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
108k
    if (ZEND_TYPE_HAS_NAME(*single_type)) {
636
3.92k
      ZEND_TYPE_SET_PTR(*single_type, new_interned_string(ZEND_TYPE_NAME(*single_type)));
637
3.92k
    }
638
54.4k
  } ZEND_TYPE_FOREACH_END();
639
54.4k
}
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.53k
  for (j = 0; j < ZEND_STR_LAST_KNOWN; j++) {
654
1.52k
    zend_known_strings[j] = new_interned_string(zend_known_strings[j]);
655
1.52k
  }
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.0k
        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.0k
        accel_copy_permanent_list_types(new_interned_string, arg_info[i].type);
681
30.0k
      }
682
11.0k
    }
683
22.1k
  } ZEND_HASH_FOREACH_END();
684
685
  /* class table hash keys, class names, properties, methods, constants, etc */
686
6.17k
  ZEND_HASH_MAP_FOREACH_BUCKET(CG(class_table), p) {
687
6.17k
    zend_class_entry *ce;
688
689
6.17k
    ce = (zend_class_entry*)Z_PTR(p->val);
690
691
6.17k
    if (p->key) {
692
3.07k
      p->key = new_interned_string(p->key);
693
3.07k
    }
694
695
6.17k
    if (ce->name) {
696
3.07k
      ce->name = new_interned_string(ce->name);
697
3.07k
      ZEND_ASSERT(ZSTR_HAS_CE_CACHE(ce->name));
698
3.07k
    }
699
700
21.6k
    ZEND_HASH_MAP_FOREACH_BUCKET(&ce->properties_info, q) {
701
21.6k
      zend_property_info *info;
702
703
21.6k
      info = (zend_property_info*)Z_PTR(q->val);
704
705
21.6k
      if (q->key) {
706
7.72k
        q->key = new_interned_string(q->key);
707
7.72k
      }
708
709
21.6k
      if (info->name) {
710
7.72k
        info->name = new_interned_string(info->name);
711
7.72k
      }
712
21.6k
    } ZEND_HASH_FOREACH_END();
713
714
79.6k
    ZEND_HASH_MAP_FOREACH_BUCKET(&ce->function_table, q) {
715
79.6k
      if (q->key) {
716
36.7k
        q->key = new_interned_string(q->key);
717
36.7k
      }
718
79.6k
      if (Z_FUNC(q->val)->common.function_name) {
719
36.7k
        Z_FUNC(q->val)->common.function_name = new_interned_string(Z_FUNC(q->val)->common.function_name);
720
36.7k
      }
721
79.6k
      if (Z_FUNC(q->val)->common.scope == ce) {
722
16.0k
        uint32_t num_args = Z_FUNC(q->val)->common.num_args + 1;
723
16.0k
        zend_arg_info *arg_info = Z_FUNC(q->val)->common.arg_info - 1;
724
725
16.0k
        if (Z_FUNC(q->val)->common.fn_flags & ZEND_ACC_VARIADIC) {
726
96
          num_args++;
727
96
        }
728
40.3k
        for (uint32_t i = 0 ; i < num_args; i++) {
729
24.3k
          if (i > 0) {
730
8.36k
            arg_info[i].name = new_interned_string(arg_info[i].name);
731
8.36k
            if (arg_info[i].default_value) {
732
2.54k
              arg_info[i].default_value = new_interned_string(arg_info[i].default_value);
733
2.54k
            }
734
8.36k
          }
735
24.3k
          accel_copy_permanent_list_types(new_interned_string, arg_info[i].type);
736
24.3k
        }
737
16.0k
      }
738
79.6k
    } ZEND_HASH_FOREACH_END();
739
740
20.4k
    ZEND_HASH_MAP_FOREACH_BUCKET(&ce->constants_table, q) {
741
20.4k
      zend_class_constant* c;
742
743
20.4k
      if (q->key) {
744
7.13k
        q->key = new_interned_string(q->key);
745
7.13k
      }
746
20.4k
      c = (zend_class_constant*)Z_PTR(q->val);
747
20.4k
      if (Z_TYPE(c->value) == IS_STRING) {
748
672
        ZVAL_STR(&c->value, new_interned_string(Z_STR(c->value)));
749
672
      }
750
20.4k
    } ZEND_HASH_FOREACH_END();
751
3.07k
  } 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
42.7k
{
1047
42.7k
  zend_stat_t statbuf = {0};
1048
#ifdef ZEND_WIN32
1049
  accel_time_t res;
1050
#endif
1051
1052
42.7k
  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
42.7k
  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
42.7k
    case ZEND_HANDLE_STREAM:
1100
42.7k
      {
1101
42.7k
        php_stream *stream = (php_stream *)file_handle->handle.stream.handle;
1102
42.7k
        php_stream_statbuf sb;
1103
42.7k
        int ret, er;
1104
1105
42.7k
        if (!stream ||
1106
3
            !stream->ops ||
1107
42.7k
            !stream->ops->stat) {
1108
42.7k
          return 0;
1109
42.7k
        }
1110
1111
3
        er = EG(error_reporting);
1112
3
        EG(error_reporting) = 0;
1113
3
        zend_try {
1114
3
          ret = stream->ops->stat(stream, &sb);
1115
3
        } zend_catch {
1116
0
          ret = -1;
1117
3
        } zend_end_try();
1118
3
        EG(error_reporting) = er;
1119
3
        if (ret != 0) {
1120
0
          return 0;
1121
0
        }
1122
1123
3
        statbuf = sb.sb;
1124
3
      }
1125
0
      break;
1126
1127
0
    default:
1128
0
      return 0;
1129
42.7k
  }
1130
1131
3
  if (size) {
1132
0
    *size = statbuf.st_size;
1133
0
  }
1134
3
  return statbuf.st_mtime;
1135
42.7k
}
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
46.4k
{
1196
46.4k
  if (persistent_script->timestamp == 0) {
1197
0
    return SUCCESS; /* Don't check timestamps of preloaded scripts */
1198
46.4k
  } else if (ZCG(accel_directives).revalidate_freq &&
1199
46.4k
      persistent_script->dynamic_members.revalidate >= ZCG(request_time)) {
1200
46.4k
    return SUCCESS;
1201
46.4k
  } 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
46.4k
}
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
277k
{
1223
277k
  const char *path = ZSTR_VAL(str);
1224
277k
  size_t path_length = ZSTR_LEN(str);
1225
1226
277k
  ZEND_ASSERT(GC_REFCOUNT(ZCG(key)) == 1);
1227
277k
  ZSTR_LEN(ZCG(key)) = 0;
1228
1229
  /* CWD and include_path don't matter for absolute file names and streams */
1230
277k
  if (IS_ABSOLUTE_PATH(path, path_length)) {
1231
    /* pass */
1232
236k
  } else if (UNEXPECTED(php_is_stream_path(path))) {
1233
1.60k
    if (!is_cacheable_stream_path(path)) {
1234
1.60k
      return NULL;
1235
1.60k
    }
1236
    /* pass */
1237
40.0k
  } else if (UNEXPECTED(!ZCG(accel_directives).use_cwd)) {
1238
    /* pass */
1239
40.0k
  } else {
1240
40.0k
    const char *include_path = NULL, *cwd = NULL;
1241
40.0k
    size_t include_path_len = 0, cwd_len = 0;
1242
40.0k
    const zend_string *parent_script = NULL;
1243
1244
40.0k
    if (EXPECTED(ZCG(cwd_key_len))) {
1245
4.72k
      cwd = ZCG(cwd_key);
1246
4.72k
      cwd_len = ZCG(cwd_key_len);
1247
35.2k
    } else {
1248
35.2k
      zend_string *cwd_str = accel_getcwd();
1249
1250
35.2k
      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
35.2k
      cwd = ZSTR_VAL(cwd_str);
1256
35.2k
      cwd_len = ZSTR_LEN(cwd_str);
1257
35.2k
      if (ZCG(cwd_check)) {
1258
35.2k
        ZCG(cwd_check) = false;
1259
35.2k
        if (ZCG(accelerator_enabled)) {
1260
1261
35.2k
          zend_string *str = accel_find_interned_string(cwd_str);
1262
35.2k
          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
35.2k
          if (str) {
1276
35.2k
            char buf[32];
1277
35.2k
            const char *res = zend_print_long_to_buf(buf + sizeof(buf) - 1, STRTAB_STR_TO_POS(&ZCSG(interned_strings), str));
1278
1279
35.2k
            cwd_len = ZCG(cwd_key_len) = buf + sizeof(buf) - 1 - res;
1280
35.2k
            cwd = ZCG(cwd_key);
1281
35.2k
            memcpy(ZCG(cwd_key), res, cwd_len + 1);
1282
35.2k
          } else {
1283
0
            return NULL;
1284
0
          }
1285
35.2k
        } else {
1286
0
          return NULL;
1287
0
        }
1288
35.2k
      }
1289
35.2k
    }
1290
1291
40.0k
    if (EXPECTED(ZCG(include_path_key_len))) {
1292
4.72k
      include_path = ZCG(include_path_key);
1293
4.72k
      include_path_len = ZCG(include_path_key_len);
1294
35.2k
    } else if (!ZCG(include_path) || ZSTR_LEN(ZCG(include_path)) == 0) {
1295
0
      include_path = "";
1296
0
      include_path_len = 0;
1297
35.2k
    } else {
1298
35.2k
      include_path = ZSTR_VAL(ZCG(include_path));
1299
35.2k
      include_path_len = ZSTR_LEN(ZCG(include_path));
1300
1301
35.2k
      if (ZCG(include_path_check)) {
1302
35.2k
        ZCG(include_path_check) = false;
1303
35.2k
        if (ZCG(accelerator_enabled)) {
1304
1305
35.2k
          zend_string *str = accel_find_interned_string(ZCG(include_path));
1306
35.2k
          if (!str) {
1307
3
            HANDLE_BLOCK_INTERRUPTIONS();
1308
3
            SHM_UNPROTECT();
1309
3
            zend_shared_alloc_lock();
1310
3
            str = accel_new_interned_string(zend_string_copy(ZCG(include_path)));
1311
3
            if (str == ZCG(include_path)) {
1312
0
              zend_string_release(str);
1313
0
              str = NULL;
1314
0
            }
1315
3
            zend_shared_alloc_unlock();
1316
3
            SHM_PROTECT();
1317
3
            HANDLE_UNBLOCK_INTERRUPTIONS();
1318
3
          }
1319
35.2k
          if (str) {
1320
35.2k
            char buf[32];
1321
35.2k
            const char *res = zend_print_long_to_buf(buf + sizeof(buf) - 1, STRTAB_STR_TO_POS(&ZCSG(interned_strings), str));
1322
1323
35.2k
            include_path_len = ZCG(include_path_key_len) = buf + sizeof(buf) - 1 - res;
1324
35.2k
            include_path = ZCG(include_path_key);
1325
35.2k
            memcpy(ZCG(include_path_key), res, include_path_len + 1);
1326
35.2k
          } else {
1327
0
            return NULL;
1328
0
          }
1329
35.2k
        } else {
1330
0
          return NULL;
1331
0
        }
1332
35.2k
      }
1333
35.2k
    }
1334
1335
    /* Calculate key length */
1336
40.0k
    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
40.0k
    char *key = ZSTR_VAL(ZCG(key));
1346
40.0k
    memcpy(key, path, path_length);
1347
40.0k
    key[path_length] = ':';
1348
40.0k
    size_t key_length = path_length + 1;
1349
40.0k
    memcpy(key + key_length, cwd, cwd_len);
1350
40.0k
    key_length += cwd_len;
1351
1352
40.0k
    if (include_path_len) {
1353
40.0k
      key[key_length] = ':';
1354
40.0k
      key_length += 1;
1355
40.0k
      memcpy(key + key_length, include_path, include_path_len);
1356
40.0k
      key_length += include_path_len;
1357
40.0k
    }
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
40.0k
    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.5k
      while (parent_script_len > 0) {
1368
56.5k
        --parent_script_len;
1369
56.5k
        if (IS_SLASH(ZSTR_VAL(parent_script)[parent_script_len])) {
1370
5.13k
          break;
1371
5.13k
        }
1372
56.5k
      }
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
40.0k
    key[key_length] = '\0';
1383
40.0k
    ZSTR_H(ZCG(key)) = 0;
1384
40.0k
    ZSTR_LEN(ZCG(key)) = key_length;
1385
40.0k
    return ZCG(key);
1386
40.0k
  }
1387
1388
  /* not use_cwd */
1389
236k
  return str;
1390
277k
}
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
62.5k
{
1400
62.5k
  if (persistent_script->corrupted) {
1401
    /* already discarded */
1402
0
    return;
1403
0
  }
1404
1405
62.5k
  persistent_script->corrupted = true;
1406
62.5k
  persistent_script->timestamp = 0;
1407
62.5k
  ZSMMG(wasted_shared_memory) += persistent_script->dynamic_members.memory_consumption;
1408
62.5k
  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
62.5k
}
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
62.5k
{
1421
62.5k
  zend_shared_alloc_lock();
1422
62.5k
  zend_accel_discard_script(persistent_script);
1423
62.5k
  zend_shared_alloc_unlock();
1424
62.5k
}
1425
1426
zend_result zend_accel_invalidate(zend_string *filename, bool force)
1427
75.0k
{
1428
75.0k
  zend_string *realpath;
1429
75.0k
  zend_persistent_script *persistent_script;
1430
75.0k
  zend_bool file_found = true;
1431
1432
75.0k
  if (!ZCG(accelerator_enabled) || accelerator_shm_read_lock() != SUCCESS) {
1433
0
    return FAILURE;
1434
0
  }
1435
1436
75.0k
  realpath = accelerator_orig_zend_resolve_path(filename);
1437
1438
75.0k
  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
75.0k
  if (ZCG(accel_directives).file_cache) {
1446
0
    zend_file_cache_invalidate(realpath);
1447
0
  }
1448
1449
75.0k
  persistent_script = zend_accel_hash_find(&ZCSG(hash), realpath);
1450
75.0k
  if (persistent_script && !persistent_script->corrupted) {
1451
62.5k
    zend_file_handle file_handle;
1452
62.5k
    zend_stream_init_filename_ex(&file_handle, realpath);
1453
62.5k
    file_handle.opened_path = realpath;
1454
1455
62.5k
    if (force ||
1456
0
      !ZCG(accel_directives).validate_timestamps ||
1457
62.5k
      do_validate_timestamps(persistent_script, &file_handle) == FAILURE) {
1458
62.5k
      HANDLE_BLOCK_INTERRUPTIONS();
1459
62.5k
      SHM_UNPROTECT();
1460
62.5k
      zend_accel_lock_discard_script(persistent_script);
1461
62.5k
      SHM_PROTECT();
1462
62.5k
      HANDLE_UNBLOCK_INTERRUPTIONS();
1463
62.5k
    }
1464
1465
62.5k
    file_handle.opened_path = NULL;
1466
62.5k
    zend_destroy_file_handle(&file_handle);
1467
62.5k
    file_found = true;
1468
62.5k
  }
1469
1470
75.0k
  accelerator_shm_read_unlock();
1471
75.0k
  zend_string_release_ex(realpath, 0);
1472
1473
75.0k
  return file_found ? SUCCESS : FAILURE;
1474
75.0k
}
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
63.1k
{
1522
63.1k
  return filename && ZSTR_LEN(filename) >= sizeof(".phar") &&
1523
63.1k
    !memcmp(ZSTR_VAL(filename) + ZSTR_LEN(filename) - (sizeof(".phar")-1), ".phar", sizeof(".phar")-1) &&
1524
0
    !strstr(ZSTR_VAL(filename), "://");
1525
63.1k
}
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
63.1k
{
1590
63.1k
  zend_accel_hash_entry *bucket;
1591
63.1k
  uint32_t memory_used;
1592
63.1k
  uint32_t orig_compiler_options;
1593
1594
63.1k
  orig_compiler_options = CG(compiler_options);
1595
63.1k
  if (ZCG(accel_directives).file_cache) {
1596
0
    CG(compiler_options) |= ZEND_COMPILE_WITH_FILE_CACHE;
1597
0
  }
1598
63.1k
  zend_optimize_script(&new_persistent_script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level);
1599
63.1k
  zend_accel_finalize_delayed_early_binding_list(new_persistent_script);
1600
63.1k
  CG(compiler_options) = orig_compiler_options;
1601
1602
  /* exclusive lock */
1603
63.1k
  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
63.1k
  bucket = zend_accel_hash_find_entry(&ZCSG(hash), new_persistent_script->script.filename);
1609
63.1k
  if (bucket) {
1610
62.5k
    zend_persistent_script *existing_persistent_script = (zend_persistent_script *)bucket->data;
1611
1612
62.5k
    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
62.5k
  }
1629
1630
63.1k
  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
63.1k
  zend_shared_alloc_init_xlat_table();
1643
1644
  /* Calculate the required memory size */
1645
63.1k
  memory_used = zend_accel_script_persist_calc(new_persistent_script, 1);
1646
1647
  /* Allocate shared memory */
1648
63.1k
  ZCG(mem) = zend_shared_alloc_aligned(memory_used);
1649
63.1k
  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
63.1k
  bzero_aligned(ZCG(mem), memory_used);
1661
1662
63.1k
  zend_shared_alloc_clear_xlat_table();
1663
1664
  /* Copy into shared memory */
1665
63.1k
  new_persistent_script = zend_accel_script_persist(new_persistent_script, 1);
1666
1667
63.1k
  zend_shared_alloc_destroy_xlat_table();
1668
1669
63.1k
  new_persistent_script->is_phar = is_phar_file(new_persistent_script->script.filename);
1670
1671
  /* Consistency check */
1672
63.1k
  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
63.1k
  bucket = zend_accel_hash_update(&ZCSG(hash), new_persistent_script->script.filename, 0, new_persistent_script);
1684
63.1k
  if (bucket) {
1685
63.1k
    zend_accel_error(ACCEL_LOG_INFO, "Cached script '%s'", ZSTR_VAL(new_persistent_script->script.filename));
1686
63.1k
    if (key &&
1687
        /* key may contain non-persistent PHAR aliases (see issues #115 and #149) */
1688
62.5k
        !zend_string_starts_with_literal(key, "phar://") &&
1689
62.5k
        !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
63.1k
  }
1706
1707
63.1k
  new_persistent_script->dynamic_members.memory_consumption = ZEND_ALIGNED_SIZE(new_persistent_script->size);
1708
1709
63.1k
  zend_shared_alloc_unlock();
1710
1711
63.1k
  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
63.1k
  *from_shared_memory = true;
1718
63.1k
  return new_persistent_script;
1719
63.1k
}
1720
1721
184
#define ZEND_AUTOGLOBAL_MASK_SERVER  (1 << 0)
1722
104
#define ZEND_AUTOGLOBAL_MASK_ENV     (1 << 1)
1723
98
#define ZEND_AUTOGLOBAL_MASK_REQUEST (1 << 2)
1724
1725
static int zend_accel_get_auto_globals(void)
1726
62.5k
{
1727
62.5k
  int mask = 0;
1728
62.5k
  if (zend_hash_exists(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_SERVER))) {
1729
86
    mask |= ZEND_AUTOGLOBAL_MASK_SERVER;
1730
86
  }
1731
62.5k
  if (zend_hash_exists(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_ENV))) {
1732
6
    mask |= ZEND_AUTOGLOBAL_MASK_ENV;
1733
6
  }
1734
62.5k
  if (zend_hash_exists(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_REQUEST))) {
1735
0
    mask |= ZEND_AUTOGLOBAL_MASK_REQUEST;
1736
0
  }
1737
62.5k
  return mask;
1738
62.5k
}
1739
1740
static void zend_accel_set_auto_globals(int mask)
1741
98
{
1742
98
  if (mask & ZEND_AUTOGLOBAL_MASK_SERVER) {
1743
90
    zend_is_auto_global(ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_SERVER));
1744
90
  }
1745
98
  if (mask & ZEND_AUTOGLOBAL_MASK_ENV) {
1746
8
    zend_is_auto_global(ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_ENV));
1747
8
  }
1748
98
  if (mask & ZEND_AUTOGLOBAL_MASK_REQUEST) {
1749
0
    zend_is_auto_global(ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_REQUEST));
1750
0
  }
1751
98
  ZCG(auto_globals_mask) |= mask;
1752
98
}
1753
1754
static zend_persistent_script *opcache_compile_file(zend_file_handle *file_handle, int type, zend_op_array **op_array_p)
1755
122k
{
1756
122k
  zend_persistent_script *new_persistent_script;
1757
122k
  uint32_t orig_functions_count, orig_class_count;
1758
122k
  zend_op_array *orig_active_op_array;
1759
122k
  zend_op_array *op_array;
1760
122k
  bool do_bailout = false;
1761
122k
  accel_time_t timestamp = 0;
1762
122k
  uint32_t orig_compiler_options = 0;
1763
1764
  /* Try to open file */
1765
122k
  if (file_handle->type == ZEND_HANDLE_FILENAME) {
1766
5
    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
5
  }
1778
1779
  /* check blacklist right after ensuring that file was opened */
1780
122k
  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
122k
  if (ZCG(accel_directives).validate_timestamps ||
1789
79.6k
      ZCG(accel_directives).file_update_protection ||
1790
79.6k
      ZCG(accel_directives).max_file_size > 0) {
1791
42.7k
    size_t size = 0;
1792
1793
    /* Obtain the file timestamps, *before* actually compiling them,
1794
     * otherwise we have a race-condition.
1795
     */
1796
42.7k
    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
42.7k
    if (timestamp == 0) {
1802
42.7k
      *op_array_p = accelerator_orig_compile_file(file_handle, type);
1803
42.7k
      return NULL;
1804
42.7k
    }
1805
1806
    /* check if file is too new (may be it's not written completely yet) */
1807
3
    if (ZCG(accel_directives).file_update_protection &&
1808
3
        ((accel_time_t)(ZCG(request_time) - ZCG(accel_directives).file_update_protection) < timestamp)) {
1809
2
      *op_array_p = accelerator_orig_compile_file(file_handle, type);
1810
2
      return NULL;
1811
2
    }
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
79.6k
  orig_active_op_array = CG(active_op_array);
1824
79.6k
  orig_functions_count = EG(function_table)->nNumUsed;
1825
79.6k
  orig_class_count = EG(class_table)->nNumUsed;
1826
1827
79.6k
  zend_try {
1828
79.6k
    orig_compiler_options = CG(compiler_options);
1829
79.6k
    CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
1830
79.6k
    CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING;
1831
79.6k
    CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION;
1832
79.6k
    CG(compiler_options) |= ZEND_COMPILE_IGNORE_OTHER_FILES;
1833
79.6k
    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
79.6k
    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
79.6k
    op_array = *op_array_p = accelerator_orig_compile_file(file_handle, type);
1846
79.6k
    CG(compiler_options) = orig_compiler_options;
1847
79.6k
  } zend_catch {
1848
2.87k
    op_array = NULL;
1849
2.87k
    do_bailout = true;
1850
2.87k
    CG(compiler_options) = orig_compiler_options;
1851
79.6k
  } zend_end_try();
1852
1853
  /* Restore originals */
1854
79.6k
  CG(active_op_array) = orig_active_op_array;
1855
1856
79.6k
  if (!op_array) {
1857
    /* compilation failed */
1858
17.0k
    if (do_bailout) {
1859
2.87k
      EG(record_errors) = false;
1860
2.87k
      zend_free_recorded_errors();
1861
2.87k
      zend_bailout();
1862
2.87k
    }
1863
14.2k
    return NULL;
1864
17.0k
  }
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
62.5k
  new_persistent_script = create_persistent_script();
1871
62.5k
  new_persistent_script->script.main_op_array = *op_array;
1872
62.5k
  zend_accel_move_user_functions(CG(function_table), CG(function_table)->nNumUsed - orig_functions_count, &new_persistent_script->script);
1873
62.5k
  zend_accel_move_user_classes(CG(class_table), CG(class_table)->nNumUsed - orig_class_count, &new_persistent_script->script);
1874
62.5k
  zend_accel_build_delayed_early_binding_list(new_persistent_script);
1875
1876
62.5k
  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
62.5k
  if (PG(auto_globals_jit)) {
1881
62.5k
    new_persistent_script->ping_auto_globals_mask = zend_accel_get_auto_globals();
1882
62.5k
  }
1883
1884
62.5k
  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
62.5k
  if (file_handle->opened_path) {
1893
6
    new_persistent_script->script.filename = zend_string_copy(file_handle->opened_path);
1894
62.5k
  } else {
1895
62.5k
    new_persistent_script->script.filename = zend_string_copy(file_handle->filename);
1896
62.5k
  }
1897
62.5k
  zend_string_hash_val(new_persistent_script->script.filename);
1898
1899
  /* Now persistent_script structure is ready in process memory */
1900
62.5k
  return new_persistent_script;
1901
79.6k
}
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
3.30k
{
2015
35.4k
  do {
2016
35.4k
    *dest++ = hexchars[n & 0xf];
2017
35.4k
    n >>= 4;
2018
35.4k
  } while (n);
2019
2020
3.30k
  return dest;
2021
3.30k
}
2022
2023
/* Prevents collisions with real scripts, as we don't cache paths prefixed with
2024
 * a scheme, except file:// and phar://. */
2025
4.96k
#define PFA_KEY_PREFIX "pfa://"
2026
2027
static zend_string *zend_accel_pfa_key(const zend_op *declaring_opline,
2028
    const zend_function *called_function)
2029
1.65k
{
2030
1.65k
  const size_t max_key_len = strlen(PFA_KEY_PREFIX) + (sizeof(uintptr_t)*2) + strlen(":") + (sizeof(uintptr_t)*2);
2031
1.65k
  zend_string *key = zend_string_alloc(max_key_len, 0);
2032
2033
1.65k
  char *dest = ZSTR_VAL(key);
2034
1.65k
  dest = zend_mempcpy(dest, PFA_KEY_PREFIX, strlen(PFA_KEY_PREFIX));
2035
1.65k
  dest = zend_accel_uintptr_hex(dest, (uintptr_t)declaring_opline);
2036
1.65k
  *dest++ = ':';
2037
2038
1.65k
  const void *ptr;
2039
1.65k
  if ((called_function->common.fn_flags & ZEND_ACC_CLOSURE)
2040
253
      && 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
253
    ptr = called_function->op_array.opcodes;
2046
1.40k
  } else {
2047
1.40k
    ptr = called_function;
2048
1.40k
  }
2049
1.65k
  dest = zend_accel_uintptr_hex(dest, (uintptr_t)ptr);
2050
2051
1.65k
  *dest = '\0';
2052
1.65k
  ZSTR_LEN(key) = dest - ZSTR_VAL(key);
2053
2054
1.65k
  return key;
2055
1.65k
}
2056
2057
const zend_op_array *zend_accel_pfa_cache_get(const zend_op_array *declaring_op_array,
2058
    const zend_op *declaring_opline, const zend_function *called_function)
2059
988
{
2060
988
  zend_string *key = zend_accel_pfa_key(declaring_opline, called_function);
2061
988
  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
988
  if (ZCG(accelerator_enabled)
2066
988
      && !file_cache_only
2067
988
      && !declaring_op_array->refcount
2068
936
      && (called_function->type != ZEND_USER_FUNCTION || !called_function->op_array.refcount)) {
2069
936
    zend_persistent_script *persistent_script = zend_accel_hash_find(&ZCSG(hash), key);
2070
936
    if (persistent_script) {
2071
198
      op_array = persistent_script->script.main_op_array.dynamic_func_defs[0];
2072
198
      if (persistent_script->num_warnings) {
2073
0
        zend_emit_recorded_errors_ex(persistent_script->num_warnings,
2074
0
            persistent_script->warnings);
2075
0
      }
2076
198
    }
2077
936
  } else {
2078
52
    op_array = zend_hash_find_ptr(&EG(partial_function_application_cache), key);
2079
52
  }
2080
2081
988
  zend_string_release(key);
2082
2083
988
  return op_array;
2084
988
}
2085
2086
zend_op_array *zend_accel_compile_pfa(zend_ast *ast,
2087
    const zend_op_array *declaring_op_array,
2088
    const zend_op *declaring_opline,
2089
    const zend_function *called_function,
2090
    zend_string *pfa_func_name)
2091
666
{
2092
666
  zend_begin_record_errors();
2093
666
  zend_op_array *op_array;
2094
2095
666
  uint32_t orig_compiler_options = CG(compiler_options);
2096
2097
666
  zend_try {
2098
666
    CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
2099
666
    CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING;
2100
666
    CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION;
2101
666
    CG(compiler_options) |= ZEND_COMPILE_IGNORE_OTHER_FILES;
2102
666
    CG(compiler_options) |= ZEND_COMPILE_IGNORE_OBSERVER;
2103
#ifdef ZEND_WIN32
2104
    /* On Windows, don't compile with internal classes. Shm may be attached from different
2105
     * processes with internal classes living in different addresses. */
2106
    CG(compiler_options) |= ZEND_COMPILE_IGNORE_INTERNAL_CLASSES;
2107
#endif
2108
2109
666
    op_array = zend_compile_ast(ast, ZEND_USER_FUNCTION, declaring_op_array->filename);
2110
2111
666
    CG(compiler_options) = orig_compiler_options;
2112
666
  } zend_catch {
2113
0
    CG(compiler_options) = orig_compiler_options;
2114
0
    zend_emit_recorded_errors();
2115
0
    zend_free_recorded_errors();
2116
0
    zend_bailout();
2117
666
  } zend_end_try();
2118
2119
666
  ZEND_ASSERT(op_array->num_dynamic_func_defs == 1);
2120
2121
666
  zend_string_release(op_array->dynamic_func_defs[0]->function_name);
2122
666
  op_array->dynamic_func_defs[0]->function_name = pfa_func_name;
2123
2124
666
  zend_string *key = zend_accel_pfa_key(declaring_opline, called_function);
2125
2126
  /* Cache op_array only if the declaring op_array and the called function
2127
   * are cached */
2128
666
  if (!ZCG(accelerator_enabled)
2129
666
      || file_cache_only
2130
666
      || declaring_op_array->refcount
2131
618
      || (called_function->type == ZEND_USER_FUNCTION && called_function->op_array.refcount)
2132
618
      || (ZCSG(restart_in_progress) && accel_restart_is_active())
2133
618
      || (!ZCG(counted) && accel_activate_add() == FAILURE)) {
2134
48
    zend_op_array *script_op_array = op_array;
2135
48
    zend_op_array *op_array = script_op_array->dynamic_func_defs[0];
2136
48
    GC_ADDREF(op_array->function_name);
2137
48
    (*op_array->refcount)++;
2138
48
    destroy_op_array(script_op_array);
2139
48
    efree(script_op_array);
2140
2141
48
    if ((called_function->common.fn_flags & ZEND_ACC_CLOSURE)
2142
9
        && called_function->type == ZEND_USER_FUNCTION
2143
9
        && called_function->op_array.refcount) {
2144
      /* Extend the lifetime of the called opcodes if
2145
       * the called function is a closure.
2146
       * See comment in zend_accel_pfa_key(). */
2147
9
      zend_op_array *copy = zend_arena_alloc(&CG(arena), sizeof(*copy));
2148
9
      memcpy(copy, called_function, sizeof(*copy));
2149
9
      function_add_ref((zend_function *) copy);
2150
      /* Reference the copy in op_array->dynamic_func_defs so that it's
2151
       * destroyed when op_array is destroyed. */
2152
9
      ZEND_ASSERT(!op_array->dynamic_func_defs && !op_array->num_dynamic_func_defs);
2153
9
      op_array->dynamic_func_defs = safe_emalloc(1, sizeof(*op_array->dynamic_func_defs), 0);
2154
9
      op_array->dynamic_func_defs[0] = copy;
2155
9
      op_array->num_dynamic_func_defs = 1;
2156
9
    }
2157
2158
48
    zend_hash_add_new_ptr(&EG(partial_function_application_cache), key, op_array);
2159
48
    zend_string_release(key);
2160
2161
48
    zend_emit_recorded_errors();
2162
48
    zend_free_recorded_errors();
2163
2164
48
    return op_array;
2165
48
  }
2166
2167
618
  zend_persistent_script *new_persistent_script = create_persistent_script();
2168
618
  new_persistent_script->script.main_op_array = *op_array;
2169
618
  efree_size(op_array, sizeof(*op_array));
2170
618
  new_persistent_script->script.filename = key;
2171
2172
618
  if (ZCG(accel_directives).record_warnings) {
2173
0
    new_persistent_script->num_warnings = EG(errors).size;
2174
0
    new_persistent_script->warnings = EG(errors).errors;
2175
0
  }
2176
2177
618
  HANDLE_BLOCK_INTERRUPTIONS();
2178
618
  SHM_UNPROTECT();
2179
2180
618
  bool from_shared_memory;
2181
  /* See GH-17246: we disable GC so that user code cannot be executed during the optimizer run. */
2182
618
  bool orig_gc_state = gc_enable(false);
2183
618
  char *orig_file_cache = ZCG(accel_directives).file_cache;
2184
  /* Disable file_cache temporarily, as we can't guarantee consistency. */
2185
618
  ZCG(accel_directives).file_cache = NULL;
2186
618
  new_persistent_script = cache_script_in_shared_memory(new_persistent_script, NULL, &from_shared_memory);
2187
618
  ZCG(accel_directives).file_cache = orig_file_cache;
2188
618
  gc_enable(orig_gc_state);
2189
2190
618
  SHM_PROTECT();
2191
618
  HANDLE_UNBLOCK_INTERRUPTIONS();
2192
2193
  /* We may have switched to an existing persistent script that was persisted in
2194
   * the meantime. Make sure to use its warnings if available. */
2195
618
  if (ZCG(accel_directives).record_warnings) {
2196
0
    EG(record_errors) = false;
2197
0
    zend_emit_recorded_errors_ex(new_persistent_script->num_warnings, new_persistent_script->warnings);
2198
618
  } else {
2199
618
    zend_emit_recorded_errors();
2200
618
  }
2201
618
  zend_free_recorded_errors();
2202
2203
618
  return new_persistent_script->script.main_op_array.dynamic_func_defs[0];
2204
666
}
2205
2206
/* zend_compile() replacement */
2207
zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
2208
275k
{
2209
275k
  zend_persistent_script *persistent_script = NULL;
2210
275k
  zend_string *key = NULL;
2211
275k
  bool from_shared_memory; /* if the script we've got is stored in SHM */
2212
2213
275k
  if (!file_handle->filename || !ZCG(accelerator_enabled)) {
2214
    /* The Accelerator is disabled, act as if without the Accelerator */
2215
0
    ZCG(cache_opline) = NULL;
2216
0
    ZCG(cache_persistent_script) = NULL;
2217
0
    if (file_handle->filename
2218
0
     && ZCG(accel_directives).file_cache
2219
0
     && ZCG(enabled) && accel_startup_ok) {
2220
0
      return file_cache_compile_file(file_handle, type);
2221
0
    }
2222
0
    return accelerator_orig_compile_file(file_handle, type);
2223
275k
  } else if (file_cache_only) {
2224
0
    ZCG(cache_opline) = NULL;
2225
0
    ZCG(cache_persistent_script) = NULL;
2226
0
    return file_cache_compile_file(file_handle, type);
2227
275k
  } else if ((ZCSG(restart_in_progress) && accel_restart_is_active())) {
2228
0
    if (ZCG(accel_directives).file_cache) {
2229
0
      return file_cache_compile_file(file_handle, type);
2230
0
    }
2231
0
    ZCG(cache_opline) = NULL;
2232
0
    ZCG(cache_persistent_script) = NULL;
2233
0
    return accelerator_orig_compile_file(file_handle, type);
2234
0
  }
2235
2236
  /* In case this callback is called from include_once, require_once or it's
2237
   * a main FastCGI request, the key must be already calculated, and cached
2238
   * persistent script already found */
2239
275k
  if (ZCG(cache_persistent_script) &&
2240
10
      ((!EG(current_execute_data) &&
2241
0
        file_handle->primary_script &&
2242
0
        ZCG(cache_opline) == NULL) ||
2243
10
       (EG(current_execute_data) &&
2244
10
        EG(current_execute_data)->func &&
2245
10
        ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
2246
10
        ZCG(cache_opline) == EG(current_execute_data)->opline))) {
2247
2248
10
    persistent_script = ZCG(cache_persistent_script);
2249
10
    if (ZSTR_LEN(ZCG(key))) {
2250
0
      key = ZCG(key);
2251
0
    }
2252
2253
275k
  } else {
2254
275k
    if (!ZCG(accel_directives).revalidate_path) {
2255
      /* try to find cached script by key */
2256
275k
      key = accel_make_persistent_key(file_handle->filename);
2257
275k
      if (!key) {
2258
1.60k
        ZCG(cache_opline) = NULL;
2259
1.60k
        ZCG(cache_persistent_script) = NULL;
2260
1.60k
        return accelerator_orig_compile_file(file_handle, type);
2261
1.60k
      }
2262
273k
      persistent_script = zend_accel_hash_find(&ZCSG(hash), key);
2263
273k
    } else if (UNEXPECTED(php_is_stream_path(ZSTR_VAL(file_handle->filename)) && !is_cacheable_stream_path(ZSTR_VAL(file_handle->filename)))) {
2264
0
      ZCG(cache_opline) = NULL;
2265
0
      ZCG(cache_persistent_script) = NULL;
2266
0
      return accelerator_orig_compile_file(file_handle, type);
2267
0
    }
2268
2269
273k
    if (!persistent_script) {
2270
      /* try to find cached script by full real path */
2271
45.8k
      zend_accel_hash_entry *bucket;
2272
2273
      /* open file to resolve the path */
2274
45.8k
        if (file_handle->type == ZEND_HANDLE_FILENAME
2275
3.08k
         && accelerator_orig_zend_stream_open_function(file_handle) == FAILURE) {
2276
1.10k
        if (!EG(exception)) {
2277
1.08k
          if (type == ZEND_REQUIRE) {
2278
180
            zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, ZSTR_VAL(file_handle->filename));
2279
902
          } else {
2280
902
            zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, ZSTR_VAL(file_handle->filename));
2281
902
          }
2282
1.08k
        }
2283
1.10k
        return NULL;
2284
1.10k
        }
2285
2286
44.7k
      if (file_handle->opened_path) {
2287
3
        bucket = zend_accel_hash_find_entry(&ZCSG(hash), file_handle->opened_path);
2288
2289
3
        if (bucket) {
2290
0
          persistent_script = (zend_persistent_script *)bucket->data;
2291
2292
0
          if (key && !persistent_script->corrupted) {
2293
0
            HANDLE_BLOCK_INTERRUPTIONS();
2294
0
            SHM_UNPROTECT();
2295
0
            zend_shared_alloc_lock();
2296
0
            zend_accel_add_key(key, bucket);
2297
0
            zend_shared_alloc_unlock();
2298
0
            SHM_PROTECT();
2299
0
            HANDLE_UNBLOCK_INTERRUPTIONS();
2300
0
          }
2301
0
        }
2302
3
      }
2303
44.7k
    }
2304
273k
  }
2305
2306
  /* clear cache */
2307
272k
  ZCG(cache_opline) = NULL;
2308
272k
  ZCG(cache_persistent_script) = NULL;
2309
2310
272k
  if (persistent_script && persistent_script->corrupted) {
2311
79.6k
    persistent_script = NULL;
2312
79.6k
  }
2313
2314
  /* Make sure we only increase the currently running processes semaphore
2315
     * once each execution (this function can be called more than once on
2316
     * each execution)
2317
     */
2318
272k
  if (!ZCG(counted)) {
2319
147k
    if (accel_activate_add() == FAILURE) {
2320
0
      if (ZCG(accel_directives).file_cache) {
2321
0
        return file_cache_compile_file(file_handle, type);
2322
0
      }
2323
0
      return accelerator_orig_compile_file(file_handle, type);
2324
0
    }
2325
147k
    ZCG(counted) = true;
2326
147k
  }
2327
2328
  /* Revalidate accessibility of cached file */
2329
272k
  if (EXPECTED(persistent_script != NULL) &&
2330
148k
      UNEXPECTED(ZCG(accel_directives).validate_permission) &&
2331
0
      file_handle->type == ZEND_HANDLE_FILENAME &&
2332
0
      UNEXPECTED(check_persistent_script_access(persistent_script))) {
2333
0
    if (!EG(exception)) {
2334
0
      if (type == ZEND_REQUIRE) {
2335
0
        zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, ZSTR_VAL(file_handle->filename));
2336
0
      } else {
2337
0
        zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, ZSTR_VAL(file_handle->filename));
2338
0
      }
2339
0
    }
2340
0
    return NULL;
2341
0
  }
2342
2343
272k
  HANDLE_BLOCK_INTERRUPTIONS();
2344
272k
  SHM_UNPROTECT();
2345
2346
  /* If script is found then validate_timestamps if option is enabled */
2347
272k
  if (persistent_script && ZCG(accel_directives).validate_timestamps) {
2348
46.4k
    if (validate_timestamp_and_record(persistent_script, file_handle) == FAILURE) {
2349
0
      zend_accel_lock_discard_script(persistent_script);
2350
0
      persistent_script = NULL;
2351
0
    }
2352
46.4k
  }
2353
2354
  /* Check the second level cache */
2355
272k
  if (!persistent_script && ZCG(accel_directives).file_cache) {
2356
0
    persistent_script = zend_file_cache_script_load(file_handle);
2357
0
  }
2358
2359
  /* If script was not found or invalidated by validate_timestamps */
2360
272k
  if (!persistent_script) {
2361
122k
    uint32_t old_const_num = zend_hash_next_free_element(EG(zend_constants));
2362
122k
    zend_op_array *op_array;
2363
2364
    /* Cache miss.. */
2365
122k
    ZCSG(misses)++;
2366
2367
    /* No memory left. Behave like without the Accelerator */
2368
122k
    if (ZSMMG(memory_exhausted) || ZCSG(restart_pending)) {
2369
0
      SHM_PROTECT();
2370
0
      HANDLE_UNBLOCK_INTERRUPTIONS();
2371
0
      if (ZCG(accel_directives).file_cache) {
2372
0
        return file_cache_compile_file(file_handle, type);
2373
0
      }
2374
0
      return accelerator_orig_compile_file(file_handle, type);
2375
0
    }
2376
2377
122k
    zend_begin_record_errors();
2378
2379
122k
    SHM_PROTECT();
2380
122k
    HANDLE_UNBLOCK_INTERRUPTIONS();
2381
122k
    persistent_script = opcache_compile_file(file_handle, type, &op_array);
2382
122k
    HANDLE_BLOCK_INTERRUPTIONS();
2383
122k
    SHM_UNPROTECT();
2384
2385
    /* Try and cache the script and assume that it is returned from_shared_memory.
2386
     * If it isn't compile_and_cache_file() changes the flag to 0
2387
     */
2388
122k
    from_shared_memory = false;
2389
122k
    if (persistent_script) {
2390
62.5k
      if (ZCG(accel_directives).record_warnings) {
2391
0
        persistent_script->num_warnings = EG(errors).size;
2392
0
        persistent_script->warnings = EG(errors).errors;
2393
0
      }
2394
2395
      /* See GH-17246: we disable GC so that user code cannot be executed during the optimizer run. */
2396
62.5k
      bool orig_gc_state = gc_enable(false);
2397
62.5k
      persistent_script = cache_script_in_shared_memory(persistent_script, key, &from_shared_memory);
2398
62.5k
      gc_enable(orig_gc_state);
2399
62.5k
    }
2400
2401
    /* Caching is disabled, returning op_array;
2402
     * or something went wrong during compilation, returning NULL
2403
     */
2404
122k
    if (!persistent_script) {
2405
54.3k
      SHM_PROTECT();
2406
54.3k
      HANDLE_UNBLOCK_INTERRUPTIONS();
2407
54.3k
      zend_emit_recorded_errors();
2408
54.3k
      zend_free_recorded_errors();
2409
54.3k
      return op_array;
2410
54.3k
    }
2411
68.0k
    if (from_shared_memory) {
2412
      /* Delete immutable arrays moved into SHM */
2413
62.5k
      uint32_t new_const_num = zend_hash_next_free_element(EG(zend_constants));
2414
62.5k
      while (new_const_num > old_const_num) {
2415
0
        new_const_num--;
2416
0
        zend_hash_index_del(EG(zend_constants), new_const_num);
2417
0
      }
2418
62.5k
    }
2419
68.0k
    persistent_script->dynamic_members.last_used = ZCG(request_time);
2420
68.0k
    SHM_PROTECT();
2421
68.0k
    HANDLE_UNBLOCK_INTERRUPTIONS();
2422
2423
    /* We may have switched to an existing persistent script that was persisted in
2424
     * the meantime. Make sure to use its warnings if available. */
2425
68.0k
    if (ZCG(accel_directives).record_warnings) {
2426
0
      EG(record_errors) = false;
2427
0
      zend_emit_recorded_errors_ex(persistent_script->num_warnings, persistent_script->warnings);
2428
68.0k
    } else {
2429
68.0k
      zend_emit_recorded_errors();
2430
68.0k
    }
2431
68.0k
    zend_free_recorded_errors();
2432
150k
  } else {
2433
2434
150k
#ifndef ZEND_WIN32
2435
150k
    ZCSG(hits)++; /* TBFixed: may lose one hit */
2436
150k
    persistent_script->dynamic_members.hits++; /* see above */
2437
#else
2438
#ifdef ZEND_ENABLE_ZVAL_LONG64
2439
    InterlockedIncrement64(&ZCSG(hits));
2440
    InterlockedIncrement64(&persistent_script->dynamic_members.hits);
2441
#else
2442
    InterlockedIncrement(&ZCSG(hits));
2443
    InterlockedIncrement(&persistent_script->dynamic_members.hits);
2444
#endif
2445
#endif
2446
2447
    /* see bug #15471 (old BTS) */
2448
150k
    if (persistent_script->script.filename) {
2449
148k
      if (!EG(current_execute_data) ||
2450
88.4k
          !EG(current_execute_data)->func ||
2451
88.4k
          !ZEND_USER_CODE(EG(current_execute_data)->func->common.type) ||
2452
88.4k
          !EG(current_execute_data)->opline ||
2453
88.4k
          EG(current_execute_data)->opline->opcode != ZEND_INCLUDE_OR_EVAL ||
2454
88.4k
          (EG(current_execute_data)->opline->extended_value != ZEND_INCLUDE_ONCE &&
2455
148k
           EG(current_execute_data)->opline->extended_value != ZEND_REQUIRE_ONCE)) {
2456
148k
        if (zend_hash_add_empty_element(&EG(included_files), persistent_script->script.filename) != NULL) {
2457
          /* ext/phar has to load phar's metadata into memory */
2458
64.1k
          if (persistent_script->is_phar) {
2459
0
            php_stream_statbuf ssb;
2460
0
            char *fname = zend_cstr_concat(
2461
0
              "phar://", sizeof("phar://") - 1,
2462
0
              ZSTR_VAL(persistent_script->script.filename),
2463
0
              ZSTR_LEN(persistent_script->script.filename));
2464
0
            php_stream_stat_path(fname, &ssb);
2465
0
            efree(fname);
2466
0
          }
2467
64.1k
        }
2468
148k
      }
2469
148k
    }
2470
150k
    persistent_script->dynamic_members.last_used = ZCG(request_time);
2471
150k
    SHM_PROTECT();
2472
150k
    HANDLE_UNBLOCK_INTERRUPTIONS();
2473
2474
150k
    zend_emit_recorded_errors_ex(persistent_script->num_warnings, persistent_script->warnings);
2475
150k
    from_shared_memory = true;
2476
150k
  }
2477
2478
  /* Fetch jit auto globals used in the script before execution */
2479
218k
  if (persistent_script->ping_auto_globals_mask & ~ZCG(auto_globals_mask)) {
2480
98
    zend_accel_set_auto_globals(persistent_script->ping_auto_globals_mask & ~ZCG(auto_globals_mask));
2481
98
  }
2482
2483
218k
  return zend_accel_load_script(persistent_script, from_shared_memory);
2484
272k
}
2485
2486
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)
2487
887
{
2488
887
  uint32_t i;
2489
2490
887
  ZEND_ASSERT(ce->ce_flags & ZEND_ACC_IMMUTABLE);
2491
887
  ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_LINKED));
2492
2493
887
  while (entry) {
2494
887
    bool found = true;
2495
887
    bool needs_autoload = false;
2496
2497
887
    if (entry->parent != parent) {
2498
0
      found = false;
2499
887
    } else {
2500
1.78k
      for (i = 0; i < ce->num_traits + ce->num_interfaces; i++) {
2501
900
        if (entry->traits_and_interfaces[i] != traits_and_interfaces[i]) {
2502
0
          found = false;
2503
0
          break;
2504
0
        }
2505
900
      }
2506
887
      if (found && entry->dependencies) {
2507
123
        for (i = 0; i < entry->dependencies_count; i++) {
2508
85
          const zend_class_entry *dependency_ce = zend_lookup_class_ex(entry->dependencies[i].name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD);
2509
2510
85
          if (dependency_ce != entry->dependencies[i].ce) {
2511
20
            if (!dependency_ce) {
2512
20
              needs_autoload = true;
2513
20
            } else {
2514
0
              found = false;
2515
0
              break;
2516
0
            }
2517
20
          }
2518
85
        }
2519
38
      }
2520
887
    }
2521
887
    if (found) {
2522
887
      *needs_autoload_ptr = needs_autoload;
2523
887
      return entry;
2524
887
    }
2525
0
    entry = entry->next;
2526
0
  }
2527
2528
0
  return NULL;
2529
887
}
2530
2531
static zend_class_entry* zend_accel_inheritance_cache_get(zend_class_entry *ce, zend_class_entry *parent, zend_class_entry **traits_and_interfaces)
2532
5.32k
{
2533
5.32k
  bool needs_autoload;
2534
5.32k
  zend_inheritance_cache_entry *entry = ce->inheritance_cache;
2535
2536
5.32k
  while (entry) {
2537
883
    entry = zend_accel_inheritance_cache_find(entry, ce, parent, traits_and_interfaces, &needs_autoload);
2538
883
    if (entry) {
2539
883
      if (!needs_autoload) {
2540
871
        zend_emit_recorded_errors_ex(entry->num_warnings, entry->warnings);
2541
871
        if (ZCSG(map_ptr_last) > CG(map_ptr_last)) {
2542
0
          zend_map_ptr_extend(ZCSG(map_ptr_last));
2543
0
        }
2544
871
        ce = entry->ce;
2545
871
        if (ZSTR_HAS_CE_CACHE(ce->name)) {
2546
833
          ZSTR_SET_CE_CACHE_EX(ce->name, ce, 0);
2547
833
        }
2548
871
        return ce;
2549
871
      }
2550
2551
16
      for (uint32_t i = 0; i < entry->dependencies_count; i++) {
2552
14
        const zend_class_entry *dependency_ce = zend_lookup_class_ex(entry->dependencies[i].name, NULL, 0);
2553
2554
14
        if (dependency_ce == NULL) {
2555
10
          return NULL;
2556
10
        }
2557
14
      }
2558
12
    }
2559
883
  }
2560
2561
4.44k
  return NULL;
2562
5.32k
}
2563
2564
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)
2565
3.38k
{
2566
3.38k
  zend_persistent_script dummy;
2567
3.38k
  size_t size;
2568
3.38k
  uint32_t i;
2569
3.38k
  bool needs_autoload;
2570
3.38k
  zend_class_entry *new_ce;
2571
3.38k
  zend_inheritance_cache_entry *entry;
2572
2573
3.38k
  ZEND_ASSERT(!(ce->ce_flags & ZEND_ACC_IMMUTABLE));
2574
3.38k
  ZEND_ASSERT(ce->ce_flags & ZEND_ACC_LINKED);
2575
2576
3.38k
  if (!ZCG(accelerator_enabled) ||
2577
3.38k
      (ZCSG(restart_in_progress) && accel_restart_is_active())) {
2578
0
    return NULL;
2579
0
  }
2580
2581
3.38k
  if (traits_and_interfaces && dependencies) {
2582
100
    for (i = 0; i < proto->num_traits + proto->num_interfaces; i++) {
2583
50
      if (traits_and_interfaces[i]) {
2584
50
        zend_hash_del(dependencies, traits_and_interfaces[i]->name);
2585
50
      }
2586
50
    }
2587
50
  }
2588
2589
3.38k
  SHM_UNPROTECT();
2590
3.38k
  zend_shared_alloc_lock();
2591
2592
3.38k
  entry = proto->inheritance_cache;
2593
3.38k
  while (entry) {
2594
4
    entry = zend_accel_inheritance_cache_find(entry, proto, parent, traits_and_interfaces, &needs_autoload);
2595
4
    if (entry) {
2596
4
      zend_shared_alloc_unlock();
2597
4
      SHM_PROTECT();
2598
4
      if (!needs_autoload) {
2599
0
        zend_map_ptr_extend(ZCSG(map_ptr_last));
2600
0
        return entry->ce;
2601
4
      } else {
2602
4
        return NULL;
2603
4
      }
2604
4
    }
2605
4
  }
2606
2607
3.37k
  zend_shared_alloc_init_xlat_table();
2608
2609
3.37k
  memset(&dummy, 0, sizeof(dummy));
2610
3.37k
  dummy.size = ZEND_ALIGNED_SIZE(
2611
3.37k
    sizeof(zend_inheritance_cache_entry) -
2612
3.37k
    sizeof(void*) +
2613
3.37k
    (sizeof(void*) * (proto->num_traits + proto->num_interfaces)));
2614
3.37k
  if (dependencies) {
2615
102
    dummy.size += ZEND_ALIGNED_SIZE(zend_hash_num_elements(dependencies) * sizeof(zend_class_dependency));
2616
102
  }
2617
3.37k
  ZCG(current_persistent_script) = &dummy;
2618
3.37k
  zend_persist_class_entry_calc(ce);
2619
3.37k
  zend_persist_warnings_calc(EG(errors).size, EG(errors).errors);
2620
3.37k
  size = dummy.size;
2621
2622
3.37k
  zend_shared_alloc_clear_xlat_table();
2623
2624
#if ZEND_MM_NEED_EIGHT_BYTE_REALIGNMENT
2625
  /* Align to 8-byte boundary */
2626
  ZCG(mem) = zend_shared_alloc(size + 8);
2627
#else
2628
3.37k
  ZCG(mem) = zend_shared_alloc(size);
2629
3.37k
#endif
2630
2631
3.37k
  if (!ZCG(mem)) {
2632
0
    zend_shared_alloc_destroy_xlat_table();
2633
0
    zend_shared_alloc_unlock();
2634
0
    SHM_PROTECT();
2635
0
    return NULL;
2636
0
  }
2637
2638
3.37k
  zend_map_ptr_extend(ZCSG(map_ptr_last));
2639
2640
#if ZEND_MM_NEED_EIGHT_BYTE_REALIGNMENT
2641
  /* Align to 8-byte boundary */
2642
  ZCG(mem) = (void*)(((uintptr_t)ZCG(mem) + 7L) & ~7L);
2643
#endif
2644
2645
3.37k
  memset(ZCG(mem), 0, size);
2646
3.37k
  entry = (zend_inheritance_cache_entry*)ZCG(mem);
2647
3.37k
  ZCG(mem) = (char*)ZCG(mem) +
2648
3.37k
    ZEND_ALIGNED_SIZE(
2649
3.37k
      (sizeof(zend_inheritance_cache_entry) -
2650
3.37k
       sizeof(void*) +
2651
3.37k
       (sizeof(void*) * (proto->num_traits + proto->num_interfaces))));
2652
3.37k
  entry->parent = parent;
2653
6.89k
  for (i = 0; i < proto->num_traits + proto->num_interfaces; i++) {
2654
3.52k
    entry->traits_and_interfaces[i] = traits_and_interfaces[i];
2655
3.52k
  }
2656
3.37k
  if (dependencies && zend_hash_num_elements(dependencies)) {
2657
94
    zend_string *dep_name;
2658
94
    zend_class_entry *dep_ce;
2659
2660
94
    i = 0;
2661
94
    entry->dependencies_count = zend_hash_num_elements(dependencies);
2662
94
    entry->dependencies = (zend_class_dependency*)ZCG(mem);
2663
648
    ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(dependencies, dep_name, dep_ce) {
2664
648
#if ZEND_DEBUG
2665
648
      ZEND_ASSERT(zend_accel_in_shm(dep_name));
2666
648
#endif
2667
648
      entry->dependencies[i].name = dep_name;
2668
230
      entry->dependencies[i].ce = dep_ce;
2669
230
      i++;
2670
230
    } ZEND_HASH_FOREACH_END();
2671
94
    ZCG(mem) = (char*)ZCG(mem) + zend_hash_num_elements(dependencies) * sizeof(zend_class_dependency);
2672
94
  }
2673
2674
  /* See GH-15657: `zend_persist_class_entry` can JIT property hook code via
2675
   * `zend_persist_property_info`, but the inheritance cache should not
2676
   * JIT those at this point in time. */
2677
3.37k
#ifdef HAVE_JIT
2678
3.37k
  bool jit_on_old = JIT_G(on);
2679
3.37k
  JIT_G(on) = false;
2680
3.37k
#endif
2681
2682
3.37k
  entry->ce = new_ce = zend_persist_class_entry(ce);
2683
3.37k
  zend_update_parent_ce(new_ce);
2684
2685
3.37k
#ifdef HAVE_JIT
2686
3.37k
  JIT_G(on) = jit_on_old;
2687
3.37k
#endif
2688
2689
3.37k
  entry->num_warnings = EG(errors).size;
2690
3.37k
  entry->warnings = zend_persist_warnings(EG(errors).size, EG(errors).errors);
2691
3.37k
  entry->next = proto->inheritance_cache;
2692
3.37k
  proto->inheritance_cache = entry;
2693
2694
3.37k
  ZCSG(map_ptr_last) = CG(map_ptr_last);
2695
2696
3.37k
  zend_shared_alloc_destroy_xlat_table();
2697
2698
3.37k
  zend_shared_alloc_unlock();
2699
3.37k
  SHM_PROTECT();
2700
2701
  /* Consistency check */
2702
3.37k
  if ((char*)entry + size != (char*)ZCG(mem)) {
2703
0
    zend_accel_error(
2704
0
      ((char*)entry + size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
2705
0
      "Internal error: wrong class size calculation: %s start=" ZEND_ADDR_FMT ", end=" ZEND_ADDR_FMT ", real=" ZEND_ADDR_FMT "\n",
2706
0
      ZSTR_VAL(ce->name),
2707
0
      (size_t)entry,
2708
0
      (size_t)((char *)entry + size),
2709
0
      (size_t)ZCG(mem));
2710
0
  }
2711
2712
3.37k
  zend_map_ptr_extend(ZCSG(map_ptr_last));
2713
2714
3.37k
  return new_ce;
2715
3.37k
}
2716
2717
#ifdef ZEND_WIN32
2718
static zend_result accel_gen_uname_id(void)
2719
{
2720
  PHP_MD5_CTX ctx;
2721
  unsigned char digest[16];
2722
  wchar_t uname[UNLEN + 1];
2723
  DWORD unsize = UNLEN;
2724
2725
  if (!GetUserNameW(uname, &unsize)) {
2726
    return FAILURE;
2727
  }
2728
  PHP_MD5Init(&ctx);
2729
  PHP_MD5Update(&ctx, (void *) uname, (unsize - 1) * sizeof(wchar_t));
2730
  PHP_MD5Update(&ctx, ZCG(accel_directives).cache_id, strlen(ZCG(accel_directives).cache_id));
2731
  PHP_MD5Final(digest, &ctx);
2732
  zend_bin2hex(accel_uname_id, digest, sizeof digest);
2733
  return SUCCESS;
2734
}
2735
#endif
2736
2737
/* zend_stream_open_function() replacement for PHP 5.3 and above */
2738
static zend_result persistent_stream_open_function(zend_file_handle *handle)
2739
1.80k
{
2740
1.80k
  if (ZCG(cache_persistent_script)) {
2741
    /* check if callback is called from include_once or it's a main request */
2742
10
    if ((!EG(current_execute_data) &&
2743
0
         handle->primary_script &&
2744
0
         ZCG(cache_opline) == NULL) ||
2745
10
        (EG(current_execute_data) &&
2746
10
         EG(current_execute_data)->func &&
2747
10
         ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
2748
10
         ZCG(cache_opline) == EG(current_execute_data)->opline)) {
2749
2750
      /* we are in include_once or FastCGI request */
2751
10
      handle->opened_path = zend_string_copy(ZCG(cache_persistent_script)->script.filename);
2752
10
      return SUCCESS;
2753
10
    }
2754
0
    ZCG(cache_opline) = NULL;
2755
0
    ZCG(cache_persistent_script) = NULL;
2756
0
  }
2757
1.79k
  return accelerator_orig_zend_stream_open_function(handle);
2758
1.80k
}
2759
2760
/* zend_resolve_path() replacement for PHP 5.3 and above */
2761
static zend_string* persistent_zend_resolve_path(zend_string *filename)
2762
7.08k
{
2763
7.08k
  if (!file_cache_only &&
2764
7.08k
      ZCG(accelerator_enabled)) {
2765
2766
    /* check if callback is called from include_once or it's a main request */
2767
7.08k
    if ((!EG(current_execute_data)) ||
2768
7.08k
        (EG(current_execute_data) &&
2769
7.08k
         EG(current_execute_data)->func &&
2770
7.08k
         ZEND_USER_CODE(EG(current_execute_data)->func->common.type) &&
2771
7.06k
         EG(current_execute_data)->opline->opcode == ZEND_INCLUDE_OR_EVAL &&
2772
7.06k
         (EG(current_execute_data)->opline->extended_value == ZEND_INCLUDE_ONCE ||
2773
7.03k
          EG(current_execute_data)->opline->extended_value == ZEND_REQUIRE_ONCE))) {
2774
2775
      /* we are in include_once or FastCGI request */
2776
2.37k
      zend_string *resolved_path;
2777
2.37k
      zend_string *key = NULL;
2778
2779
2.37k
      if (!ZCG(accel_directives).revalidate_path) {
2780
        /* lookup by "not-real" path */
2781
2.37k
        key = accel_make_persistent_key(filename);
2782
2.37k
        if (key) {
2783
2.37k
          const zend_accel_hash_entry *bucket = zend_accel_hash_find_entry(&ZCSG(hash), key);
2784
2.37k
          if (bucket != NULL) {
2785
14
            zend_persistent_script *persistent_script = (zend_persistent_script *)bucket->data;
2786
14
            if (!persistent_script->corrupted) {
2787
14
              ZCG(cache_opline) = EG(current_execute_data) ? EG(current_execute_data)->opline : NULL;
2788
14
              ZCG(cache_persistent_script) = persistent_script;
2789
14
              return zend_string_copy(persistent_script->script.filename);
2790
14
            }
2791
14
          }
2792
2.37k
        } else {
2793
0
          ZCG(cache_opline) = NULL;
2794
0
          ZCG(cache_persistent_script) = NULL;
2795
0
          return accelerator_orig_zend_resolve_path(filename);
2796
0
        }
2797
2.37k
      }
2798
2799
      /* find the full real path */
2800
2.36k
      resolved_path = accelerator_orig_zend_resolve_path(filename);
2801
2802
2.36k
      if (resolved_path) {
2803
        /* lookup by real path */
2804
18
        zend_accel_hash_entry *bucket = zend_accel_hash_find_entry(&ZCSG(hash), resolved_path);
2805
18
        if (bucket) {
2806
0
          zend_persistent_script *persistent_script = (zend_persistent_script *)bucket->data;
2807
0
          if (!persistent_script->corrupted) {
2808
0
            if (key) {
2809
              /* add another "key" for the same bucket */
2810
0
              HANDLE_BLOCK_INTERRUPTIONS();
2811
0
              SHM_UNPROTECT();
2812
0
              zend_shared_alloc_lock();
2813
0
              zend_accel_add_key(key, bucket);
2814
0
              zend_shared_alloc_unlock();
2815
0
              SHM_PROTECT();
2816
0
              HANDLE_UNBLOCK_INTERRUPTIONS();
2817
0
            } else {
2818
0
              ZSTR_LEN(ZCG(key)) = 0;
2819
0
            }
2820
0
            ZCG(cache_opline) = EG(current_execute_data) ? EG(current_execute_data)->opline : NULL;
2821
0
            ZCG(cache_persistent_script) = persistent_script;
2822
0
            return resolved_path;
2823
0
          }
2824
0
        }
2825
18
      }
2826
2827
2.36k
      ZCG(cache_opline) = NULL;
2828
2.36k
      ZCG(cache_persistent_script) = NULL;
2829
2.36k
      return resolved_path;
2830
2.36k
    }
2831
7.08k
  }
2832
4.70k
  ZCG(cache_opline) = NULL;
2833
4.70k
  ZCG(cache_persistent_script) = NULL;
2834
4.70k
  return accelerator_orig_zend_resolve_path(filename);
2835
7.08k
}
2836
2837
static void zend_reset_cache_vars(void)
2838
16
{
2839
16
  ZSMMG(memory_exhausted) = false;
2840
16
  ZCSG(hits) = 0;
2841
16
  ZCSG(misses) = 0;
2842
16
  ZCSG(blacklist_misses) = 0;
2843
16
  ZSMMG(wasted_shared_memory) = 0;
2844
16
  ZCSG(restart_pending) = false;
2845
16
  ZCSG(force_restart_time) = 0;
2846
16
  ZCSG(map_ptr_last) = CG(map_ptr_last);
2847
16
  ZCSG(map_ptr_static_last) = zend_map_ptr_static_last;
2848
16
}
2849
2850
static void accel_reset_pcre_cache(void)
2851
0
{
2852
0
  Bucket *p;
2853
2854
0
  ZEND_HASH_MAP_FOREACH_BUCKET(&PCRE_G(pcre_cache), p) {
2855
    /* Remove PCRE cache entries with inconsistent keys */
2856
0
    if (zend_accel_in_shm(p->key)) {
2857
0
      p->key = NULL;
2858
0
      zend_hash_del_bucket(&PCRE_G(pcre_cache), p);
2859
0
    }
2860
0
  } ZEND_HASH_FOREACH_END();
2861
0
}
2862
2863
ZEND_RINIT_FUNCTION(zend_accelerator)
2864
300k
{
2865
300k
  if (!ZCG(enabled) || !accel_startup_ok) {
2866
0
    ZCG(accelerator_enabled) = false;
2867
0
    return SUCCESS;
2868
0
  }
2869
2870
  /* PHP-5.4 and above return "double", but we use 1 sec precision */
2871
300k
  ZCG(auto_globals_mask) = 0;
2872
300k
  ZCG(request_time) = (time_t)sapi_get_request_time();
2873
300k
  ZCG(cache_opline) = NULL;
2874
300k
  ZCG(cache_persistent_script) = NULL;
2875
300k
  ZCG(include_path_key_len) = 0;
2876
300k
  ZCG(include_path_check) = true;
2877
2878
300k
  ZCG(cwd) = NULL;
2879
300k
  ZCG(cwd_key_len) = 0;
2880
300k
  ZCG(cwd_check) = true;
2881
2882
300k
  if (file_cache_only) {
2883
0
    ZCG(accelerator_enabled) = false;
2884
0
    return SUCCESS;
2885
0
  }
2886
2887
300k
#ifndef ZEND_WIN32
2888
300k
  if (ZCG(accel_directives).validate_root) {
2889
0
    struct stat buf;
2890
2891
0
    if (stat("/", &buf) != 0) {
2892
0
      ZCG(root_hash) = 0;
2893
0
    } else {
2894
0
      ZCG(root_hash) = buf.st_ino;
2895
0
      if (sizeof(buf.st_ino) > sizeof(ZCG(root_hash))) {
2896
0
        if (ZCG(root_hash) != buf.st_ino) {
2897
0
          zend_string *key = ZSTR_INIT_LITERAL("opcache.enable", 0);
2898
0
          zend_alter_ini_entry_chars(key, "0", 1, ZEND_INI_SYSTEM, ZEND_INI_STAGE_RUNTIME);
2899
0
          zend_string_release_ex(key, 0);
2900
0
          zend_accel_error(ACCEL_LOG_WARNING, "Can't cache files in chroot() directory with too big inode");
2901
0
          return SUCCESS;
2902
0
        }
2903
0
      }
2904
0
    }
2905
300k
  } else {
2906
300k
    ZCG(root_hash) = 0;
2907
300k
  }
2908
300k
#endif
2909
2910
300k
  HANDLE_BLOCK_INTERRUPTIONS();
2911
300k
  SHM_UNPROTECT();
2912
2913
300k
  if (ZCG(counted)) {
2914
#ifdef ZTS
2915
    zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for thread id %lu", (unsigned long) tsrm_thread_id());
2916
#else
2917
0
    zend_accel_error(ACCEL_LOG_WARNING, "Stuck count for pid %d", getpid());
2918
0
#endif
2919
0
    accel_unlock_all();
2920
0
    ZCG(counted) = false;
2921
0
  }
2922
2923
300k
  if (ZCSG(restart_pending)) {
2924
0
    zend_shared_alloc_lock();
2925
0
    if (ZCSG(restart_pending)) { /* check again, to ensure that the cache wasn't already cleaned by another process */
2926
0
      if (accel_is_inactive()) {
2927
0
        zend_accel_error(ACCEL_LOG_DEBUG, "Restarting!");
2928
0
        ZCSG(restart_pending) = false;
2929
0
        switch ZCSG(restart_reason) {
2930
0
          case ACCEL_RESTART_OOM:
2931
0
            ZCSG(oom_restarts)++;
2932
0
            break;
2933
0
          case ACCEL_RESTART_HASH:
2934
0
            ZCSG(hash_restarts)++;
2935
0
            break;
2936
0
          case ACCEL_RESTART_USER:
2937
0
            ZCSG(manual_restarts)++;
2938
0
            break;
2939
0
        }
2940
0
        accel_restart_enter();
2941
2942
0
        zend_map_ptr_reset();
2943
0
        zend_reset_cache_vars();
2944
0
        zend_accel_hash_clean(&ZCSG(hash));
2945
2946
0
        if (ZCG(accel_directives).interned_strings_buffer) {
2947
0
          accel_interned_strings_restore_state();
2948
0
        }
2949
2950
0
        zend_shared_alloc_restore_state();
2951
0
#ifdef PRELOAD_SUPPORT
2952
0
        if (ZCSG(preload_script)) {
2953
0
          preload_restart();
2954
0
        }
2955
0
#endif
2956
2957
0
#ifdef HAVE_JIT
2958
0
        zend_jit_restart();
2959
0
#endif
2960
2961
0
        ZCSG(accelerator_enabled) = ZCSG(cache_status_before_restart);
2962
0
        if (ZCSG(last_restart_time) < ZCG(request_time)) {
2963
0
          ZCSG(last_restart_time) = ZCG(request_time);
2964
0
        } else {
2965
0
          ZCSG(last_restart_time)++;
2966
0
        }
2967
0
        accel_restart_leave();
2968
0
      }
2969
0
    }
2970
0
    zend_shared_alloc_unlock();
2971
0
  }
2972
2973
300k
  ZCG(accelerator_enabled) = ZCSG(accelerator_enabled);
2974
2975
300k
  SHM_PROTECT();
2976
300k
  HANDLE_UNBLOCK_INTERRUPTIONS();
2977
2978
300k
  if (ZCG(accelerator_enabled) && ZCSG(last_restart_time) != ZCG(last_restart_time)) {
2979
    /* SHM was reinitialized. */
2980
0
    ZCG(last_restart_time) = ZCSG(last_restart_time);
2981
2982
    /* Reset in-process realpath cache */
2983
0
    realpath_cache_clean();
2984
2985
0
    accel_reset_pcre_cache();
2986
0
    ZCG(pcre_reseted) = false;
2987
300k
  } else if (!ZCG(accelerator_enabled) && !ZCG(pcre_reseted)) {
2988
0
    accel_reset_pcre_cache();
2989
0
    ZCG(pcre_reseted) = true;
2990
0
  }
2991
2992
2993
300k
#ifdef HAVE_JIT
2994
300k
  zend_jit_activate();
2995
300k
#endif
2996
2997
300k
#ifdef PRELOAD_SUPPORT
2998
300k
  if (ZCSG(preload_script)) {
2999
0
    preload_activate();
3000
0
  }
3001
300k
#endif
3002
3003
300k
  return SUCCESS;
3004
300k
}
3005
3006
#ifdef HAVE_JIT
3007
void accel_deactivate(void)
3008
300k
{
3009
300k
  zend_jit_deactivate();
3010
300k
}
3011
#endif
3012
3013
zend_result accel_post_deactivate(void)
3014
300k
{
3015
300k
  if (ZCG(cwd)) {
3016
35.2k
    zend_string_release_ex(ZCG(cwd), 0);
3017
35.2k
    ZCG(cwd) = NULL;
3018
35.2k
  }
3019
3020
300k
  if (!ZCG(enabled) || !accel_startup_ok) {
3021
0
    return SUCCESS;
3022
0
  }
3023
3024
300k
  zend_shared_alloc_safe_unlock(); /* be sure we didn't leave cache locked */
3025
300k
  accel_unlock_all();
3026
300k
  ZCG(counted) = false;
3027
3028
300k
  return SUCCESS;
3029
300k
}
3030
3031
static int accelerator_remove_cb(zend_extension *element1, zend_extension *element2)
3032
0
{
3033
0
  (void)element2; /* keep the compiler happy */
3034
3035
0
  if (!strcmp(element1->name, ACCELERATOR_PRODUCT_NAME )) {
3036
0
    element1->startup = NULL;
3037
#if 0
3038
    /* We have to call shutdown callback it to free TS resources */
3039
    element1->shutdown = NULL;
3040
#endif
3041
0
    element1->activate = NULL;
3042
0
    element1->deactivate = NULL;
3043
0
    element1->op_array_handler = NULL;
3044
3045
#ifdef __DEBUG_MESSAGES__
3046
    fprintf(stderr, ACCELERATOR_PRODUCT_NAME " is disabled: %s\n", (zps_failure_reason ? zps_failure_reason : "unknown error"));
3047
    fflush(stderr);
3048
#endif
3049
0
  }
3050
3051
0
  return 0;
3052
0
}
3053
3054
static void zps_startup_failure(const char *reason, const char *api_reason, int (*cb)(zend_extension *, zend_extension *))
3055
0
{
3056
0
  accel_startup_ok = false;
3057
0
  zps_failure_reason = reason;
3058
0
  zps_api_failure_reason = api_reason?api_reason:reason;
3059
0
  zend_llist_del_element(&zend_extensions, NULL, (int (*)(void *, void *))cb);
3060
0
}
3061
3062
/* Return whether we are running a CLI (Command LIne) SAPI for which Opcache is
3063
 * disabled when `opcache.enable_cli=0` */
3064
static inline bool accel_sapi_is_cli(void)
3065
16
{
3066
16
  return strcmp(sapi_module.name, "cli") == 0
3067
16
    || strcmp(sapi_module.name, "phpdbg") == 0;
3068
16
}
3069
3070
static zend_result zend_accel_init_shm(void)
3071
16
{
3072
16
  int i;
3073
16
  size_t accel_shared_globals_size;
3074
3075
16
  zend_shared_alloc_lock();
3076
3077
16
  if (ZCG(accel_directives).interned_strings_buffer) {
3078
16
    accel_shared_globals_size = sizeof(zend_accel_shared_globals) + ZCG(accel_directives).interned_strings_buffer * 1024 * 1024;
3079
16
  } else {
3080
    /* Make sure there is always at least one interned string hash slot,
3081
     * so the table can be queried unconditionally. */
3082
0
    accel_shared_globals_size = sizeof(zend_accel_shared_globals) + sizeof(zend_string_table_pos_t);
3083
0
  }
3084
3085
16
  accel_shared_globals = zend_shared_alloc(accel_shared_globals_size);
3086
16
  if (!accel_shared_globals) {
3087
0
    zend_shared_alloc_unlock();
3088
0
    zend_accel_error_noreturn(ACCEL_LOG_FATAL,
3089
0
        "Insufficient shared memory for interned strings buffer! (tried to allocate %zu bytes)",
3090
0
        accel_shared_globals_size);
3091
0
    return FAILURE;
3092
0
  }
3093
16
  memset(accel_shared_globals, 0, sizeof(zend_accel_shared_globals));
3094
16
  ZSMMG(app_shared_globals) = accel_shared_globals;
3095
3096
16
  zend_accel_hash_init(&ZCSG(hash), ZCG(accel_directives).max_accelerated_files);
3097
3098
16
  if (ZCG(accel_directives).interned_strings_buffer) {
3099
16
    uint32_t hash_size;
3100
3101
    /* must be a power of two */
3102
16
    hash_size = ZCG(accel_directives).interned_strings_buffer * (32 * 1024);
3103
16
    hash_size |= (hash_size >> 1);
3104
16
    hash_size |= (hash_size >> 2);
3105
16
    hash_size |= (hash_size >> 4);
3106
16
    hash_size |= (hash_size >> 8);
3107
16
    hash_size |= (hash_size >> 16);
3108
3109
16
    ZCSG(interned_strings).nTableMask =
3110
16
      hash_size * sizeof(zend_string_table_pos_t);
3111
16
    ZCSG(interned_strings).nNumOfElements = 0;
3112
16
    ZCSG(interned_strings).start =
3113
16
      (zend_string*)((char*)&ZCSG(interned_strings) +
3114
16
        sizeof(zend_string_table) +
3115
16
        ((hash_size + 1) * sizeof(zend_string_table_pos_t))) +
3116
16
        8;
3117
16
    ZEND_ASSERT(((uintptr_t)ZCSG(interned_strings).start & 0x7) == 0); /* should be 8 byte aligned */
3118
3119
16
    ZCSG(interned_strings).top =
3120
16
      ZCSG(interned_strings).start;
3121
16
    ZCSG(interned_strings).end =
3122
16
      (zend_string*)((char*)(accel_shared_globals + 1) + /* table data is stored after accel_shared_globals */
3123
16
        ZCG(accel_directives).interned_strings_buffer * 1024 * 1024);
3124
16
    ZEND_ASSERT(((uintptr_t)ZCSG(interned_strings).end - (uintptr_t)&ZCSG(interned_strings)) / ZEND_STRING_TABLE_POS_ALIGNMENT < ZEND_STRING_TABLE_POS_MAX);
3125
16
    ZCSG(interned_strings).saved_top = NULL;
3126
3127
16
    memset((char*)&ZCSG(interned_strings) + sizeof(zend_string_table),
3128
16
      STRTAB_INVALID_POS,
3129
16
      (char*)ZCSG(interned_strings).start -
3130
16
        ((char*)&ZCSG(interned_strings) + sizeof(zend_string_table)));
3131
16
  } else {
3132
0
    *STRTAB_HASH_TO_SLOT(&ZCSG(interned_strings), 0) = STRTAB_INVALID_POS;
3133
0
  }
3134
3135
  /* We can reuse init_interned_string_for_php for the "init_existing_interned" case,
3136
   * because the function does not create new interned strings at runtime. */
3137
16
  zend_interned_strings_set_request_storage_handlers(
3138
16
    accel_new_interned_string_for_php,
3139
16
    accel_init_interned_string_for_php,
3140
16
    accel_init_interned_string_for_php);
3141
3142
16
  zend_reset_cache_vars();
3143
3144
16
  ZCSG(oom_restarts) = 0;
3145
16
  ZCSG(hash_restarts) = 0;
3146
16
  ZCSG(manual_restarts) = 0;
3147
3148
16
  ZCSG(accelerator_enabled) = true;
3149
16
  ZCSG(start_time) = zend_accel_get_time();
3150
16
  ZCSG(last_restart_time) = 0;
3151
16
  ZCSG(restart_in_progress) = false;
3152
3153
48
  for (i = 0; i < -HT_MIN_MASK; i++) {
3154
32
    ZCSG(uninitialized_bucket)[i] = HT_INVALID_IDX;
3155
32
  }
3156
3157
16
  zend_shared_alloc_unlock();
3158
3159
16
  return SUCCESS;
3160
16
}
3161
3162
static void accel_globals_ctor(zend_accel_globals *accel_globals)
3163
16
{
3164
16
  memset(accel_globals, 0, sizeof(zend_accel_globals));
3165
16
  accel_globals->key = zend_string_alloc(ZCG_KEY_LEN, true);
3166
16
  GC_MAKE_PERSISTENT_LOCAL(accel_globals->key);
3167
16
}
3168
3169
static void accel_globals_dtor(zend_accel_globals *accel_globals)
3170
0
{
3171
0
  zend_string_free(accel_globals->key);
3172
0
  if (accel_globals->preloaded_internal_run_time_cache) {
3173
0
    pefree(accel_globals->preloaded_internal_run_time_cache, 1);
3174
0
  }
3175
0
}
3176
3177
#ifdef HAVE_HUGE_CODE_PAGES
3178
# ifndef _WIN32
3179
#  include <sys/mman.h>
3180
#  ifndef MAP_ANON
3181
#   ifdef MAP_ANONYMOUS
3182
#    define MAP_ANON MAP_ANONYMOUS
3183
#   endif
3184
#  endif
3185
#  ifndef MAP_FAILED
3186
#   define MAP_FAILED ((void*)-1)
3187
#  endif
3188
#  ifdef MAP_ALIGNED_SUPER
3189
#   include <sys/types.h>
3190
#   include <sys/sysctl.h>
3191
#   include <sys/user.h>
3192
#   define MAP_HUGETLB MAP_ALIGNED_SUPER
3193
#  endif
3194
#  if __has_include(<link.h>)
3195
#   include <link.h>
3196
#  endif
3197
#  if __has_include(<elf.h>)
3198
#   include <elf.h>
3199
#  endif
3200
# endif
3201
3202
0
# define ZEND_HUGE_PAGE_SIZE (2UL * 1024 * 1024)
3203
3204
# 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>)
3205
static zend_result
3206
__attribute__((section(".remap_stub")))
3207
__attribute__((aligned(ZEND_HUGE_PAGE_SIZE)))
3208
zend_never_inline
3209
accel_remap_huge_pages(void *start, size_t size, size_t real_size)
3210
0
{
3211
0
  void *ret = MAP_FAILED;
3212
0
  void *mem;
3213
3214
0
  mem = mmap(NULL, size,
3215
0
    PROT_READ | PROT_WRITE,
3216
0
    MAP_PRIVATE | MAP_ANONYMOUS,
3217
0
    -1, 0);
3218
0
  if (mem == MAP_FAILED) {
3219
0
    zend_error(E_WARNING,
3220
0
      ACCELERATOR_PRODUCT_NAME " huge_code_pages: mmap failed: %s (%d)",
3221
0
      strerror(errno), errno);
3222
0
    return FAILURE;
3223
0
  }
3224
0
  memcpy(mem, start, real_size);
3225
3226
0
#  ifdef MAP_HUGETLB
3227
0
  ret = mmap(start, size,
3228
0
    PROT_READ | PROT_WRITE | PROT_EXEC,
3229
0
    MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_HUGETLB,
3230
0
    -1, 0);
3231
0
#  endif
3232
0
  if (ret == MAP_FAILED) {
3233
0
    ret = mmap(start, size,
3234
0
      PROT_READ | PROT_WRITE | PROT_EXEC,
3235
0
      MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
3236
0
      -1, 0);
3237
    /* this should never happen? */
3238
0
    ZEND_ASSERT(ret != MAP_FAILED);
3239
0
#  ifdef MADV_HUGEPAGE
3240
0
    if (-1 == madvise(start, size, MADV_HUGEPAGE)) {
3241
0
      memcpy(start, mem, real_size);
3242
0
      mprotect(start, size, PROT_READ | PROT_EXEC);
3243
0
      munmap(mem, size);
3244
0
      zend_error(E_WARNING,
3245
0
        ACCELERATOR_PRODUCT_NAME " huge_code_pages: madvise(HUGEPAGE) failed: %s (%d)",
3246
0
        strerror(errno), errno);
3247
0
      return FAILURE;
3248
0
    }
3249
#  else
3250
    memcpy(start, mem, real_size);
3251
    mprotect(start, size, PROT_READ | PROT_EXEC);
3252
    munmap(mem, size);
3253
    zend_error(E_WARNING,
3254
      ACCELERATOR_PRODUCT_NAME " huge_code_pages: mmap(HUGETLB) failed: %s (%d)",
3255
      strerror(errno), errno);
3256
    return FAILURE;
3257
#  endif
3258
0
  }
3259
3260
  // Given the MAP_FIXED flag the address can never diverge
3261
0
  ZEND_ASSERT(ret == start);
3262
3263
0
  memcpy(start, mem, real_size);
3264
0
  mprotect(start, size, PROT_READ | PROT_EXEC);
3265
0
  zend_mmap_set_name(start, size, "zend_huge_code_pages");
3266
3267
0
  munmap(mem, size);
3268
3269
0
  return SUCCESS;
3270
0
}
3271
3272
0
static int accel_dl_iterate_phdr_callback(struct dl_phdr_info *info, size_t size, void *data) {
3273
0
  if (info->dlpi_name == NULL || strcmp(info->dlpi_name, "") == 0) {
3274
0
    *((uintptr_t*)data) = info->dlpi_addr;
3275
0
    return 1;
3276
0
  }
3277
0
  return 0;
3278
0
}
3279
3280
0
static zend_result accel_find_program_section(ElfW(Shdr) *section) {
3281
3282
0
  uintptr_t base_addr;
3283
0
  if (dl_iterate_phdr(accel_dl_iterate_phdr_callback, &base_addr) != 1) {
3284
0
    zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: executable base address not found");
3285
0
    return FAILURE;
3286
0
  }
3287
3288
0
#if defined(__linux__)
3289
0
  FILE *f = fopen("/proc/self/exe", "r");
3290
#elif defined(__FreeBSD__)
3291
  char path[4096];
3292
  int mib[4];
3293
  size_t len = sizeof(path);
3294
3295
  mib[0] = CTL_KERN;
3296
  mib[1] = KERN_PROC;
3297
  mib[2] = KERN_PROC_PATHNAME;
3298
  mib[3] = -1; /* Current process */
3299
3300
  if (sysctl(mib, 4, path, &len, NULL, 0) == -1) {
3301
    zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: sysctl(KERN_PROC_PATHNAME) failed: %s (%d)",
3302
        strerror(errno), errno);
3303
    return FAILURE;
3304
  }
3305
3306
  FILE *f = fopen(path, "r");
3307
#endif
3308
0
  if (!f) {
3309
0
    zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: fopen(/proc/self/exe) failed: %s (%d)",
3310
0
        strerror(errno), errno);
3311
0
    return FAILURE;
3312
0
  }
3313
3314
  /* Read ELF header */
3315
0
  ElfW(Ehdr) ehdr;
3316
0
  if (!fread(&ehdr, sizeof(ehdr), 1, f)) {
3317
0
    zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: fread() failed: %s (%d)",
3318
0
        strerror(errno), errno);
3319
0
    fclose(f);
3320
0
    return FAILURE;
3321
0
  }
3322
3323
  /* Read section headers */
3324
0
  ElfW(Shdr) shdrs[ehdr.e_shnum];
3325
0
  if (fseek(f, ehdr.e_shoff, SEEK_SET) != 0) {
3326
0
    zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: fseek() failed: %s (%d)",
3327
0
        strerror(errno), errno);
3328
0
    fclose(f);
3329
0
    return FAILURE;
3330
0
  }
3331
0
  if (fread(shdrs, sizeof(shdrs[0]), ehdr.e_shnum, f) != ehdr.e_shnum) {
3332
0
    zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: fread() failed: %s (%d)",
3333
0
        strerror(errno), errno);
3334
0
    fclose(f);
3335
0
    return FAILURE;
3336
0
  }
3337
3338
0
  fclose(f);
3339
3340
  /* Find the program section */
3341
0
  for (ElfW(Half) idx = 0; idx < ehdr.e_shnum; idx++) {
3342
0
    ElfW(Shdr) *sh = &shdrs[idx];
3343
0
    uintptr_t start = (uintptr_t)sh->sh_addr + base_addr;
3344
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);
3345
0
    if ((uintptr_t)accel_find_program_section >= start && (uintptr_t)accel_find_program_section < start + sh->sh_size) {
3346
0
      *section = *sh;
3347
0
      section->sh_addr = (ElfW(Addr))start;
3348
0
      return SUCCESS;
3349
0
    }
3350
0
  }
3351
3352
0
  return FAILURE;
3353
0
}
3354
3355
static void accel_move_code_to_huge_pages(void)
3356
0
{
3357
0
  ElfW(Shdr) section;
3358
0
  if (accel_find_program_section(&section) == FAILURE) {
3359
0
    zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages: program section not found");
3360
0
    return;
3361
0
  }
3362
3363
0
  uintptr_t start = ZEND_MM_ALIGNED_SIZE_EX(section.sh_addr, ZEND_HUGE_PAGE_SIZE);
3364
0
  uintptr_t end = (section.sh_addr + section.sh_size) & ~(ZEND_HUGE_PAGE_SIZE-1UL);
3365
0
  if (end > start) {
3366
0
    zend_accel_error(ACCEL_LOG_DEBUG, "remap to huge page %" PRIxPTR "-%" PRIxPTR "\n", start, end);
3367
0
    accel_remap_huge_pages((void*)start, end - start, end - start);
3368
0
  }
3369
0
}
3370
# else
3371
static void accel_move_code_to_huge_pages(void)
3372
{
3373
  zend_error(E_WARNING, ACCELERATOR_PRODUCT_NAME ": opcache.huge_code_pages has no affect as huge page is not supported");
3374
  return;
3375
}
3376
# endif /* defined(MAP_HUGETLB) || defined(MADV_HUGEPAGE) */
3377
#endif /* HAVE_HUGE_CODE_PAGES */
3378
3379
void start_accel_extension(void)
3380
16
{
3381
16
  zend_register_extension(&opcache_extension_entry, NULL);
3382
16
}
3383
3384
static int accel_startup(zend_extension *extension)
3385
16
{
3386
#ifdef ZTS
3387
  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);
3388
#else
3389
16
  accel_globals_ctor(&accel_globals);
3390
16
#endif
3391
3392
16
#ifdef HAVE_JIT
3393
16
  zend_jit_init();
3394
16
#endif
3395
3396
#ifdef ZEND_WIN32
3397
# if !defined(__has_feature) || !__has_feature(address_sanitizer)
3398
  _setmaxstdio(2048); /* The default configuration is limited to 512 stdio files */
3399
# endif
3400
#endif
3401
3402
16
  zend_accel_register_ini_entries();
3403
3404
#ifdef ZEND_WIN32
3405
  if (UNEXPECTED(accel_gen_uname_id() == FAILURE)) {
3406
    zps_startup_failure("Unable to get user name", NULL, accelerator_remove_cb);
3407
    return SUCCESS;
3408
  }
3409
#endif
3410
3411
16
#ifdef HAVE_HUGE_CODE_PAGES
3412
16
  if (ZCG(accel_directives).huge_code_pages &&
3413
0
      (strcmp(sapi_module.name, "cli") == 0 ||
3414
0
       strcmp(sapi_module.name, "cli-server") == 0 ||
3415
0
     strcmp(sapi_module.name, "cgi-fcgi") == 0 ||
3416
0
     strcmp(sapi_module.name, "fpm-fcgi") == 0)) {
3417
0
    accel_move_code_to_huge_pages();
3418
0
  }
3419
16
#endif
3420
3421
16
  if (!ZCG(accel_directives).enable_cli && accel_sapi_is_cli()) {
3422
0
    accel_startup_ok = false;
3423
0
    zps_startup_failure("Opcode Caching is disabled for CLI", NULL, accelerator_remove_cb);
3424
0
    return SUCCESS;
3425
0
  }
3426
3427
16
  if (ZCG(enabled) == 0) {
3428
0
    return SUCCESS ;
3429
0
  }
3430
3431
16
  orig_post_startup_cb = zend_post_startup_cb;
3432
16
  zend_post_startup_cb = accel_post_startup;
3433
3434
  /* Prevent unloading */
3435
16
  extension->handle = 0;
3436
3437
16
  return SUCCESS;
3438
16
}
3439
3440
static zend_result accel_post_startup(void)
3441
16
{
3442
16
  zend_function *func;
3443
16
  zend_ini_entry *ini_entry;
3444
3445
16
  if (orig_post_startup_cb) {
3446
0
    zend_result (*cb)(void) = orig_post_startup_cb;
3447
3448
0
    orig_post_startup_cb = NULL;
3449
0
    if (cb() != SUCCESS) {
3450
0
      return FAILURE;
3451
0
    }
3452
0
  }
3453
3454
/********************************************/
3455
/* End of non-SHM dependent initializations */
3456
/********************************************/
3457
16
  file_cache_only = ZCG(accel_directives).file_cache_only;
3458
16
  if (!file_cache_only) {
3459
16
    size_t shm_size = ZCG(accel_directives).memory_consumption;
3460
16
    size_t jit_size = 0;
3461
16
#ifdef HAVE_JIT
3462
16
    size_t jit_buffer_size = 0;
3463
16
    bool reattached = false;
3464
3465
16
    if (JIT_G(enabled) && JIT_G(buffer_size)
3466
0
     && zend_jit_check_support() == SUCCESS) {
3467
0
      size_t page_size;
3468
3469
0
      page_size = zend_get_page_size();
3470
0
      if (!page_size || (page_size & (page_size - 1))) {
3471
0
        zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - can't get page size.");
3472
0
        abort();
3473
0
      }
3474
0
      jit_buffer_size = JIT_G(buffer_size);
3475
0
      jit_buffer_size = ZEND_MM_ALIGNED_SIZE_EX(jit_buffer_size, page_size);
3476
0
# ifndef ZEND_JIT_USE_APPLE_MAP_JIT
3477
0
      jit_size = jit_buffer_size;
3478
0
      shm_size += jit_size;
3479
0
# endif
3480
0
    }
3481
16
#endif
3482
3483
16
    switch (zend_shared_alloc_startup(shm_size, jit_size)) {
3484
16
      case ALLOC_SUCCESS:
3485
16
        if (zend_accel_init_shm() == FAILURE) {
3486
0
          accel_startup_ok = false;
3487
0
          return FAILURE;
3488
0
        }
3489
16
        break;
3490
16
      case ALLOC_FAILURE:
3491
0
        accel_startup_ok = false;
3492
0
        zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - probably not enough shared memory.");
3493
0
        return SUCCESS;
3494
0
      case NO_SHM_BACKEND:
3495
0
        zend_accel_error(ACCEL_LOG_INFO, "Opcode Caching is disabled: No available SHM backend. Set opcache.enable=0 to hide this message.");
3496
0
        zps_startup_failure("No available SHM backend", NULL, accelerator_remove_cb);
3497
        /* Do not abort PHP startup */
3498
0
        return SUCCESS;
3499
3500
0
      case SUCCESSFULLY_REATTACHED:
3501
0
#ifdef HAVE_JIT
3502
0
        reattached = true;
3503
0
#endif
3504
0
        zend_shared_alloc_lock();
3505
0
        accel_shared_globals = (zend_accel_shared_globals *) ZSMMG(app_shared_globals);
3506
0
        zend_interned_strings_set_request_storage_handlers(
3507
0
          accel_new_interned_string_for_php,
3508
0
          accel_init_interned_string_for_php,
3509
0
          accel_init_interned_string_for_php);
3510
0
        zend_shared_alloc_unlock();
3511
0
        break;
3512
0
      case FAILED_REATTACHED:
3513
0
        accel_startup_ok = false;
3514
0
        zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Failure to initialize shared memory structures - cannot reattach to exiting shared memory.");
3515
0
        return SUCCESS;
3516
#if ENABLE_FILE_CACHE_FALLBACK
3517
      case ALLOC_FALLBACK:
3518
        zend_shared_alloc_lock();
3519
        file_cache_only = true;
3520
        fallback_process = true;
3521
        zend_shared_alloc_unlock();
3522
        goto file_cache_fallback;
3523
#endif
3524
16
    }
3525
3526
    /* from this point further, shared memory is supposed to be OK */
3527
3528
    /* remember the last restart time in the process memory */
3529
16
    ZCG(last_restart_time) = ZCSG(last_restart_time);
3530
3531
16
    zend_shared_alloc_lock();
3532
16
#ifdef HAVE_JIT
3533
16
    if (JIT_G(enabled)) {
3534
0
      if (JIT_G(buffer_size) == 0) {
3535
0
        JIT_G(enabled) = false;
3536
0
        JIT_G(on) = false;
3537
0
      } else {
3538
# ifdef ZEND_JIT_USE_APPLE_MAP_JIT
3539
        zend_jit_startup(NULL, jit_buffer_size, reattached);
3540
# else
3541
0
        if (!ZSMMG(reserved)) {
3542
0
          zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Could not enable JIT: could not use reserved buffer!");
3543
0
        }
3544
0
        zend_jit_startup(ZSMMG(reserved), jit_buffer_size, reattached);
3545
0
# endif
3546
0
        zend_jit_startup_ok = true;
3547
0
      }
3548
0
    }
3549
16
#endif
3550
16
    zend_shared_alloc_save_state();
3551
16
    zend_shared_alloc_unlock();
3552
3553
16
    SHM_PROTECT();
3554
16
  } else if (!ZCG(accel_directives).file_cache) {
3555
0
    accel_startup_ok = false;
3556
0
    zend_accel_error_noreturn(ACCEL_LOG_FATAL, "opcache.file_cache_only is set without a proper setting of opcache.file_cache");
3557
0
    return SUCCESS;
3558
0
  } else {
3559
0
#ifdef HAVE_JIT
3560
0
    JIT_G(enabled) = false;
3561
0
    JIT_G(on) = false;
3562
0
#endif
3563
0
    accel_shared_globals = calloc(1, sizeof(zend_accel_shared_globals));
3564
0
  }
3565
3566
  /* opcache.file_cache_read_only should only be enabled when all script files are read-only */
3567
16
  int file_cache_access_mode = 0;
3568
3569
16
  if (ZCG(accel_directives).file_cache_read_only) {
3570
0
    zend_accel_error(ACCEL_LOG_INFO, "opcache.file_cache is in read-only mode");
3571
3572
0
    if (!ZCG(accel_directives).file_cache) {
3573
0
      accel_startup_ok = false;
3574
0
      zend_accel_error_noreturn(ACCEL_LOG_FATAL, "opcache.file_cache_read_only is set without a proper setting of opcache.file_cache");
3575
0
      return SUCCESS;
3576
0
    }
3577
3578
    /* opcache.file_cache is read only, so ensure the directory is readable */
3579
0
#ifndef ZEND_WIN32
3580
0
    file_cache_access_mode = R_OK | X_OK;
3581
#else
3582
    file_cache_access_mode = 04; // Read access
3583
#endif
3584
16
  } else {
3585
    /* opcache.file_cache isn't read only, so ensure the directory is writable */
3586
16
#ifndef ZEND_WIN32
3587
16
    file_cache_access_mode = R_OK | W_OK | X_OK;
3588
#else
3589
    file_cache_access_mode = 06; // Read and write access
3590
#endif
3591
16
  }
3592
3593
16
  if ( ZCG(accel_directives).file_cache ) {
3594
0
    zend_accel_error(ACCEL_LOG_INFO, "opcache.file_cache running with PHP build ID: %.32s", zend_system_id);
3595
3596
0
    zend_stat_t buf = {0};
3597
3598
0
    if (!IS_ABSOLUTE_PATH(ZCG(accel_directives).file_cache, strlen(ZCG(accel_directives).file_cache)) ||
3599
0
      zend_stat(ZCG(accel_directives).file_cache, &buf) != 0 ||
3600
0
      !S_ISDIR(buf.st_mode) ||
3601
0
#ifndef ZEND_WIN32
3602
0
      access(ZCG(accel_directives).file_cache, file_cache_access_mode) != 0
3603
#else
3604
      _access(ZCG(accel_directives).file_cache, file_cache_access_mode) != 0
3605
#endif
3606
0
      ) {
3607
0
      accel_startup_ok = false;
3608
0
      zend_accel_error_noreturn(ACCEL_LOG_FATAL, "opcache.file_cache must be a full path of an accessible directory");
3609
0
      return SUCCESS;
3610
0
    }
3611
0
  }
3612
3613
#if ENABLE_FILE_CACHE_FALLBACK
3614
file_cache_fallback:
3615
#endif
3616
3617
  /* Override compiler */
3618
16
  accelerator_orig_compile_file = zend_compile_file;
3619
16
  zend_compile_file = persistent_compile_file;
3620
3621
  /* Override stream opener function (to eliminate open() call caused by
3622
   * include/require statements ) */
3623
16
  accelerator_orig_zend_stream_open_function = zend_stream_open_function;
3624
16
  zend_stream_open_function = persistent_stream_open_function;
3625
3626
  /* Override path resolver function (to eliminate stat() calls caused by
3627
   * include_once/require_once statements */
3628
16
  accelerator_orig_zend_resolve_path = zend_resolve_path;
3629
16
  zend_resolve_path = persistent_zend_resolve_path;
3630
3631
  /* Override chdir() function */
3632
16
  if ((func = zend_hash_str_find_ptr(CG(function_table), "chdir", sizeof("chdir")-1)) != NULL &&
3633
0
      func->type == ZEND_INTERNAL_FUNCTION) {
3634
0
    orig_chdir = func->internal_function.handler;
3635
0
    func->internal_function.handler = ZEND_FN(accel_chdir);
3636
0
  }
3637
16
  ZCG(cwd) = NULL;
3638
16
  ZCG(include_path) = NULL;
3639
3640
  /* Override "include_path" modifier callback */
3641
16
  if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) {
3642
16
    ZCG(include_path) = ini_entry->value;
3643
16
    orig_include_path_on_modify = ini_entry->on_modify;
3644
16
    ini_entry->on_modify = accel_include_path_on_modify;
3645
16
  }
3646
3647
16
  accel_startup_ok = true;
3648
3649
  /* Override file_exists(), is_file() and is_readable() */
3650
16
  zend_accel_override_file_functions();
3651
3652
  /* Load black list */
3653
16
  accel_blacklist.entries = NULL;
3654
16
  if (ZCG(enabled) && accel_startup_ok &&
3655
16
      ZCG(accel_directives).user_blacklist_filename &&
3656
16
      *ZCG(accel_directives.user_blacklist_filename)) {
3657
0
    zend_accel_blacklist_init(&accel_blacklist);
3658
0
    zend_accel_blacklist_load(&accel_blacklist, ZCG(accel_directives.user_blacklist_filename));
3659
0
  }
3660
3661
16
  if (!file_cache_only && ZCG(accel_directives).interned_strings_buffer) {
3662
16
    accel_use_shm_interned_strings();
3663
16
  }
3664
3665
16
  if (accel_finish_startup() != SUCCESS) {
3666
0
    return FAILURE;
3667
0
  }
3668
3669
16
  if (ZCG(enabled) && accel_startup_ok) {
3670
    /* Override inheritance cache callbaks */
3671
16
    accelerator_orig_inheritance_cache_get = zend_inheritance_cache_get;
3672
16
    accelerator_orig_inheritance_cache_add = zend_inheritance_cache_add;
3673
16
    zend_inheritance_cache_get = zend_accel_inheritance_cache_get;
3674
16
    zend_inheritance_cache_add = zend_accel_inheritance_cache_add;
3675
16
  }
3676
3677
16
  return SUCCESS;
3678
16
}
3679
3680
static void (*orig_post_shutdown_cb)(void);
3681
3682
static void accel_post_shutdown(void)
3683
0
{
3684
0
  zend_shared_alloc_shutdown();
3685
0
}
3686
3687
void accel_shutdown(void)
3688
0
{
3689
0
  zend_ini_entry *ini_entry;
3690
0
  bool _file_cache_only = false;
3691
3692
0
#ifdef HAVE_JIT
3693
0
  zend_jit_shutdown();
3694
0
#endif
3695
3696
0
  zend_accel_blacklist_shutdown(&accel_blacklist);
3697
3698
0
  if (!ZCG(enabled) || !accel_startup_ok) {
3699
#ifdef ZTS
3700
    ts_free_id(accel_globals_id);
3701
#else
3702
0
    accel_globals_dtor(&accel_globals);
3703
0
#endif
3704
0
    return;
3705
0
  }
3706
3707
0
#ifdef PRELOAD_SUPPORT
3708
0
  if (ZCSG(preload_script)) {
3709
0
    preload_shutdown();
3710
0
  }
3711
0
#endif
3712
3713
0
  _file_cache_only = file_cache_only;
3714
3715
0
  accel_reset_pcre_cache();
3716
3717
#ifdef ZTS
3718
  ts_free_id(accel_globals_id);
3719
#else
3720
0
  accel_globals_dtor(&accel_globals);
3721
0
#endif
3722
3723
0
  if (!_file_cache_only) {
3724
    /* Delay SHM detach */
3725
0
    orig_post_shutdown_cb = zend_post_shutdown_cb;
3726
0
    zend_post_shutdown_cb = accel_post_shutdown;
3727
0
  } else {
3728
0
    free(accel_shared_globals);
3729
0
  }
3730
3731
0
  zend_compile_file = accelerator_orig_compile_file;
3732
0
  zend_inheritance_cache_get = accelerator_orig_inheritance_cache_get;
3733
0
  zend_inheritance_cache_add = accelerator_orig_inheritance_cache_add;
3734
3735
0
  if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), "include_path", sizeof("include_path")-1)) != NULL) {
3736
0
    ini_entry->on_modify = orig_include_path_on_modify;
3737
0
  }
3738
3739
0
  accel_startup_ok = false;
3740
0
}
3741
3742
void zend_accel_schedule_restart(zend_accel_restart_reason reason)
3743
0
{
3744
0
  const char *zend_accel_restart_reason_text[ACCEL_RESTART_USER + 1] = {
3745
0
    "out of memory",
3746
0
    "hash overflow",
3747
0
    "user",
3748
0
  };
3749
3750
0
  if (ZCSG(restart_pending)) {
3751
    /* don't schedule twice */
3752
0
    return;
3753
0
  }
3754
3755
0
  if (UNEXPECTED(zend_accel_schedule_restart_hook)) {
3756
0
    zend_accel_schedule_restart_hook(reason);
3757
0
  }
3758
3759
0
  zend_accel_error(ACCEL_LOG_DEBUG, "Restart Scheduled! Reason: %s",
3760
0
      zend_accel_restart_reason_text[reason]);
3761
3762
0
  HANDLE_BLOCK_INTERRUPTIONS();
3763
0
  SHM_UNPROTECT();
3764
0
  ZCSG(restart_pending) = true;
3765
0
  ZCSG(restart_reason) = reason;
3766
0
  ZCSG(cache_status_before_restart) = ZCSG(accelerator_enabled);
3767
0
  ZCSG(accelerator_enabled) = false;
3768
3769
0
  if (ZCG(accel_directives).force_restart_timeout) {
3770
0
    ZCSG(force_restart_time) = zend_accel_get_time() + ZCG(accel_directives).force_restart_timeout;
3771
0
  } else {
3772
0
    ZCSG(force_restart_time) = 0;
3773
0
  }
3774
0
  SHM_PROTECT();
3775
0
  HANDLE_UNBLOCK_INTERRUPTIONS();
3776
0
}
3777
3778
static void accel_deactivate_now(void)
3779
0
{
3780
  /* this is needed because on WIN32 lock is not decreased unless ZCG(counted) is set */
3781
#ifdef ZEND_WIN32
3782
  ZCG(counted) = true;
3783
#endif
3784
0
  accel_deactivate_sub();
3785
0
}
3786
3787
/* ensures it is OK to read SHM
3788
  if it's not OK (restart in progress) returns FAILURE
3789
  if OK returns SUCCESS
3790
  MUST call accelerator_shm_read_unlock after done lock operations
3791
*/
3792
zend_result accelerator_shm_read_lock(void)
3793
75.0k
{
3794
75.0k
  if (ZCG(counted)) {
3795
    /* counted means we are holding read lock for SHM, so that nothing bad can happen */
3796
75.0k
    return SUCCESS;
3797
75.0k
  } else {
3798
    /* here accelerator is active but we do not hold SHM lock. This means restart was scheduled
3799
      or is in progress now */
3800
0
    if (accel_activate_add() == FAILURE) { /* acquire usage lock */
3801
0
      return FAILURE;
3802
0
    }
3803
    /* Now if we weren't inside restart, restart would not begin until we remove usage lock */
3804
0
    if (ZCSG(restart_in_progress)) {
3805
      /* we already were inside restart this means it's not safe to touch shm */
3806
0
      accel_deactivate_now(); /* drop usage lock */
3807
0
      return FAILURE;
3808
0
    }
3809
0
    ZCG(counted) = true;
3810
0
  }
3811
0
  return SUCCESS;
3812
75.0k
}
3813
3814
/* must be called ONLY after SUCCESSFUL accelerator_shm_read_lock */
3815
void accelerator_shm_read_unlock(void)
3816
75.0k
{
3817
75.0k
  if (!ZCG(counted)) {
3818
    /* counted is false - meaning we had to readlock manually, release readlock now */
3819
0
    accel_deactivate_now();
3820
0
  }
3821
75.0k
}
3822
3823
/* Preloading */
3824
#ifdef PRELOAD_SUPPORT
3825
static HashTable *preload_scripts = NULL;
3826
static zend_op_array *(*preload_orig_compile_file)(zend_file_handle *file_handle, int type);
3827
3828
static void preload_shutdown(void)
3829
0
{
3830
0
  zval *zv;
3831
3832
#if 0
3833
  if (EG(zend_constants)) {
3834
    ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(zend_constants), zv) {
3835
      zend_constant *c = Z_PTR_P(zv);
3836
      if (ZEND_CONSTANT_FLAGS(c) & CONST_PERSISTENT) {
3837
        break;
3838
      }
3839
    } ZEND_HASH_MAP_FOREACH_END_DEL();
3840
  }
3841
#endif
3842
3843
0
  if (EG(function_table)) {
3844
0
    ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(function_table), zv) {
3845
0
      const zend_function *func = Z_PTR_P(zv);
3846
0
      if (func->type == ZEND_INTERNAL_FUNCTION) {
3847
0
        break;
3848
0
      }
3849
0
    } ZEND_HASH_MAP_FOREACH_END_DEL();
3850
0
  }
3851
3852
0
  if (EG(class_table)) {
3853
0
    ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(class_table), zv) {
3854
0
      const zend_class_entry *ce = Z_PTR_P(zv);
3855
0
      if (ce->type == ZEND_INTERNAL_CLASS && Z_TYPE_P(zv) != IS_ALIAS_PTR) {
3856
0
        break;
3857
0
      }
3858
0
    } ZEND_HASH_MAP_FOREACH_END_DEL();
3859
0
  }
3860
0
}
3861
3862
static void preload_activate(void)
3863
0
{
3864
0
  if (ZCSG(preload_script)->ping_auto_globals_mask & ~ZCG(auto_globals_mask)) {
3865
0
    zend_accel_set_auto_globals(ZCSG(preload_script)->ping_auto_globals_mask & ~ZCG(auto_globals_mask));
3866
0
  }
3867
0
}
3868
3869
static void preload_restart(void)
3870
0
{
3871
0
  zend_accel_hash_update(&ZCSG(hash), ZCSG(preload_script)->script.filename, 0, ZCSG(preload_script));
3872
0
  if (ZCSG(saved_scripts)) {
3873
0
    zend_persistent_script **p = ZCSG(saved_scripts);
3874
0
    while (*p) {
3875
0
      zend_accel_hash_update(&ZCSG(hash), (*p)->script.filename, 0, *p);
3876
0
      p++;
3877
0
    }
3878
0
  }
3879
0
}
3880
3881
0
static size_t preload_try_strip_filename(const zend_string *filename) {
3882
  /*FIXME: better way to handle eval()'d code? see COMPILED_STRING_DESCRIPTION_FORMAT */
3883
0
  if (ZSTR_LEN(filename) > sizeof(" eval()'d code")
3884
0
    && *(ZSTR_VAL(filename) + ZSTR_LEN(filename) - sizeof(" eval()'d code")) == ':') {
3885
0
    const char *cfilename = ZSTR_VAL(filename);
3886
0
    size_t cfilenamelen = ZSTR_LEN(filename) - sizeof(" eval()'d code") - 1 /*:*/;
3887
0
    while (cfilenamelen && cfilename[--cfilenamelen] != '(');
3888
0
    return cfilenamelen;
3889
0
  }
3890
0
  return 0;
3891
0
}
3892
3893
static void preload_move_user_functions(HashTable *src, HashTable *dst)
3894
0
{
3895
0
  Bucket *p;
3896
0
  dtor_func_t orig_dtor = src->pDestructor;
3897
0
  zend_string *filename = NULL;
3898
0
  bool copy = false;
3899
3900
0
  src->pDestructor = NULL;
3901
0
  zend_hash_extend(dst, dst->nNumUsed + src->nNumUsed, 0);
3902
0
  ZEND_HASH_MAP_REVERSE_FOREACH_BUCKET(src, p) {
3903
0
    zend_function *function = Z_PTR(p->val);
3904
3905
0
    if (EXPECTED(function->type == ZEND_USER_FUNCTION)) {
3906
0
      if (function->op_array.filename != filename) {
3907
0
        filename = function->op_array.filename;
3908
0
        if (filename) {
3909
0
          if (!(copy = zend_hash_exists(preload_scripts, filename))) {
3910
0
            size_t eval_len = preload_try_strip_filename(filename);
3911
0
            if (eval_len) {
3912
0
              copy = zend_hash_str_exists(preload_scripts, ZSTR_VAL(filename), eval_len);
3913
0
            }
3914
0
          }
3915
0
        } else {
3916
0
          copy = false;
3917
0
        }
3918
0
      }
3919
0
      if (copy) {
3920
0
        _zend_hash_append_ptr(dst, p->key, function);
3921
0
      } else {
3922
0
        orig_dtor(&p->val);
3923
0
      }
3924
0
      zend_hash_del_bucket(src, p);
3925
0
    } else {
3926
0
      break;
3927
0
    }
3928
0
  } ZEND_HASH_FOREACH_END();
3929
0
  src->pDestructor = orig_dtor;
3930
0
}
3931
3932
static void preload_move_user_classes(HashTable *src, HashTable *dst)
3933
0
{
3934
0
  Bucket *p;
3935
0
  dtor_func_t orig_dtor = src->pDestructor;
3936
0
  zend_string *filename = NULL;
3937
0
  bool copy = false;
3938
3939
0
  src->pDestructor = NULL;
3940
0
  zend_hash_extend(dst, dst->nNumUsed + src->nNumUsed, 0);
3941
0
  ZEND_HASH_MAP_FOREACH_BUCKET_FROM(src, p, EG(persistent_classes_count)) {
3942
0
    zend_class_entry *ce = Z_PTR(p->val);
3943
3944
    /* Possible with internal class aliases */
3945
0
    if (ce->type == ZEND_INTERNAL_CLASS) {
3946
0
      ZEND_ASSERT(Z_TYPE(p->val) == IS_ALIAS_PTR);
3947
0
      _zend_hash_append(dst, p->key, &p->val);
3948
0
      zend_hash_del_bucket(src, p);
3949
0
      continue;
3950
0
    }
3951
3952
0
    if (ce->info.user.filename != filename) {
3953
0
      filename = ce->info.user.filename;
3954
0
      if (filename) {
3955
0
        if (!(copy = zend_hash_exists(preload_scripts, filename))) {
3956
0
          size_t eval_len = preload_try_strip_filename(filename);
3957
0
          if (eval_len) {
3958
0
            copy = zend_hash_str_exists(preload_scripts, ZSTR_VAL(filename), eval_len);
3959
0
          }
3960
0
        }
3961
0
      } else {
3962
0
        copy = false;
3963
0
      }
3964
0
    }
3965
0
    if (copy) {
3966
0
      _zend_hash_append(dst, p->key, &p->val);
3967
0
    } else {
3968
0
      orig_dtor(&p->val);
3969
0
    }
3970
0
    zend_hash_del_bucket(src, p);
3971
0
  } ZEND_HASH_FOREACH_END();
3972
0
  src->pDestructor = orig_dtor;
3973
0
}
3974
3975
static zend_op_array *preload_compile_file(zend_file_handle *file_handle, int type)
3976
0
{
3977
0
  zend_op_array *op_array = preload_orig_compile_file(file_handle, type);
3978
3979
0
  if (op_array && op_array->refcount) {
3980
0
    zend_persistent_script *script;
3981
3982
0
    script = create_persistent_script();
3983
0
    script->script.filename = zend_string_copy(op_array->filename);
3984
0
    zend_string_hash_val(script->script.filename);
3985
0
    script->script.main_op_array = *op_array;
3986
3987
//???   efree(op_array->refcount);
3988
0
    op_array->refcount = NULL;
3989
3990
0
    zend_hash_add_ptr(preload_scripts, script->script.filename, script);
3991
0
  }
3992
3993
0
  return op_array;
3994
0
}
3995
3996
static void preload_sort_classes(void *base, size_t count, size_t siz, compare_func_t compare, swap_func_t swp)
3997
0
{
3998
0
  Bucket *b1 = base;
3999
0
  Bucket *b2;
4000
0
  Bucket *end = b1 + count;
4001
0
  Bucket tmp;
4002
0
  const zend_class_entry *ce, *p;
4003
4004
0
  while (b1 < end) {
4005
0
try_again:
4006
0
    ce = Z_PTR(b1->val);
4007
0
    if (ce->parent && (ce->ce_flags & ZEND_ACC_LINKED)) {
4008
0
      p = ce->parent;
4009
0
      if (p->type == ZEND_USER_CLASS) {
4010
0
        b2 = b1 + 1;
4011
0
        while (b2 < end) {
4012
0
          if (p == Z_PTR(b2->val)) {
4013
0
            tmp = *b1;
4014
0
            *b1 = *b2;
4015
0
            *b2 = tmp;
4016
0
            goto try_again;
4017
0
          }
4018
0
          b2++;
4019
0
        }
4020
0
      }
4021
0
    }
4022
0
    if (ce->num_interfaces && (ce->ce_flags & ZEND_ACC_LINKED)) {
4023
0
      uint32_t i = 0;
4024
0
      for (i = 0; i < ce->num_interfaces; i++) {
4025
0
        p = ce->interfaces[i];
4026
0
        if (p->type == ZEND_USER_CLASS) {
4027
0
          b2 = b1 + 1;
4028
0
          while (b2 < end) {
4029
0
            if (p == Z_PTR(b2->val)) {
4030
0
              tmp = *b1;
4031
0
              *b1 = *b2;
4032
0
              *b2 = tmp;
4033
0
              goto try_again;
4034
0
            }
4035
0
            b2++;
4036
0
          }
4037
0
        }
4038
0
      }
4039
0
    }
4040
0
    b1++;
4041
0
  }
4042
0
}
4043
4044
typedef struct {
4045
  const char *kind;
4046
  const char *name;
4047
} preload_error;
4048
4049
static zend_result preload_resolve_deps(preload_error *error, const zend_class_entry *ce)
4050
0
{
4051
0
  memset(error, 0, sizeof(preload_error));
4052
4053
0
  if (ce->parent_name) {
4054
0
    const zend_class_entry *parent = zend_hash_find_ptr_lc(EG(class_table), ce->parent_name);
4055
0
    if (!parent) {
4056
0
      error->kind = "Unknown parent ";
4057
0
      error->name = ZSTR_VAL(ce->parent_name);
4058
0
      return FAILURE;
4059
0
    }
4060
0
  }
4061
4062
0
  if (ce->num_interfaces) {
4063
0
    for (uint32_t i = 0; i < ce->num_interfaces; i++) {
4064
0
      const zend_class_entry *interface =
4065
0
        zend_hash_find_ptr(EG(class_table), ce->interface_names[i].lc_name);
4066
0
      if (!interface) {
4067
0
        error->kind = "Unknown interface ";
4068
0
        error->name = ZSTR_VAL(ce->interface_names[i].name);
4069
0
        return FAILURE;
4070
0
      }
4071
0
    }
4072
0
  }
4073
4074
0
  if (ce->num_traits) {
4075
0
    for (uint32_t i = 0; i < ce->num_traits; i++) {
4076
0
      const zend_class_entry *trait =
4077
0
        zend_hash_find_ptr(EG(class_table), ce->trait_names[i].lc_name);
4078
0
      if (!trait) {
4079
0
        error->kind = "Unknown trait ";
4080
0
        error->name = ZSTR_VAL(ce->trait_names[i].name);
4081
0
        return FAILURE;
4082
0
      }
4083
0
    }
4084
0
  }
4085
4086
0
  return SUCCESS;
4087
0
}
4088
4089
static bool preload_try_resolve_constants(zend_class_entry *ce)
4090
0
{
4091
0
  bool ok, changed, was_changed = false;
4092
0
  zend_class_constant *c;
4093
0
  zval *val;
4094
0
  zend_string *key;
4095
4096
0
  EG(exception) = (void*)(uintptr_t)-1; /* prevent error reporting */
4097
0
  do {
4098
0
    ok = true;
4099
0
    changed = false;
4100
0
    ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) {
4101
0
      val = &c->value;
4102
0
      if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
4103
        /* For deprecated constants, we need to flag the zval for recursion
4104
         * detection. Make sure the zval is separated out of shm. */
4105
0
        if (ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED) {
4106
0
          ok = false;
4107
0
        }
4108
0
        if (EXPECTED(zend_update_class_constant(c, key, c->ce) == SUCCESS)) {
4109
0
          was_changed = changed = true;
4110
0
        } else {
4111
0
          ok = false;
4112
0
        }
4113
0
      }
4114
0
    } ZEND_HASH_FOREACH_END();
4115
0
    if (ok) {
4116
0
      ce->ce_flags &= ~ZEND_ACC_HAS_AST_CONSTANTS;
4117
0
    }
4118
0
    if (ce->default_properties_count) {
4119
0
      bool resolved = true;
4120
4121
0
      for (uint32_t i = 0; i < ce->default_properties_count; i++) {
4122
0
        zend_property_info *prop = ce->properties_info_table[i];
4123
0
        if (!prop) {
4124
0
          continue;
4125
0
        }
4126
4127
0
        val = &ce->default_properties_table[OBJ_PROP_TO_NUM(prop->offset)];
4128
0
        if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
4129
0
          if (UNEXPECTED(zval_update_constant_ex(val, prop->ce) != SUCCESS)) {
4130
0
            resolved = ok = false;
4131
0
          }
4132
0
        }
4133
0
      }
4134
0
      if (resolved) {
4135
0
        ce->ce_flags &= ~ZEND_ACC_HAS_AST_PROPERTIES;
4136
0
      }
4137
0
    }
4138
0
    if (ce->default_static_members_count) {
4139
0
      uint32_t count = ce->parent ? ce->default_static_members_count - ce->parent->default_static_members_count : ce->default_static_members_count;
4140
0
      bool resolved = true;
4141
4142
0
      val = ce->default_static_members_table + ce->default_static_members_count - 1;
4143
0
      while (count) {
4144
0
        if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
4145
0
          if (UNEXPECTED(zval_update_constant_ex(val, ce) != SUCCESS)) {
4146
0
            resolved = ok = false;
4147
0
          }
4148
0
        }
4149
0
        val--;
4150
0
        count--;
4151
0
      }
4152
0
      if (resolved) {
4153
0
        ce->ce_flags &= ~ZEND_ACC_HAS_AST_STATICS;
4154
0
      }
4155
0
    }
4156
0
  } while (changed && !ok);
4157
0
  EG(exception) = NULL;
4158
0
  CG(in_compilation) = false;
4159
4160
0
  if (ok) {
4161
0
    ce->ce_flags |= ZEND_ACC_CONSTANTS_UPDATED;
4162
0
  }
4163
4164
0
  return ok || was_changed;
4165
0
}
4166
4167
static void (*orig_error_cb)(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message);
4168
4169
static void preload_error_cb(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message)
4170
0
{
4171
  /* Suppress printing of the error, only bail out for fatal errors. */
4172
0
  if (type & E_FATAL_ERRORS) {
4173
0
    zend_bailout();
4174
0
  }
4175
0
}
4176
4177
/* Remove DECLARE opcodes and dynamic defs. */
4178
static void preload_remove_declares(zend_op_array *op_array)
4179
0
{
4180
0
  zend_op *opline = op_array->opcodes;
4181
0
  const zend_op *end = opline + op_array->last;
4182
0
  uint32_t skip_dynamic_func_count = 0;
4183
0
  zend_string *key;
4184
0
  zend_op_array *func;
4185
4186
0
  while (opline != end) {
4187
0
    switch (opline->opcode) {
4188
0
      case ZEND_DECLARE_CLASS:
4189
0
      case ZEND_DECLARE_CLASS_DELAYED:
4190
0
        key = Z_STR_P(RT_CONSTANT(opline, opline->op1) + 1);
4191
0
        if (!zend_hash_exists(CG(class_table), key)) {
4192
0
          MAKE_NOP(opline);
4193
0
        }
4194
0
        break;
4195
0
      case ZEND_DECLARE_FUNCTION:
4196
0
        opline->op2.num -= skip_dynamic_func_count;
4197
0
        key = Z_STR_P(RT_CONSTANT(opline, opline->op1));
4198
0
        func = zend_hash_find_ptr(EG(function_table), key);
4199
0
        if (func && func == op_array->dynamic_func_defs[opline->op2.num]) {
4200
0
          zend_op_array **dynamic_func_defs;
4201
4202
0
          op_array->num_dynamic_func_defs--;
4203
0
          if (op_array->num_dynamic_func_defs == 0) {
4204
0
            dynamic_func_defs = NULL;
4205
0
          } else {
4206
0
            dynamic_func_defs = emalloc(sizeof(zend_op_array*) * op_array->num_dynamic_func_defs);
4207
0
            if (opline->op2.num > 0) {
4208
0
              memcpy(
4209
0
                dynamic_func_defs,
4210
0
                op_array->dynamic_func_defs,
4211
0
                sizeof(zend_op_array*) * opline->op2.num);
4212
0
            }
4213
0
            if (op_array->num_dynamic_func_defs - opline->op2.num > 0) {
4214
0
              memcpy(
4215
0
                dynamic_func_defs + opline->op2.num,
4216
0
                op_array->dynamic_func_defs + (opline->op2.num + 1),
4217
0
                sizeof(zend_op_array*) * (op_array->num_dynamic_func_defs - opline->op2.num));
4218
0
            }
4219
0
          }
4220
0
          efree(op_array->dynamic_func_defs);
4221
0
          op_array->dynamic_func_defs = dynamic_func_defs;
4222
0
          skip_dynamic_func_count++;
4223
0
          MAKE_NOP(opline);
4224
0
        }
4225
0
        break;
4226
0
      case ZEND_DECLARE_LAMBDA_FUNCTION:
4227
0
        opline->op2.num -= skip_dynamic_func_count;
4228
0
        break;
4229
0
    }
4230
0
    opline++;
4231
0
  }
4232
0
}
4233
4234
static void preload_link(void)
4235
0
{
4236
0
  zval *zv;
4237
0
  zend_persistent_script *script;
4238
0
  zend_class_entry *ce;
4239
0
  zend_string *key;
4240
0
  bool changed;
4241
4242
0
  HashTable errors;
4243
0
  zend_hash_init(&errors, 0, NULL, NULL, 0);
4244
4245
  /* Resolve class dependencies */
4246
0
  do {
4247
0
    changed = false;
4248
4249
0
    ZEND_HASH_MAP_FOREACH_STR_KEY_VAL_FROM(EG(class_table), key, zv, EG(persistent_classes_count)) {
4250
0
      ce = Z_PTR_P(zv);
4251
4252
      /* Possible with internal class aliases */
4253
0
      if (ce->type == ZEND_INTERNAL_CLASS) {
4254
0
        ZEND_ASSERT(Z_TYPE_P(zv) == IS_ALIAS_PTR);
4255
0
        continue;
4256
0
      }
4257
4258
0
      if (!(ce->ce_flags & (ZEND_ACC_TOP_LEVEL|ZEND_ACC_ANON_CLASS))
4259
0
          || (ce->ce_flags & ZEND_ACC_LINKED)) {
4260
0
        continue;
4261
0
      }
4262
4263
0
      zend_string *lcname = zend_string_tolower(ce->name);
4264
0
      if (!(ce->ce_flags & ZEND_ACC_ANON_CLASS)) {
4265
0
        if (zend_hash_exists(EG(class_table), lcname)) {
4266
0
          zend_string_release(lcname);
4267
0
          continue;
4268
0
        }
4269
0
      }
4270
4271
0
      preload_error error_info;
4272
0
      if (preload_resolve_deps(&error_info, ce) == FAILURE) {
4273
0
        zend_string_release(lcname);
4274
0
        continue;
4275
0
      }
4276
4277
0
      zv = zend_hash_set_bucket_key(EG(class_table), (Bucket*)zv, lcname);
4278
0
      ZEND_ASSERT(zv && "We already checked above that the class doesn't exist yet");
4279
4280
      /* Set the FILE_CACHED flag to force a lazy load, and the CACHED flag to
4281
       * prevent freeing of interface names. */
4282
0
      void *checkpoint = zend_arena_checkpoint(CG(arena));
4283
0
      zend_class_entry *orig_ce = ce;
4284
0
      uint32_t temporary_flags = ZEND_ACC_FILE_CACHED|ZEND_ACC_CACHED;
4285
0
      ce->ce_flags |= temporary_flags;
4286
0
      if (ce->parent_name) {
4287
0
        zend_string_addref(ce->parent_name);
4288
0
      }
4289
4290
      /* Record and suppress errors during inheritance. */
4291
0
      orig_error_cb = zend_error_cb;
4292
0
      zend_error_cb = preload_error_cb;
4293
0
      zend_begin_record_errors();
4294
4295
      /* Set filename & lineno information for inheritance errors */
4296
0
      CG(in_compilation) = true;
4297
0
      CG(compiled_filename) = ce->info.user.filename;
4298
0
      CG(zend_lineno) = ce->info.user.line_start;
4299
0
      zend_try {
4300
0
        ce = zend_do_link_class(ce, NULL, lcname);
4301
0
        if (!ce) {
4302
0
          ZEND_ASSERT(0 && "Class linking failed?");
4303
0
        }
4304
0
        ce->ce_flags &= ~temporary_flags;
4305
0
        changed = true;
4306
4307
        /* Inheritance successful, print out any warnings. */
4308
0
        zend_error_cb = orig_error_cb;
4309
0
        zend_emit_recorded_errors();
4310
0
      } zend_catch {
4311
        /* Clear variance obligations that were left behind on bailout. */
4312
0
        if (CG(delayed_variance_obligations)) {
4313
0
          zend_hash_index_del(
4314
0
            CG(delayed_variance_obligations), (uintptr_t) Z_CE_P(zv));
4315
0
        }
4316
4317
        /* Restore the original class. */
4318
0
        zv = zend_hash_set_bucket_key(EG(class_table), (Bucket*)zv, key);
4319
0
        Z_CE_P(zv) = orig_ce;
4320
0
        orig_ce->ce_flags &= ~temporary_flags;
4321
0
        zend_arena_release(&CG(arena), checkpoint);
4322
4323
        /* Remember the last error. */
4324
0
        zend_error_cb = orig_error_cb;
4325
0
        EG(record_errors) = false;
4326
0
        ZEND_ASSERT(EG(errors).size > 0);
4327
0
        zend_hash_update_ptr(&errors, key, EG(errors).errors[EG(errors).size-1]);
4328
0
        EG(errors).size--;
4329
0
      } zend_end_try();
4330
0
      CG(in_compilation) = false;
4331
0
      CG(compiled_filename) = NULL;
4332
0
      zend_free_recorded_errors();
4333
0
      zend_string_release(lcname);
4334
0
    } ZEND_HASH_FOREACH_END();
4335
0
  } while (changed);
4336
4337
0
  do {
4338
0
    changed = false;
4339
4340
0
    ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(class_table), zv) {
4341
0
      ce = Z_PTR_P(zv);
4342
4343
      /* Possible with internal class aliases */
4344
0
      if (ce->type == ZEND_INTERNAL_CLASS) {
4345
0
        if (Z_TYPE_P(zv) != IS_ALIAS_PTR) {
4346
0
          break; /* can stop already */
4347
0
        }
4348
0
        continue;
4349
0
      }
4350
4351
0
      if ((ce->ce_flags & ZEND_ACC_LINKED) && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
4352
0
        if (!(ce->ce_flags & ZEND_ACC_TRAIT)) { /* don't update traits */
4353
0
          CG(in_compilation) = true; /* prevent autoloading */
4354
0
          if (preload_try_resolve_constants(ce)) {
4355
0
            changed = true;
4356
0
          }
4357
0
          CG(in_compilation) = false;
4358
0
        }
4359
0
      }
4360
0
    } ZEND_HASH_FOREACH_END();
4361
0
  } while (changed);
4362
4363
  /* Warn for classes that could not be linked. */
4364
0
  ZEND_HASH_MAP_FOREACH_STR_KEY_VAL_FROM(
4365
0
      EG(class_table), key, zv, EG(persistent_classes_count)) {
4366
0
    ce = Z_PTR_P(zv);
4367
4368
    /* Possible with internal class aliases */
4369
0
    if (ce->type == ZEND_INTERNAL_CLASS) {
4370
0
      ZEND_ASSERT(Z_TYPE_P(zv) == IS_ALIAS_PTR);
4371
0
      continue;
4372
0
    }
4373
4374
0
    if ((ce->ce_flags & (ZEND_ACC_TOP_LEVEL|ZEND_ACC_ANON_CLASS))
4375
0
        && !(ce->ce_flags & ZEND_ACC_LINKED)) {
4376
0
      zend_string *lcname = zend_string_tolower(ce->name);
4377
0
      preload_error error;
4378
0
      if (!(ce->ce_flags & ZEND_ACC_ANON_CLASS)
4379
0
       && zend_hash_exists(EG(class_table), lcname)) {
4380
0
        zend_error_at(
4381
0
          E_WARNING, ce->info.user.filename, ce->info.user.line_start,
4382
0
          "Can't preload already declared class %s", ZSTR_VAL(ce->name));
4383
0
      } else if (preload_resolve_deps(&error, ce) == FAILURE) {
4384
0
        zend_error_at(
4385
0
          E_WARNING, ce->info.user.filename, ce->info.user.line_start,
4386
0
          "Can't preload unlinked class %s: %s%s",
4387
0
          ZSTR_VAL(ce->name), error.kind, error.name);
4388
0
      } else {
4389
0
        zend_error_info *error = zend_hash_find_ptr(&errors, key);
4390
0
        zend_error_at(
4391
0
          E_WARNING, error->filename, error->lineno,
4392
0
          "Can't preload unlinked class %s: %s",
4393
0
          ZSTR_VAL(ce->name), ZSTR_VAL(error->message));
4394
0
      }
4395
0
      zend_string_release(lcname);
4396
0
    }
4397
0
  } ZEND_HASH_FOREACH_END();
4398
4399
0
  zend_hash_destroy(&errors);
4400
4401
0
  ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4402
0
    zend_op_array *op_array = &script->script.main_op_array;
4403
0
    preload_remove_declares(op_array);
4404
4405
0
    if (op_array->fn_flags & ZEND_ACC_EARLY_BINDING) {
4406
0
      zend_accel_free_delayed_early_binding_list(script);
4407
0
      zend_accel_build_delayed_early_binding_list(script);
4408
0
      if (!script->num_early_bindings) {
4409
0
        op_array->fn_flags &= ~ZEND_ACC_EARLY_BINDING;
4410
0
      }
4411
0
    }
4412
0
  } ZEND_HASH_FOREACH_END();
4413
4414
  /* Dynamic defs inside functions and methods need to be removed as well. */
4415
0
  zend_op_array *op_array;
4416
0
  ZEND_HASH_MAP_FOREACH_PTR_FROM(EG(function_table), op_array, EG(persistent_functions_count)) {
4417
0
    ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION);
4418
0
    preload_remove_declares(op_array);
4419
0
  } ZEND_HASH_FOREACH_END();
4420
0
  ZEND_HASH_MAP_FOREACH_PTR_FROM(EG(class_table), ce, EG(persistent_classes_count)) {
4421
0
    ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
4422
0
      if (op_array->type == ZEND_USER_FUNCTION) {
4423
0
        preload_remove_declares(op_array);
4424
0
      }
4425
0
    } ZEND_HASH_FOREACH_END();
4426
4427
0
    if (ce->num_hooked_props > 0) {
4428
0
      zend_property_info *info;
4429
4430
0
      ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, info) {
4431
0
        if (info->hooks) {
4432
0
          for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) {
4433
0
            if (info->hooks[i]) {
4434
0
              op_array = &info->hooks[i]->op_array;
4435
0
              ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION);
4436
0
              if (!(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
4437
0
                preload_remove_declares(op_array);
4438
0
              }
4439
0
            }
4440
0
          }
4441
0
        }
4442
0
      } ZEND_HASH_FOREACH_END();
4443
0
    }
4444
0
  } ZEND_HASH_FOREACH_END();
4445
0
}
4446
4447
static zend_string *preload_resolve_path(zend_string *filename)
4448
0
{
4449
0
  if (php_is_stream_path(ZSTR_VAL(filename))) {
4450
0
    return NULL;
4451
0
  }
4452
0
  return zend_resolve_path(filename);
4453
0
}
4454
4455
static void preload_remove_empty_includes(void)
4456
0
{
4457
0
  zend_persistent_script *script;
4458
0
  bool changed;
4459
4460
  /* mark all as empty */
4461
0
  ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4462
0
    script->empty = true;
4463
0
  } ZEND_HASH_FOREACH_END();
4464
4465
  /* find non empty scripts */
4466
0
  do {
4467
0
    changed = false;
4468
0
    ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4469
0
      if (script->empty) {
4470
0
        bool empty = true;
4471
0
        zend_op *opline = script->script.main_op_array.opcodes;
4472
0
        const zend_op *end = opline + script->script.main_op_array.last;
4473
4474
0
        while (opline < end) {
4475
0
          if (opline->opcode == ZEND_INCLUDE_OR_EVAL &&
4476
0
              opline->extended_value != ZEND_EVAL &&
4477
0
              opline->op1_type == IS_CONST &&
4478
0
              Z_TYPE_P(RT_CONSTANT(opline, opline->op1)) == IS_STRING &&
4479
0
              opline->result_type == IS_UNUSED) {
4480
4481
0
            zend_string *resolved_path = preload_resolve_path(Z_STR_P(RT_CONSTANT(opline, opline->op1)));
4482
4483
0
            if (resolved_path) {
4484
0
              zend_persistent_script *incl = zend_hash_find_ptr(preload_scripts, resolved_path);
4485
0
              zend_string_release(resolved_path);
4486
0
              if (!incl || !incl->empty) {
4487
0
                empty = false;
4488
0
                break;
4489
0
              }
4490
0
            } else {
4491
0
              empty = false;
4492
0
              break;
4493
0
            }
4494
0
          } else if (opline->opcode != ZEND_NOP &&
4495
0
                     opline->opcode != ZEND_RETURN &&
4496
0
                     opline->opcode != ZEND_HANDLE_EXCEPTION) {
4497
0
            empty = false;
4498
0
            break;
4499
0
          }
4500
0
          opline++;
4501
0
        }
4502
0
        if (!empty) {
4503
0
          script->empty = false;
4504
0
          changed = true;
4505
0
        }
4506
0
      }
4507
0
    } ZEND_HASH_FOREACH_END();
4508
0
  } while (changed);
4509
4510
  /* remove empty includes */
4511
0
  ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4512
0
    zend_op *opline = script->script.main_op_array.opcodes;
4513
0
    const zend_op *end = opline + script->script.main_op_array.last;
4514
4515
0
    while (opline < end) {
4516
0
      if (opline->opcode == ZEND_INCLUDE_OR_EVAL &&
4517
0
          opline->extended_value != ZEND_EVAL &&
4518
0
          opline->op1_type == IS_CONST &&
4519
0
          Z_TYPE_P(RT_CONSTANT(opline, opline->op1)) == IS_STRING) {
4520
4521
0
        zend_string *resolved_path = preload_resolve_path(Z_STR_P(RT_CONSTANT(opline, opline->op1)));
4522
4523
0
        if (resolved_path) {
4524
0
          const zend_persistent_script *incl = zend_hash_find_ptr(preload_scripts, resolved_path);
4525
0
          if (incl && incl->empty && opline->result_type == IS_UNUSED) {
4526
0
            MAKE_NOP(opline);
4527
0
          } else {
4528
0
            if (!IS_ABSOLUTE_PATH(Z_STRVAL_P(RT_CONSTANT(opline, opline->op1)), Z_STRLEN_P(RT_CONSTANT(opline, opline->op1)))) {
4529
              /* replace relative patch with absolute one */
4530
0
              zend_string_release(Z_STR_P(RT_CONSTANT(opline, opline->op1)));
4531
0
              ZVAL_STR_COPY(RT_CONSTANT(opline, opline->op1), resolved_path);
4532
0
            }
4533
0
          }
4534
0
          zend_string_release(resolved_path);
4535
0
        }
4536
0
      }
4537
0
      opline++;
4538
0
    }
4539
0
  } ZEND_HASH_FOREACH_END();
4540
0
}
4541
4542
0
static void preload_register_trait_methods(const zend_class_entry *ce) {
4543
0
  zend_op_array *op_array;
4544
4545
0
  ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
4546
0
    if (!(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
4547
0
      ZEND_ASSERT(op_array->refcount && "Must have refcount pointer");
4548
0
      zend_shared_alloc_register_xlat_entry(op_array->refcount, op_array);
4549
0
    }
4550
0
  } ZEND_HASH_FOREACH_END();
4551
4552
0
  if (ce->num_hooked_props > 0) {
4553
0
    ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, zend_property_info *info) {
4554
0
      if (info->hooks) {
4555
0
        for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) {
4556
0
          if (info->hooks[i]) {
4557
0
            op_array = &info->hooks[i]->op_array;
4558
0
            if (!(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
4559
0
              ZEND_ASSERT(op_array->refcount && "Must have refcount pointer");
4560
0
              zend_shared_alloc_register_xlat_entry(op_array->refcount, op_array);
4561
0
            }
4562
0
          }
4563
0
        }
4564
0
      }
4565
0
    } ZEND_HASH_FOREACH_END();
4566
0
  }
4567
0
}
4568
4569
static void preload_fix_trait_op_array(zend_op_array *op_array)
4570
0
{
4571
0
  if (!(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
4572
0
    return;
4573
0
  }
4574
4575
0
  const zend_op_array *orig_op_array = zend_shared_alloc_get_xlat_entry(op_array->refcount);
4576
0
  ZEND_ASSERT(orig_op_array && "Must be in xlat table");
4577
4578
0
  zend_string *function_name = op_array->function_name;
4579
0
  zend_class_entry *scope = op_array->scope;
4580
0
  uint32_t fn_flags = op_array->fn_flags;
4581
0
  uint32_t fn_flags2 = op_array->fn_flags2;
4582
0
  zend_function *prototype = op_array->prototype;
4583
0
  HashTable *ht = op_array->static_variables;
4584
0
  const zend_property_info *prop_info = op_array->prop_info;
4585
0
  *op_array = *orig_op_array;
4586
0
  op_array->function_name = function_name;
4587
0
  op_array->scope = scope;
4588
0
  op_array->fn_flags = fn_flags;
4589
0
  op_array->fn_flags2 = fn_flags2;
4590
0
  op_array->prototype = prototype;
4591
0
  op_array->static_variables = ht;
4592
0
  op_array->prop_info = prop_info;
4593
0
}
4594
4595
static void preload_fix_trait_methods(const zend_class_entry *ce)
4596
0
{
4597
0
  zend_op_array *op_array;
4598
4599
0
  ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
4600
0
    preload_fix_trait_op_array(op_array);
4601
0
  } ZEND_HASH_FOREACH_END();
4602
4603
0
  if (ce->num_hooked_props > 0) {
4604
0
    zend_property_info *info;
4605
0
    ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, info) {
4606
0
      if (info->hooks) {
4607
0
        for (uint32_t i = 0; i < ZEND_PROPERTY_HOOK_COUNT; i++) {
4608
0
          if (info->hooks[i]) {
4609
0
            op_array = &info->hooks[i]->op_array;
4610
0
            preload_fix_trait_op_array(op_array);
4611
0
          }
4612
0
        }
4613
0
      }
4614
0
    } ZEND_HASH_FOREACH_END();
4615
0
  }
4616
0
}
4617
4618
static void preload_optimize(zend_persistent_script *script)
4619
0
{
4620
0
  const zend_class_entry *ce;
4621
0
  zend_persistent_script *tmp_script;
4622
4623
0
  zend_shared_alloc_init_xlat_table();
4624
4625
0
  ZEND_HASH_MAP_FOREACH_PTR(&script->script.class_table, ce) {
4626
0
    if (ce->ce_flags & ZEND_ACC_TRAIT) {
4627
0
      preload_register_trait_methods(ce);
4628
0
    }
4629
0
  } ZEND_HASH_FOREACH_END();
4630
4631
0
  ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, tmp_script) {
4632
0
    ZEND_HASH_MAP_FOREACH_PTR(&tmp_script->script.class_table, ce) {
4633
0
      if (ce->ce_flags & ZEND_ACC_TRAIT) {
4634
0
        preload_register_trait_methods(ce);
4635
0
      }
4636
0
    } ZEND_HASH_FOREACH_END();
4637
0
  } ZEND_HASH_FOREACH_END();
4638
4639
0
  zend_optimize_script(&script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level);
4640
0
  zend_accel_finalize_delayed_early_binding_list(script);
4641
4642
0
  ZEND_HASH_MAP_FOREACH_PTR(&script->script.class_table, ce) {
4643
0
    preload_fix_trait_methods(ce);
4644
0
  } ZEND_HASH_FOREACH_END();
4645
4646
0
  ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
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
0
  } ZEND_HASH_FOREACH_END();
4651
4652
0
  zend_shared_alloc_destroy_xlat_table();
4653
4654
0
  ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4655
0
    zend_optimize_script(&script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level);
4656
0
    zend_accel_finalize_delayed_early_binding_list(script);
4657
0
  } ZEND_HASH_FOREACH_END();
4658
0
}
4659
4660
static zend_persistent_script* preload_script_in_shared_memory(zend_persistent_script *new_persistent_script)
4661
0
{
4662
0
  zend_accel_hash_entry *bucket;
4663
0
  uint32_t memory_used;
4664
0
  uint32_t checkpoint;
4665
4666
0
  if (zend_accel_hash_is_full(&ZCSG(hash))) {
4667
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.");
4668
0
    return NULL;
4669
0
  }
4670
4671
0
  checkpoint = zend_shared_alloc_checkpoint_xlat_table();
4672
4673
  /* Calculate the required memory size */
4674
0
  memory_used = zend_accel_script_persist_calc(new_persistent_script, 1);
4675
4676
  /* Allocate shared memory */
4677
0
  ZCG(mem) = zend_shared_alloc_aligned(memory_used);
4678
0
  if (!ZCG(mem)) {
4679
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.");
4680
0
    return NULL;
4681
0
  }
4682
4683
0
  bzero_aligned(ZCG(mem), memory_used);
4684
4685
0
  zend_shared_alloc_restore_xlat_table(checkpoint);
4686
4687
  /* Copy into shared memory */
4688
0
  new_persistent_script = zend_accel_script_persist(new_persistent_script, 1);
4689
4690
0
  new_persistent_script->is_phar = is_phar_file(new_persistent_script->script.filename);
4691
4692
  /* Consistency check */
4693
0
  if ((char*)new_persistent_script->mem + new_persistent_script->size != (char*)ZCG(mem)) {
4694
0
    zend_accel_error(
4695
0
      ((char*)new_persistent_script->mem + new_persistent_script->size < (char*)ZCG(mem)) ? ACCEL_LOG_ERROR : ACCEL_LOG_WARNING,
4696
0
      "Internal error: wrong size calculation: %s start=" ZEND_ADDR_FMT ", end=" ZEND_ADDR_FMT ", real=" ZEND_ADDR_FMT "\n",
4697
0
      ZSTR_VAL(new_persistent_script->script.filename),
4698
0
      (size_t)new_persistent_script->mem,
4699
0
      (size_t)((char *)new_persistent_script->mem + new_persistent_script->size),
4700
0
      (size_t)ZCG(mem));
4701
0
  }
4702
4703
  /* store script structure in the hash table */
4704
0
  bucket = zend_accel_hash_update(&ZCSG(hash), new_persistent_script->script.filename, 0, new_persistent_script);
4705
0
  if (bucket) {
4706
0
    zend_accel_error(ACCEL_LOG_INFO, "Cached script '%s'", ZSTR_VAL(new_persistent_script->script.filename));
4707
0
  }
4708
4709
0
  new_persistent_script->dynamic_members.memory_consumption = ZEND_ALIGNED_SIZE(new_persistent_script->size);
4710
4711
0
  return new_persistent_script;
4712
0
}
4713
4714
static void preload_load(size_t orig_map_ptr_static_last)
4715
0
{
4716
  /* Load into process tables */
4717
0
  const zend_script *script = &ZCSG(preload_script)->script;
4718
0
  if (zend_hash_num_elements(&script->function_table)) {
4719
0
    Bucket *p = script->function_table.arData;
4720
0
    const Bucket *end = p + script->function_table.nNumUsed;
4721
4722
0
    zend_hash_extend(CG(function_table),
4723
0
      CG(function_table)->nNumUsed + script->function_table.nNumUsed, 0);
4724
0
    for (; p != end; p++) {
4725
0
      _zend_hash_append_ptr_ex(CG(function_table), p->key, Z_PTR(p->val), 1);
4726
0
    }
4727
0
  }
4728
4729
0
  if (zend_hash_num_elements(&script->class_table)) {
4730
0
    Bucket *p = script->class_table.arData;
4731
0
    const Bucket *end = p + script->class_table.nNumUsed;
4732
4733
0
    zend_hash_extend(CG(class_table),
4734
0
      CG(class_table)->nNumUsed + script->class_table.nNumUsed, 0);
4735
0
    for (; p != end; p++) {
4736
0
      _zend_hash_append_ex(CG(class_table), p->key, &p->val, 1);
4737
0
    }
4738
0
  }
4739
4740
0
  size_t old_map_ptr_last = CG(map_ptr_last);
4741
0
  if (zend_map_ptr_static_last != ZCSG(map_ptr_static_last) || old_map_ptr_last != ZCSG(map_ptr_last)) {
4742
0
    CG(map_ptr_last) = ZCSG(map_ptr_last);
4743
0
    CG(map_ptr_size) = ZEND_MM_ALIGNED_SIZE_EX(ZCSG(map_ptr_last) + 1, 4096);
4744
0
    zend_map_ptr_static_last = ZCSG(map_ptr_static_last);
4745
4746
    /* Grow map_ptr table as needed, but allocate once for static + regular map_ptrs */
4747
0
    size_t new_static_size = ZEND_MM_ALIGNED_SIZE_EX(zend_map_ptr_static_last, 4096);
4748
0
    if (zend_map_ptr_static_size != new_static_size) {
4749
0
      void *new_base = pemalloc((new_static_size + CG(map_ptr_size)) * sizeof(void *), 1);
4750
0
      if (CG(map_ptr_real_base)) {
4751
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 *));
4752
0
        pefree(CG(map_ptr_real_base), 1);
4753
0
      }
4754
0
      CG(map_ptr_real_base) = new_base;
4755
0
      zend_map_ptr_static_size = new_static_size;
4756
0
    } else {
4757
0
      CG(map_ptr_real_base) = perealloc(CG(map_ptr_real_base), (zend_map_ptr_static_size + CG(map_ptr_size)) * sizeof(void *), 1);
4758
0
    }
4759
4760
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 *));
4761
0
    CG(map_ptr_base) = ZEND_MAP_PTR_BIASED_BASE(CG(map_ptr_real_base));
4762
0
  }
4763
4764
0
  if (orig_map_ptr_static_last != zend_map_ptr_static_last) {
4765
    /* preloaded static entries currently are all runtime cache pointers, just assign them as such */
4766
0
    size_t runtime_cache_size = zend_internal_run_time_cache_reserved_size();
4767
0
    ZCG(preloaded_internal_run_time_cache_size) = (zend_map_ptr_static_last - orig_map_ptr_static_last) * runtime_cache_size;
4768
0
    char *cache = pemalloc(ZCG(preloaded_internal_run_time_cache_size), 1);
4769
0
    ZCG(preloaded_internal_run_time_cache) = cache;
4770
4771
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) {
4772
0
            *ZEND_MAP_PTR_STATIC_NUM_TO_PTR(cur_static_map_ptr) = cache;
4773
0
      cache += runtime_cache_size;
4774
0
    }
4775
0
  }
4776
0
}
4777
4778
#if HAVE_JIT
4779
static void zend_accel_clear_call_graph_ptrs(const zend_op_array *op_array)
4780
0
{
4781
0
  ZEND_ASSERT(ZEND_USER_CODE(op_array->type));
4782
0
  zend_func_info *info = ZEND_FUNC_INFO(op_array);
4783
0
  if (info) {
4784
0
    info->caller_info = NULL;
4785
0
    info->callee_info = NULL;
4786
0
  }
4787
0
}
4788
4789
static void accel_reset_arena_info(const zend_persistent_script *script)
4790
0
{
4791
0
  const zend_op_array *op_array;
4792
0
  zend_class_entry *ce;
4793
4794
0
  zend_accel_clear_call_graph_ptrs(&script->script.main_op_array);
4795
0
  ZEND_HASH_MAP_FOREACH_PTR(&script->script.function_table, op_array) {
4796
0
    zend_accel_clear_call_graph_ptrs(op_array);
4797
0
  } ZEND_HASH_FOREACH_END();
4798
0
  ZEND_HASH_MAP_FOREACH_PTR(&script->script.class_table, ce) {
4799
0
    ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
4800
0
      if (op_array->scope == ce
4801
0
       && op_array->type == ZEND_USER_FUNCTION
4802
0
       && !(op_array->fn_flags & ZEND_ACC_ABSTRACT)
4803
0
       && !(op_array->fn_flags & ZEND_ACC_TRAIT_CLONE)) {
4804
0
        zend_accel_clear_call_graph_ptrs(op_array);
4805
0
      }
4806
0
    } ZEND_HASH_FOREACH_END();
4807
0
  } ZEND_HASH_FOREACH_END();
4808
0
}
4809
#endif
4810
4811
static zend_result accel_preload(const char *config, bool in_child)
4812
0
{
4813
0
  zend_file_handle file_handle;
4814
0
  zend_result ret;
4815
0
  char *orig_open_basedir;
4816
0
  size_t orig_map_ptr_last, orig_map_ptr_static_last;
4817
0
  uint32_t orig_compiler_options;
4818
4819
0
  ZCG(enabled) = false;
4820
0
  ZCG(accelerator_enabled) = false;
4821
0
  orig_open_basedir = PG(open_basedir);
4822
0
  PG(open_basedir) = NULL;
4823
0
  preload_orig_compile_file = accelerator_orig_compile_file;
4824
0
  accelerator_orig_compile_file = preload_compile_file;
4825
4826
0
  orig_map_ptr_last = CG(map_ptr_last);
4827
0
  orig_map_ptr_static_last = zend_map_ptr_static_last;
4828
4829
  /* Compile and execute preloading script */
4830
0
  zend_stream_init_filename(&file_handle, (char *) config);
4831
4832
0
  preload_scripts = emalloc(sizeof(HashTable));
4833
0
  zend_hash_init(preload_scripts, 0, NULL, NULL, 0);
4834
4835
0
  orig_compiler_options = CG(compiler_options);
4836
0
  if (in_child) {
4837
0
    CG(compiler_options) |= ZEND_COMPILE_PRELOAD_IN_CHILD;
4838
0
  }
4839
0
  CG(compiler_options) |= ZEND_COMPILE_PRELOAD;
4840
0
  CG(compiler_options) |= ZEND_COMPILE_HANDLE_OP_ARRAY;
4841
0
  CG(compiler_options) |= ZEND_COMPILE_DELAYED_BINDING;
4842
0
  CG(compiler_options) |= ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION;
4843
0
  CG(compiler_options) |= ZEND_COMPILE_IGNORE_OTHER_FILES;
4844
0
  CG(skip_shebang) = true;
4845
4846
0
  zend_try {
4847
0
    zend_op_array *op_array;
4848
4849
0
    ret = SUCCESS;
4850
0
    op_array = zend_compile_file(&file_handle, ZEND_REQUIRE);
4851
0
    if (file_handle.opened_path) {
4852
0
      zend_hash_add_empty_element(&EG(included_files), file_handle.opened_path);
4853
0
    }
4854
0
    zend_destroy_file_handle(&file_handle);
4855
0
    if (op_array) {
4856
0
      zend_execute(op_array, NULL);
4857
0
      if (UNEXPECTED(EG(exception))) {
4858
0
        if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) {
4859
0
          zend_user_exception_handler();
4860
0
        }
4861
0
        if (EG(exception)) {
4862
0
          ret = zend_exception_error(EG(exception), E_ERROR);
4863
0
          if (ret == FAILURE) {
4864
0
            CG(unclean_shutdown) = true;
4865
0
          }
4866
0
        }
4867
0
      }
4868
0
      destroy_op_array(op_array);
4869
0
      efree_size(op_array, sizeof(zend_op_array));
4870
0
    } else {
4871
0
      if (EG(exception)) {
4872
0
        zend_exception_error(EG(exception), E_ERROR);
4873
0
      }
4874
4875
0
      CG(unclean_shutdown) = true;
4876
0
      ret = FAILURE;
4877
0
    }
4878
0
  } zend_catch {
4879
0
    ret = FAILURE;
4880
0
  } zend_end_try();
4881
4882
0
  PG(open_basedir) = orig_open_basedir;
4883
0
  accelerator_orig_compile_file = preload_orig_compile_file;
4884
0
  ZCG(enabled) = true;
4885
4886
0
  zend_destroy_file_handle(&file_handle);
4887
4888
0
  if (ret == SUCCESS) {
4889
0
    zend_persistent_script *script;
4890
0
    int ping_auto_globals_mask;
4891
0
    int i;
4892
4893
0
    if (PG(auto_globals_jit)) {
4894
0
      ping_auto_globals_mask = zend_accel_get_auto_globals();
4895
0
    } else {
4896
0
      ping_auto_globals_mask = 0;
4897
0
    }
4898
4899
0
    if (EG(zend_constants)) {
4900
      /* Remember __COMPILER_HALT_OFFSET__(s). Do this early,
4901
       * as zend_shutdown_executor_values() destroys constants. */
4902
0
      ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
4903
0
        zend_execute_data *orig_execute_data = EG(current_execute_data);
4904
0
        zend_execute_data fake_execute_data;
4905
0
        zval *offset;
4906
4907
0
        memset(&fake_execute_data, 0, sizeof(fake_execute_data));
4908
0
        fake_execute_data.func = (zend_function*)&script->script.main_op_array;
4909
0
        EG(current_execute_data) = &fake_execute_data;
4910
0
        if ((offset = zend_get_constant_str("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__") - 1)) != NULL) {
4911
0
          script->compiler_halt_offset = Z_LVAL_P(offset);
4912
0
        }
4913
0
        EG(current_execute_data) = orig_execute_data;
4914
0
      } ZEND_HASH_FOREACH_END();
4915
0
    }
4916
4917
    /* Cleanup executor */
4918
0
    EG(flags) |= EG_FLAGS_IN_SHUTDOWN;
4919
4920
0
    php_call_shutdown_functions();
4921
0
    zend_call_destructors();
4922
0
    php_output_end_all();
4923
0
    php_free_shutdown_functions();
4924
4925
    /* Release stored values to avoid dangling pointers */
4926
0
    zend_shutdown_executor_values(/* fast_shutdown */ false);
4927
4928
    /* On ZTS we execute `executor_globals_ctor` which reset the freelist and p5s pointers, while on NTS we don't.
4929
     * We have to clean up the memory before the actual request takes place to avoid a memory leak. */
4930
#ifdef ZTS
4931
    zend_shutdown_strtod();
4932
#endif
4933
4934
    /* We don't want to preload constants.
4935
     * Check that  zend_shutdown_executor_values() also destroys constants. */
4936
0
    ZEND_ASSERT(zend_hash_num_elements(EG(zend_constants)) == EG(persistent_constants_count));
4937
4938
0
    zend_hash_init(&EG(symbol_table), 0, NULL, ZVAL_PTR_DTOR, 0);
4939
4940
0
    CG(map_ptr_last) = orig_map_ptr_last;
4941
4942
0
    if (EG(full_tables_cleanup)) {
4943
0
      zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading is not compatible with dl() function.");
4944
0
      ret = FAILURE;
4945
0
      goto finish;
4946
0
    }
4947
4948
    /* Inheritance errors may be thrown during linking */
4949
0
    zend_try {
4950
0
      preload_link();
4951
0
    } zend_catch {
4952
0
      CG(map_ptr_last) = orig_map_ptr_last;
4953
0
      ret = FAILURE;
4954
0
      goto finish;
4955
0
    } zend_end_try();
4956
4957
0
    preload_remove_empty_includes();
4958
4959
0
    script = create_persistent_script();
4960
0
    script->ping_auto_globals_mask = ping_auto_globals_mask;
4961
4962
    /* Store all functions and classes in a single pseudo-file */
4963
0
    CG(compiled_filename) = ZSTR_INIT_LITERAL("$PRELOAD$", 0);
4964
#if ZEND_USE_ABS_CONST_ADDR
4965
    init_op_array(&script->script.main_op_array, ZEND_USER_FUNCTION, 1);
4966
#else
4967
0
    init_op_array(&script->script.main_op_array, ZEND_USER_FUNCTION, 2);
4968
0
#endif
4969
0
    script->script.main_op_array.fn_flags |= ZEND_ACC_DONE_PASS_TWO;
4970
0
    script->script.main_op_array.last = 1;
4971
0
    script->script.main_op_array.last_literal = 1;
4972
0
    script->script.main_op_array.T = ZEND_OBSERVER_ENABLED;
4973
#if ZEND_USE_ABS_CONST_ADDR
4974
    script->script.main_op_array.literals = (zval*)emalloc(sizeof(zval));
4975
#else
4976
0
    script->script.main_op_array.literals = (zval*)(script->script.main_op_array.opcodes + 1);
4977
0
#endif
4978
0
    ZVAL_NULL(script->script.main_op_array.literals);
4979
0
    memset(script->script.main_op_array.opcodes, 0, sizeof(zend_op));
4980
0
    script->script.main_op_array.opcodes[0].opcode = ZEND_RETURN;
4981
0
    script->script.main_op_array.opcodes[0].op1_type = IS_CONST;
4982
0
    script->script.main_op_array.opcodes[0].op1.constant = 0;
4983
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);
4984
0
    zend_vm_set_opcode_handler(script->script.main_op_array.opcodes);
4985
4986
0
    script->script.filename = CG(compiled_filename);
4987
0
    CG(compiled_filename) = NULL;
4988
4989
0
    preload_move_user_functions(CG(function_table), &script->script.function_table);
4990
0
    preload_move_user_classes(CG(class_table), &script->script.class_table);
4991
4992
0
    zend_hash_sort_ex(&script->script.class_table, preload_sort_classes, NULL, 0);
4993
4994
0
    preload_optimize(script);
4995
4996
0
    zend_shared_alloc_init_xlat_table();
4997
4998
0
    HANDLE_BLOCK_INTERRUPTIONS();
4999
0
    SHM_UNPROTECT();
5000
5001
0
    ZCSG(preload_script) = preload_script_in_shared_memory(script);
5002
5003
0
    SHM_PROTECT();
5004
0
    HANDLE_UNBLOCK_INTERRUPTIONS();
5005
5006
0
    preload_load(orig_map_ptr_static_last);
5007
5008
    /* Update persistent counts, as shutdown will discard anything past
5009
     * that, and these tables are aliases to global ones at this point. */
5010
0
    EG(persistent_functions_count) = EG(function_table)->nNumUsed;
5011
0
    EG(persistent_classes_count)   = EG(class_table)->nNumUsed;
5012
0
    EG(persistent_constants_count) = EG(zend_constants)->nNumUsed;
5013
5014
    /* Store individual scripts with unlinked classes */
5015
0
    HANDLE_BLOCK_INTERRUPTIONS();
5016
0
    SHM_UNPROTECT();
5017
5018
0
    i = 0;
5019
0
    ZCSG(saved_scripts) = zend_shared_alloc((zend_hash_num_elements(preload_scripts) + 1) * sizeof(void*));
5020
0
    ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) {
5021
0
      if (zend_hash_num_elements(&script->script.class_table) > 1) {
5022
0
        zend_hash_sort_ex(&script->script.class_table, preload_sort_classes, NULL, 0);
5023
0
      }
5024
0
      ZCSG(saved_scripts)[i++] = preload_script_in_shared_memory(script);
5025
0
    } ZEND_HASH_FOREACH_END();
5026
0
    ZCSG(saved_scripts)[i] = NULL;
5027
5028
0
#if HAVE_JIT
5029
    /* During persisting, the JIT may trigger and fill in the call graph.
5030
     * The call graph info is allocated on the arena which will be gone after preloading.
5031
     * To prevent invalid accesses during normal requests, the arena data should be cleared.
5032
     * This has to be done after all scripts have been persisted because shared op arrays between
5033
     * scripts can change the call graph. */
5034
0
    accel_reset_arena_info(ZCSG(preload_script));
5035
0
    for (zend_persistent_script **scripts = ZCSG(saved_scripts); *scripts; scripts++) {
5036
0
      accel_reset_arena_info(*scripts);
5037
0
    }
5038
0
#endif
5039
5040
0
    zend_shared_alloc_save_state();
5041
0
    accel_interned_strings_save_state();
5042
5043
0
    SHM_PROTECT();
5044
0
    HANDLE_UNBLOCK_INTERRUPTIONS();
5045
5046
0
    zend_shared_alloc_destroy_xlat_table();
5047
0
  } else {
5048
0
    CG(map_ptr_last) = orig_map_ptr_last;
5049
0
  }
5050
5051
0
finish:
5052
0
  CG(compiler_options) = orig_compiler_options;
5053
0
  zend_hash_destroy(preload_scripts);
5054
0
  efree(preload_scripts);
5055
0
  preload_scripts = NULL;
5056
5057
0
  return ret;
5058
0
}
5059
5060
static size_t preload_ub_write(const char *str, size_t str_length)
5061
0
{
5062
0
  return fwrite(str, 1, str_length, stdout);
5063
0
}
5064
5065
static void preload_flush(void *server_context)
5066
0
{
5067
0
  fflush(stdout);
5068
0
}
5069
5070
static int preload_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s)
5071
0
{
5072
0
  return 0;
5073
0
}
5074
5075
static int preload_send_headers(sapi_headers_struct *sapi_headers)
5076
0
{
5077
0
  return SAPI_HEADER_SENT_SUCCESSFULLY;
5078
0
}
5079
5080
static void preload_send_header(sapi_header_struct *sapi_header, void *server_context)
5081
0
{
5082
0
}
5083
5084
static zend_result accel_finish_startup_preload(bool in_child)
5085
0
{
5086
0
  zend_result ret = SUCCESS;
5087
0
  int orig_error_reporting;
5088
5089
0
  int (*orig_activate)(void) = sapi_module.activate;
5090
0
  int (*orig_deactivate)(void) = sapi_module.deactivate;
5091
0
  void (*orig_register_server_variables)(zval *track_vars_array) = sapi_module.register_server_variables;
5092
0
  int (*orig_header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers) = sapi_module.header_handler;
5093
0
  int (*orig_send_headers)(sapi_headers_struct *sapi_headers) = sapi_module.send_headers;
5094
0
  void (*orig_send_header)(sapi_header_struct *sapi_header, void *server_context)= sapi_module.send_header;
5095
0
  char *(*orig_getenv)(const char *name, size_t name_len) = sapi_module.getenv;
5096
0
  size_t (*orig_ub_write)(const char *str, size_t str_length) = sapi_module.ub_write;
5097
0
  void (*orig_flush)(void *server_context) = sapi_module.flush;
5098
0
#ifdef ZEND_SIGNALS
5099
0
  bool old_reset_signals = SIGG(reset);
5100
0
#endif
5101
5102
0
  ZCG(preloading) = true;
5103
5104
0
  sapi_module.activate = NULL;
5105
0
  sapi_module.deactivate = NULL;
5106
0
  sapi_module.register_server_variables = NULL;
5107
0
  sapi_module.header_handler = preload_header_handler;
5108
0
  sapi_module.send_headers = preload_send_headers;
5109
0
  sapi_module.send_header = preload_send_header;
5110
0
  sapi_module.getenv = NULL;
5111
0
  sapi_module.ub_write = preload_ub_write;
5112
0
  sapi_module.flush = preload_flush;
5113
5114
0
  zend_interned_strings_switch_storage(1);
5115
5116
0
#ifdef ZEND_SIGNALS
5117
0
  SIGG(reset) = false;
5118
0
#endif
5119
5120
0
  orig_error_reporting = EG(error_reporting);
5121
0
  EG(error_reporting) = 0;
5122
5123
0
  const zend_result rc = php_request_startup();
5124
5125
0
  EG(error_reporting) = orig_error_reporting;
5126
5127
0
  if (rc == SUCCESS) {
5128
0
    bool orig_report_memleaks;
5129
5130
    /* don't send headers */
5131
0
    SG(headers_sent) = true;
5132
0
    SG(request_info).no_headers = true;
5133
0
    php_output_set_status(0);
5134
5135
0
    ZCG(auto_globals_mask) = 0;
5136
0
    ZCG(request_time) = (time_t)sapi_get_request_time();
5137
0
    ZCG(cache_opline) = NULL;
5138
0
    ZCG(cache_persistent_script) = NULL;
5139
0
    ZCG(include_path_key_len) = 0;
5140
0
    ZCG(include_path_check) = true;
5141
5142
0
    ZCG(cwd) = NULL;
5143
0
    ZCG(cwd_key_len) = 0;
5144
0
    ZCG(cwd_check) = true;
5145
5146
0
    if (accel_preload(ZCG(accel_directives).preload, in_child) != SUCCESS) {
5147
0
      ret = FAILURE;
5148
0
    }
5149
0
    preload_flush(NULL);
5150
5151
0
    orig_report_memleaks = PG(report_memleaks);
5152
0
    PG(report_memleaks) = false;
5153
0
#ifdef ZEND_SIGNALS
5154
    /* We may not have registered signal handlers due to SIGG(reset)=0, so
5155
     * also disable the check that they are registered. */
5156
0
    SIGG(check) = false;
5157
0
#endif
5158
0
    php_request_shutdown(NULL); /* calls zend_shared_alloc_unlock(); */
5159
0
    EG(class_table) = NULL;
5160
0
    EG(function_table) = NULL;
5161
0
    PG(report_memleaks) = orig_report_memleaks;
5162
#ifdef ZTS
5163
    /* Reset the virtual CWD state back to the original state created by virtual_cwd_startup().
5164
     * This is necessary because the normal startup code assumes the CWD state is active. */
5165
    virtual_cwd_activate();
5166
#endif
5167
0
  } else {
5168
0
    zend_shared_alloc_unlock();
5169
0
    ret = FAILURE;
5170
0
  }
5171
0
#ifdef ZEND_SIGNALS
5172
0
  SIGG(reset) = old_reset_signals;
5173
0
#endif
5174
5175
0
  sapi_module.activate = orig_activate;
5176
0
  sapi_module.deactivate = orig_deactivate;
5177
0
  sapi_module.register_server_variables = orig_register_server_variables;
5178
0
  sapi_module.header_handler = orig_header_handler;
5179
0
  sapi_module.send_headers = orig_send_headers;
5180
0
  sapi_module.send_header = orig_send_header;
5181
0
  sapi_module.getenv = orig_getenv;
5182
0
  sapi_module.ub_write = orig_ub_write;
5183
0
  sapi_module.flush = orig_flush;
5184
5185
0
  ZCG(preloading) = false;
5186
5187
0
  sapi_activate();
5188
5189
0
  return ret;
5190
0
}
5191
5192
static zend_result accel_finish_startup_preload_subprocess(pid_t *pid)
5193
0
{
5194
0
  uid_t euid = geteuid();
5195
0
  if (euid != 0) {
5196
0
    if (ZCG(accel_directives).preload_user
5197
0
     && *ZCG(accel_directives).preload_user) {
5198
0
      zend_accel_error(ACCEL_LOG_WARNING, "\"opcache.preload_user\" is ignored because the current user is not \"root\"");
5199
0
    }
5200
5201
0
    *pid = -1;
5202
0
    return SUCCESS;
5203
0
  }
5204
5205
0
  if (!ZCG(accel_directives).preload_user
5206
0
   || !*ZCG(accel_directives).preload_user) {
5207
5208
0
    bool sapi_requires_preload_user = !(strcmp(sapi_module.name, "cli") == 0
5209
0
      || strcmp(sapi_module.name, "phpdbg") == 0);
5210
5211
0
    if (!sapi_requires_preload_user) {
5212
0
      *pid = -1;
5213
0
      return SUCCESS;
5214
0
    }
5215
5216
0
    zend_shared_alloc_unlock();
5217
0
    zend_accel_error_noreturn(ACCEL_LOG_FATAL, "\"opcache.preload\" requires \"opcache.preload_user\" when running under uid 0");
5218
0
    return FAILURE;
5219
0
  }
5220
5221
0
  struct passwd *pw = getpwnam(ZCG(accel_directives).preload_user);
5222
0
  if (pw == NULL) {
5223
0
    zend_shared_alloc_unlock();
5224
0
    zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to getpwnam(\"%s\")", ZCG(accel_directives).preload_user);
5225
0
    return FAILURE;
5226
0
  }
5227
5228
0
  if (pw->pw_uid == euid) {
5229
0
    *pid = -1;
5230
0
    return SUCCESS;
5231
0
  }
5232
5233
0
  *pid = fork();
5234
0
  if (*pid == -1) {
5235
0
    zend_shared_alloc_unlock();
5236
0
    zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to fork()");
5237
0
    return FAILURE;
5238
0
  }
5239
5240
0
  if (*pid == 0) { /* children */
5241
0
    if (setgid(pw->pw_gid) < 0) {
5242
0
      zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setgid(%d)", pw->pw_gid);
5243
0
      exit(1);
5244
0
    }
5245
0
    if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
5246
0
      zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to initgroups(\"%s\", %d)", pw->pw_name, pw->pw_uid);
5247
0
      exit(1);
5248
0
    }
5249
0
    if (setuid(pw->pw_uid) < 0) {
5250
0
      zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setuid(%d)", pw->pw_uid);
5251
0
      exit(1);
5252
0
    }
5253
0
    php_child_init();
5254
0
  }
5255
5256
0
  return SUCCESS;
5257
0
}
5258
#endif /* ZEND_WIN32 */
5259
5260
static zend_result accel_finish_startup(void)
5261
16
{
5262
16
  if (!ZCG(enabled) || !accel_startup_ok) {
5263
0
    return SUCCESS;
5264
0
  }
5265
5266
16
  if (!(ZCG(accel_directives).preload && *ZCG(accel_directives).preload)) {
5267
16
    return SUCCESS;
5268
16
  }
5269
5270
#ifdef ZEND_WIN32
5271
  zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Preloading is not supported on Windows");
5272
  return FAILURE;
5273
#else /* ZEND_WIN32 */
5274
5275
0
  if (UNEXPECTED(file_cache_only)) {
5276
0
    zend_accel_error(ACCEL_LOG_WARNING, "Preloading doesn't work in \"file_cache_only\" mode");
5277
0
    return SUCCESS;
5278
0
  }
5279
5280
  /* exclusive lock */
5281
0
  zend_shared_alloc_lock();
5282
5283
0
  if (ZCSG(preload_script)) {
5284
    /* Preloading was done in another process */
5285
0
    preload_load(zend_map_ptr_static_last);
5286
0
    zend_shared_alloc_unlock();
5287
0
    return SUCCESS;
5288
0
  }
5289
5290
5291
0
  pid_t pid;
5292
0
  if (accel_finish_startup_preload_subprocess(&pid) == FAILURE) {
5293
0
    zend_shared_alloc_unlock();
5294
0
    return FAILURE;
5295
0
  }
5296
5297
0
  if (pid == -1) { /* no subprocess was needed */
5298
    /* The called function unlocks the shared alloc lock */
5299
0
    return accel_finish_startup_preload(false);
5300
0
  } else if (pid == 0) { /* subprocess */
5301
0
    const zend_result ret = accel_finish_startup_preload(true);
5302
5303
0
    exit(ret == SUCCESS ? 0 : 1);
5304
0
  } else { /* parent */
5305
0
# ifdef HAVE_SIGPROCMASK
5306
    /* Interrupting the waitpid() call below with a signal would cause the
5307
     * process to exit. This is fine when the signal disposition is set to
5308
     * terminate the process, but not otherwise.
5309
     * When running the apache2handler, preloading is performed in the
5310
     * control process. SIGUSR1 and SIGHUP are used to tell the control
5311
     * process to restart children. Exiting when these signals are received
5312
     * would unexpectedly shutdown the server instead of restarting it.
5313
     * Block the USR1 and HUP signals from being delivered during the
5314
     * syscall when running the apache2handler SAPI, as these are not
5315
     * supposed to terminate the process. See GH-20051. */
5316
0
    bool is_apache2handler = strcmp(sapi_module.name, "apache2handler") == 0;
5317
0
    sigset_t set, oldset;
5318
0
    if (is_apache2handler) {
5319
0
      if (sigemptyset(&set)
5320
0
          || sigaddset(&set, SIGUSR1)
5321
0
          || sigaddset(&set, SIGHUP)) {
5322
0
        ZEND_UNREACHABLE();
5323
0
      }
5324
0
      if (sigprocmask(SIG_BLOCK, &set, &oldset)) {
5325
0
        ZEND_UNREACHABLE();
5326
0
      }
5327
0
    }
5328
0
# endif
5329
5330
0
    int status;
5331
0
    if (waitpid(pid, &status, 0) < 0) {
5332
0
      zend_shared_alloc_unlock();
5333
0
      zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to waitpid(%d)", pid);
5334
0
    }
5335
5336
0
# ifdef HAVE_SIGPROCMASK
5337
0
    if (is_apache2handler) {
5338
0
      if (sigprocmask(SIG_SETMASK, &oldset, NULL)) {
5339
0
        ZEND_UNREACHABLE();
5340
0
      }
5341
0
    }
5342
0
# endif
5343
5344
0
    if (ZCSG(preload_script)) {
5345
0
      preload_load(zend_map_ptr_static_last);
5346
0
    }
5347
5348
0
    zend_shared_alloc_unlock();
5349
5350
0
    if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
5351
0
      return SUCCESS;
5352
0
    } else {
5353
0
      return FAILURE;
5354
0
    }
5355
0
  }
5356
0
#endif /* ZEND_WIN32 */
5357
0
}
5358
5359
300k
static void accel_activate(void) {
5360
300k
  if (ZCG(preloaded_internal_run_time_cache)) {
5361
0
    memset(ZCG(preloaded_internal_run_time_cache), 0, ZCG(preloaded_internal_run_time_cache_size));
5362
0
  }
5363
300k
}
5364
5365
static zend_extension opcache_extension_entry = {
5366
  ACCELERATOR_PRODUCT_NAME,               /* name */
5367
  PHP_VERSION,              /* version */
5368
  "Zend by Perforce",         /* author */
5369
  "https://www.zend.com/",          /* URL */
5370
  "Copyright ©",           /* copyright */
5371
  accel_startup,                /* startup */
5372
  NULL,                 /* shutdown */
5373
  accel_activate,             /* per-script activation */
5374
#ifdef HAVE_JIT
5375
  accel_deactivate,                       /* per-script deactivation */
5376
#else
5377
  NULL,                 /* per-script deactivation */
5378
#endif
5379
  NULL,                 /* message handler */
5380
  NULL,                 /* op_array handler */
5381
  NULL,                 /* extended statement handler */
5382
  NULL,                 /* extended fcall begin handler */
5383
  NULL,                 /* extended fcall end handler */
5384
  NULL,                 /* op_array ctor */
5385
  NULL,                 /* op_array dtor */
5386
  STANDARD_ZEND_EXTENSION_PROPERTIES
5387
};