Coverage Report

Created: 2026-06-13 07:01

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