Coverage Report

Created: 2026-10-28 06:20

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