Coverage Report

Created: 2026-09-14 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/opcache/zend_shared_alloc.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
#if defined(__linux__)
21
# include <php_config.h>
22
# if defined(HAVE_MEMFD_CREATE)
23
#  include <sys/mman.h>
24
# endif
25
#endif
26
27
#include <errno.h>
28
#include "ZendAccelerator.h"
29
#include "zend_shared_alloc.h"
30
#ifdef HAVE_UNISTD_H
31
# include <unistd.h>
32
#endif
33
#include <fcntl.h>
34
#ifndef ZEND_WIN32
35
# include <sys/types.h>
36
# include <signal.h>
37
# include <sys/stat.h>
38
# include <stdio.h>
39
#endif
40
41
#ifdef HAVE_MPROTECT
42
# include "sys/mman.h"
43
#endif
44
45
0
#define SEM_FILENAME_PREFIX ".ZendSem."
46
48
#define S_H(s) g_shared_alloc_handler->s
47
48
/* True globals */
49
/* old/new mapping. We can use true global even for ZTS because its usage
50
   is wrapped with exclusive lock anyway */
51
static const zend_shared_memory_handlers *g_shared_alloc_handler = NULL;
52
static const char *g_shared_model;
53
/* pointer to globals allocated in SHM and shared across processes */
54
ZEND_EXT_API zend_smm_shared_globals *smm_shared_globals;
55
56
#ifdef ZTS
57
static MUTEX_T zts_protect_lock;
58
static uint32_t zts_unprotected_threads;
59
#endif
60
61
#ifndef ZEND_WIN32
62
#ifdef ZTS
63
static MUTEX_T zts_lock;
64
#endif
65
int lock_file = -1;
66
static char lockfile_name[MAXPATHLEN];
67
#endif
68
69
static const zend_shared_memory_handler_entry handler_table[] = {
70
#ifdef USE_MMAP
71
  { "mmap", &zend_alloc_mmap_handlers },
72
#endif
73
#ifdef USE_SHM
74
  { "shm", &zend_alloc_shm_handlers },
75
#endif
76
#ifdef USE_SHM_OPEN
77
  { "posix", &zend_alloc_posix_handlers },
78
#endif
79
#ifdef ZEND_WIN32
80
  { "win32", &zend_alloc_win32_handlers },
81
#endif
82
  { NULL, NULL}
83
};
84
85
#ifndef ZEND_WIN32
86
void zend_shared_alloc_create_lock(char *lockfile_path)
87
16
{
88
16
  int val;
89
90
#ifdef ZTS
91
  zts_lock = tsrm_mutex_alloc();
92
#endif
93
94
16
#if defined(__linux__) && defined(HAVE_MEMFD_CREATE)
95
  /* on Linux, we can use a memfd instead of a "real" file, so
96
   * we can do this without a writable filesystem and without
97
   * needing to clean up */
98
  /* note: FreeBSD has memfd_create(), too, but fcntl(F_SETLKW)
99
   * on it fails with EBADF, therefore we use this only on
100
   * Linux */
101
16
  lock_file = memfd_create("opcache_lock", MFD_CLOEXEC);
102
16
  if (lock_file >= 0)
103
16
    return;
104
0
#endif
105
106
0
#ifdef O_TMPFILE
107
0
  lock_file = open(lockfile_path, O_RDWR | O_TMPFILE | O_EXCL | O_CLOEXEC, 0666);
108
  /* lack of O_TMPFILE support results in many possible errors
109
   * use it only when open returns a non-negative integer */
110
0
  if (lock_file >= 0) {
111
0
    return;
112
0
  }
113
0
#endif
114
115
0
  snprintf(lockfile_name, sizeof(lockfile_name), "%s/%sXXXXXX", lockfile_path, SEM_FILENAME_PREFIX);
116
0
  lock_file = mkstemp(lockfile_name);
117
0
  if (lock_file == -1) {
118
0
    zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Unable to create opcache lock file in %s: %s (%d)", lockfile_path, strerror(errno), errno);
119
0
  }
120
121
0
  if (fchmod(lock_file, 0666) == -1) {
122
0
    zend_accel_error(ACCEL_LOG_WARNING, "Unable to change opcache lock file permissions in %s: %s (%d)", lockfile_path, strerror(errno), errno);
123
0
  }
124
125
0
  val = fcntl(lock_file, F_GETFD, 0);
126
0
  val |= FD_CLOEXEC;
127
0
  fcntl(lock_file, F_SETFD, val);
128
129
0
  unlink(lockfile_name);
130
0
}
131
#endif
132
133
static void no_memory_bailout(size_t allocate_size, const char *error)
134
0
{
135
0
  zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Unable to allocate shared memory segment of %zu bytes: %s: %s (%d)", allocate_size, error?error:"unknown", strerror(errno), errno );
136
0
}
137
138
static void copy_shared_segments(void *to, void *from, int count, int size)
139
16
{
140
16
  zend_shared_segment **shared_segments_v = (zend_shared_segment **)to;
141
16
  void *shared_segments_to_p = ((char *)to + count*(sizeof(void *)));
142
16
  void *shared_segments_from_p = from;
143
16
  int i;
144
145
32
  for (i = 0; i < count; i++) {
146
16
    shared_segments_v[i] =  shared_segments_to_p;
147
16
    memcpy(shared_segments_to_p, shared_segments_from_p, size);
148
16
    shared_segments_to_p = ((char *)shared_segments_to_p + size);
149
16
    shared_segments_from_p = ((char *)shared_segments_from_p + size);
150
16
  }
151
16
}
152
153
static int zend_shared_alloc_try(const zend_shared_memory_handler_entry *he, size_t requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, const char **error_in)
154
16
{
155
16
  int res;
156
16
  g_shared_alloc_handler = he->handler;
157
16
  g_shared_model = he->name;
158
16
  ZSMMG(shared_segments) = NULL;
159
16
  ZSMMG(shared_segments_count) = 0;
160
161
16
  res = S_H(create_segments)(requested_size, shared_segments_p, shared_segments_count, error_in);
162
163
16
  if (res) {
164
    /* this model works! */
165
16
    return res;
166
16
  }
167
0
  if (*shared_segments_p) {
168
0
    int i;
169
    /* cleanup */
170
0
    for (i = 0; i < *shared_segments_count; i++) {
171
0
      if ((*shared_segments_p)[i]->p && (*shared_segments_p)[i]->p != (void *)-1) {
172
0
        S_H(detach_segment)((*shared_segments_p)[i]);
173
0
      }
174
0
    }
175
0
    free(*shared_segments_p);
176
0
    *shared_segments_p = NULL;
177
0
  }
178
0
  g_shared_alloc_handler = NULL;
179
0
  return ALLOC_FAILURE;
180
16
}
181
182
int zend_shared_alloc_startup(size_t requested_size, size_t reserved_size)
183
16
{
184
16
  zend_shared_segment **tmp_shared_segments;
185
16
  size_t shared_segments_array_size;
186
16
  zend_smm_shared_globals tmp_shared_globals, *p_tmp_shared_globals;
187
16
  const char *error_in = NULL;
188
16
  const zend_shared_memory_handler_entry *he;
189
16
  int res = ALLOC_FAILURE;
190
16
  int i;
191
192
#ifdef ZTS
193
  zts_protect_lock = tsrm_mutex_alloc();
194
  zts_unprotected_threads = 0;
195
#endif
196
197
  /* shared_free must be valid before we call zend_shared_alloc()
198
   * - make it temporarily point to a local variable
199
   */
200
16
  smm_shared_globals = &tmp_shared_globals;
201
16
  ZSMMG(shared_free) = requested_size - reserved_size; /* goes to tmp_shared_globals.shared_free */
202
203
16
#ifndef ZEND_WIN32
204
16
  zend_shared_alloc_create_lock(ZCG(accel_directives).lockfile_path);
205
#else
206
  zend_shared_alloc_create_lock();
207
#endif
208
209
16
  if (ZCG(accel_directives).memory_model && ZCG(accel_directives).memory_model[0]) {
210
0
    const char *model = ZCG(accel_directives).memory_model;
211
    /* "cgi" is really "shm"... */
212
0
    if (strncmp(ZCG(accel_directives).memory_model, "cgi", sizeof("cgi")) == 0) {
213
0
      model = "shm";
214
0
    }
215
216
0
    for (he = handler_table; he->name; he++) {
217
0
      if (strcmp(model, he->name) == 0) {
218
0
        res = zend_shared_alloc_try(he, requested_size, &ZSMMG(shared_segments), &ZSMMG(shared_segments_count), &error_in);
219
0
        if (res) {
220
          /* this model works! */
221
0
          break;
222
0
        }
223
0
      }
224
0
    }
225
0
  }
226
227
16
  if (res == FAILED_REATTACHED) {
228
0
    smm_shared_globals = NULL;
229
0
    return res;
230
0
  }
231
#if ENABLE_FILE_CACHE_FALLBACK
232
  if (ALLOC_FALLBACK == res) {
233
    smm_shared_globals = NULL;
234
    return ALLOC_FALLBACK;
235
  }
236
#endif
237
238
16
  if (!g_shared_alloc_handler) {
239
    /* try memory handlers in order */
240
16
    if (handler_table->name == NULL) {
241
0
      return NO_SHM_BACKEND;
242
0
    }
243
16
    for (he = handler_table; he->name; he++) {
244
16
      res = zend_shared_alloc_try(he, requested_size, &ZSMMG(shared_segments), &ZSMMG(shared_segments_count), &error_in);
245
16
      if (res) {
246
        /* this model works! */
247
16
        break;
248
16
      }
249
16
    }
250
16
  }
251
252
16
  if (!g_shared_alloc_handler) {
253
0
    no_memory_bailout(requested_size, error_in);
254
0
    return ALLOC_FAILURE;
255
0
  }
256
257
16
  if (res == SUCCESSFULLY_REATTACHED) {
258
0
    return res;
259
0
  }
260
#if ENABLE_FILE_CACHE_FALLBACK
261
  if (ALLOC_FALLBACK == res) {
262
    smm_shared_globals = NULL;
263
    return ALLOC_FALLBACK;
264
  }
265
#endif
266
267
32
  for (i = 0; i < ZSMMG(shared_segments_count); i++) {
268
16
    ZSMMG(shared_segments)[i]->end = ZSMMG(shared_segments)[i]->size;
269
16
  }
270
271
16
  shared_segments_array_size = ZSMMG(shared_segments_count) * S_H(segment_type_size)();
272
273
  /* move shared_segments and shared_free to shared memory */
274
16
  ZCG(locked) = 1; /* no need to perform a real lock at this point */
275
276
16
  p_tmp_shared_globals = (zend_smm_shared_globals *) zend_shared_alloc(sizeof(zend_smm_shared_globals));
277
16
  if (!p_tmp_shared_globals) {
278
0
    zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Insufficient shared memory!");
279
0
    return ALLOC_FAILURE;
280
0
  }
281
16
  memset(p_tmp_shared_globals, 0, sizeof(zend_smm_shared_globals));
282
283
16
  tmp_shared_segments = zend_shared_alloc(shared_segments_array_size + ZSMMG(shared_segments_count) * sizeof(void *));
284
16
  if (!tmp_shared_segments) {
285
0
    zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Insufficient shared memory!");
286
0
    return ALLOC_FAILURE;
287
0
  }
288
289
16
  copy_shared_segments(tmp_shared_segments, ZSMMG(shared_segments)[0], ZSMMG(shared_segments_count), S_H(segment_type_size)());
290
291
16
  *p_tmp_shared_globals = tmp_shared_globals;
292
16
  smm_shared_globals = p_tmp_shared_globals;
293
294
16
  free(ZSMMG(shared_segments));
295
16
  ZSMMG(shared_segments) = tmp_shared_segments;
296
297
16
  ZSMMG(shared_memory_state).positions = (size_t *)zend_shared_alloc(sizeof(size_t) * ZSMMG(shared_segments_count));
298
16
  if (!ZSMMG(shared_memory_state).positions) {
299
0
    zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Insufficient shared memory!");
300
0
    return ALLOC_FAILURE;
301
0
  }
302
303
16
  if (reserved_size) {
304
0
    i = ZSMMG(shared_segments_count) - 1;
305
0
    if (ZSMMG(shared_segments)[i]->size - ZSMMG(shared_segments)[i]->pos >= reserved_size) {
306
0
      ZSMMG(shared_segments)[i]->end = ZSMMG(shared_segments)[i]->size - reserved_size;
307
0
      ZSMMG(reserved) = (char*)ZSMMG(shared_segments)[i]->p + ZSMMG(shared_segments)[i]->end;
308
0
      ZSMMG(reserved_size) = reserved_size;
309
0
    } else {
310
0
      zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Insufficient shared memory!");
311
0
      return ALLOC_FAILURE;
312
0
    }
313
0
  }
314
315
16
  ZCG(locked) = 0;
316
317
16
  return res;
318
16
}
319
320
void zend_shared_alloc_shutdown(void)
321
0
{
322
0
  zend_shared_segment **tmp_shared_segments;
323
0
  zend_shared_segment *shared_segments_buf[16];
324
0
  size_t shared_segments_array_size;
325
0
  zend_smm_shared_globals tmp_shared_globals;
326
0
  int i;
327
328
0
  tmp_shared_globals = *smm_shared_globals;
329
0
  smm_shared_globals = &tmp_shared_globals;
330
0
  shared_segments_array_size = ZSMMG(shared_segments_count) * (S_H(segment_type_size)() + sizeof(void *));
331
0
  if (shared_segments_array_size > 16) {
332
0
    tmp_shared_segments = malloc(shared_segments_array_size);
333
0
  } else {
334
0
    tmp_shared_segments = shared_segments_buf;
335
0
  }
336
0
  copy_shared_segments(tmp_shared_segments, ZSMMG(shared_segments)[0], ZSMMG(shared_segments_count), S_H(segment_type_size)());
337
0
  ZSMMG(shared_segments) = tmp_shared_segments;
338
339
0
  for (i = 0; i < ZSMMG(shared_segments_count); i++) {
340
0
    S_H(detach_segment)(ZSMMG(shared_segments)[i]);
341
0
  }
342
0
  if (shared_segments_array_size > 16) {
343
0
    free(ZSMMG(shared_segments));
344
0
  }
345
0
  ZSMMG(shared_segments) = NULL;
346
0
  g_shared_alloc_handler = NULL;
347
0
#ifndef ZEND_WIN32
348
0
  close(lock_file);
349
350
# ifdef ZTS
351
  tsrm_mutex_free(zts_lock);
352
# endif
353
0
#endif
354
#ifdef ZTS
355
  tsrm_mutex_free(zts_protect_lock);
356
#endif
357
0
}
358
359
static size_t zend_shared_alloc_get_largest_free_block(void)
360
0
{
361
0
  int i;
362
0
  size_t largest_block_size = 0;
363
364
0
  for (i = 0; i < ZSMMG(shared_segments_count); i++) {
365
0
    size_t block_size = ZSMMG(shared_segments)[i]->end - ZSMMG(shared_segments)[i]->pos;
366
367
0
    if (block_size>largest_block_size) {
368
0
      largest_block_size = block_size;
369
0
    }
370
0
  }
371
0
  return largest_block_size;
372
0
}
373
374
0
#define MIN_FREE_MEMORY 64*1024
375
376
0
#define SHARED_ALLOC_FAILED() do {   \
377
0
    zend_accel_error(ACCEL_LOG_WARNING, "Not enough free shared space to allocate %zu bytes (%zu bytes free)", size, ZSMMG(shared_free)); \
378
0
    if (zend_shared_alloc_get_largest_free_block() < MIN_FREE_MEMORY) { \
379
0
      ZSMMG(memory_exhausted) = 1; \
380
0
    } \
381
0
  } while (0)
382
383
void *zend_shared_alloc(size_t size)
384
65.3k
{
385
65.3k
  int i;
386
65.3k
  size_t block_size = ZEND_ALIGNED_SIZE(size);
387
388
65.3k
#if 1
389
65.3k
  if (!ZCG(locked)) {
390
0
    ZEND_ASSERT(0 && "Shared memory lock not obtained");
391
0
    zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Shared memory lock not obtained");
392
0
  }
393
65.3k
#endif
394
65.3k
  if (block_size > ZSMMG(shared_free)) { /* No hope to find a big-enough block */
395
0
    SHARED_ALLOC_FAILED();
396
0
    return NULL;
397
0
  }
398
65.3k
  for (i = 0; i < ZSMMG(shared_segments_count); i++) {
399
65.3k
    if (ZSMMG(shared_segments)[i]->end - ZSMMG(shared_segments)[i]->pos >= block_size) { /* found a valid block */
400
65.3k
      void *retval = (void *) (((char *) ZSMMG(shared_segments)[i]->p) + ZSMMG(shared_segments)[i]->pos);
401
402
65.3k
      ZSMMG(shared_segments)[i]->pos += block_size;
403
65.3k
      ZSMMG(shared_free) -= block_size;
404
65.3k
      ZEND_ASSERT(((uintptr_t)retval & 0x7) == 0); /* should be 8 byte aligned */
405
65.3k
      return retval;
406
65.3k
    }
407
65.3k
  }
408
0
  SHARED_ALLOC_FAILED();
409
0
  return NULL;
410
65.3k
}
411
412
static zend_always_inline zend_ulong zend_rotr3(zend_ulong key)
413
1.95M
{
414
1.95M
  return (key >> 3) | (key << ((sizeof(key) * 8) - 3));
415
1.95M
}
416
417
int zend_shared_memdup_size(void *source, size_t size)
418
245k
{
419
245k
  void *old_p;
420
245k
  zend_ulong key = (zend_ulong)source;
421
422
245k
  key = zend_rotr3(key);
423
245k
  if ((old_p = zend_hash_index_find_ptr(&ZCG(xlat_table), key)) != NULL) {
424
    /* we already duplicated this pointer */
425
90.8k
    return 0;
426
90.8k
  }
427
154k
  zend_hash_index_add_new_ptr(&ZCG(xlat_table), key, source);
428
154k
  return ZEND_ALIGNED_SIZE(size);
429
245k
}
430
431
static zend_always_inline void *_zend_shared_memdup(void *source, size_t size, bool get_xlat, bool set_xlat, bool free_source)
432
856k
{
433
856k
  void *old_p, *retval;
434
856k
  zend_ulong key;
435
436
856k
  if (get_xlat) {
437
0
    key = (zend_ulong)source;
438
0
    key = zend_rotr3(key);
439
0
    if ((old_p = zend_hash_index_find_ptr(&ZCG(xlat_table), key)) != NULL) {
440
      /* we already duplicated this pointer */
441
0
      return old_p;
442
0
    }
443
0
  }
444
856k
  retval = ZCG(mem);
445
856k
  ZCG(mem) = (void*)(((char*)ZCG(mem)) + ZEND_ALIGNED_SIZE(size));
446
856k
  memcpy(retval, source, size);
447
856k
  if (set_xlat) {
448
716k
    if (!get_xlat) {
449
716k
      key = (zend_ulong)source;
450
716k
      key = zend_rotr3(key);
451
716k
    }
452
716k
    zend_hash_index_add_new_ptr(&ZCG(xlat_table), key, retval);
453
716k
  }
454
856k
  if (free_source) {
455
333k
    efree(source);
456
333k
  }
457
856k
  return retval;
458
856k
}
459
460
void *zend_shared_memdup_get_put_free(void *source, size_t size)
461
0
{
462
0
  return _zend_shared_memdup(source, size, true, true, true);
463
0
}
464
465
void *zend_shared_memdup_put_free(void *source, size_t size)
466
227k
{
467
227k
  return _zend_shared_memdup(source, size, false, true, true);
468
227k
}
469
470
void *zend_shared_memdup_free(void *source, size_t size)
471
105k
{
472
105k
  return _zend_shared_memdup(source, size, false, false, true);
473
105k
}
474
475
void *zend_shared_memdup_get_put(void *source, size_t size)
476
0
{
477
0
  return _zend_shared_memdup(source, size, true, true, false);
478
0
}
479
480
void *zend_shared_memdup_put(void *source, size_t size)
481
488k
{
482
488k
  return _zend_shared_memdup(source, size, false, true, false);
483
488k
}
484
485
void *zend_shared_memdup(void *source, size_t size)
486
34.3k
{
487
34.3k
  return _zend_shared_memdup(source, size, false, false, false);
488
34.3k
}
489
490
void zend_shared_alloc_safe_unlock(void)
491
295k
{
492
295k
  if (ZCG(locked)) {
493
0
    zend_shared_alloc_unlock();
494
0
  }
495
295k
}
496
497
void zend_shared_alloc_lock(void)
498
126k
{
499
126k
  ZEND_ASSERT(!ZCG(locked));
500
501
126k
#ifndef ZEND_WIN32
502
126k
  struct flock mem_write_lock;
503
504
126k
  mem_write_lock.l_type = F_WRLCK;
505
126k
  mem_write_lock.l_whence = SEEK_SET;
506
126k
  mem_write_lock.l_start = 0;
507
126k
  mem_write_lock.l_len = 1;
508
509
#ifdef ZTS
510
  tsrm_mutex_lock(zts_lock);
511
#endif
512
513
#if 0
514
  /* this will happen once per process, and will un-globalize mem_write_lock */
515
  if (mem_write_lock.l_pid == -1) {
516
    mem_write_lock.l_pid = getpid();
517
  }
518
#endif
519
520
126k
  while (1) {
521
126k
    if (fcntl(lock_file, F_SETLKW, &mem_write_lock) == -1) {
522
0
      if (errno == EINTR) {
523
0
        continue;
524
0
      }
525
0
      zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Cannot create lock - %s (%d)", strerror(errno), errno);
526
0
    }
527
126k
    break;
528
126k
  }
529
#else
530
  zend_shared_alloc_lock_win32();
531
#endif
532
533
126k
  ZCG(locked) = 1;
534
126k
}
535
536
void zend_shared_alloc_unlock(void)
537
126k
{
538
126k
  ZEND_ASSERT(ZCG(locked));
539
540
126k
#ifndef ZEND_WIN32
541
126k
  struct flock mem_write_unlock;
542
543
126k
  mem_write_unlock.l_type = F_UNLCK;
544
126k
  mem_write_unlock.l_whence = SEEK_SET;
545
126k
  mem_write_unlock.l_start = 0;
546
126k
  mem_write_unlock.l_len = 1;
547
126k
#endif
548
549
126k
  ZCG(locked) = 0;
550
551
126k
#ifndef ZEND_WIN32
552
126k
  if (fcntl(lock_file, F_SETLK, &mem_write_unlock) == -1) {
553
0
    zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Cannot remove lock - %s (%d)", strerror(errno), errno);
554
0
  }
555
#ifdef ZTS
556
  tsrm_mutex_unlock(zts_lock);
557
#endif
558
#else
559
  zend_shared_alloc_unlock_win32();
560
#endif
561
126k
}
562
563
void zend_shared_alloc_init_xlat_table(void)
564
65.2k
{
565
  /* Prepare translation table */
566
65.2k
  zend_hash_init(&ZCG(xlat_table), 128, NULL, NULL, 0);
567
65.2k
}
568
569
void zend_shared_alloc_destroy_xlat_table(void)
570
65.2k
{
571
  /* Destroy translation table */
572
65.2k
  zend_hash_destroy(&ZCG(xlat_table));
573
65.2k
}
574
575
void zend_shared_alloc_clear_xlat_table(void)
576
65.2k
{
577
65.2k
  zend_hash_clean(&ZCG(xlat_table));
578
65.2k
}
579
580
uint32_t zend_shared_alloc_checkpoint_xlat_table(void)
581
0
{
582
0
  return ZCG(xlat_table).nNumUsed;
583
0
}
584
585
void zend_shared_alloc_restore_xlat_table(uint32_t checkpoint)
586
0
{
587
0
  zend_hash_discard(&ZCG(xlat_table), checkpoint);
588
0
}
589
590
void zend_shared_alloc_register_xlat_entry(const void *key_pointer, const void *value)
591
278k
{
592
278k
  zend_ulong key = (zend_ulong)key_pointer;
593
594
278k
  key = zend_rotr3(key);
595
278k
  zend_hash_index_add_new_ptr(&ZCG(xlat_table), key, (void*)value);
596
278k
}
597
598
void *zend_shared_alloc_get_xlat_entry(const void *key_pointer)
599
713k
{
600
713k
  void *retval;
601
713k
  zend_ulong key = (zend_ulong)key_pointer;
602
603
713k
  key = zend_rotr3(key);
604
713k
  if ((retval = zend_hash_index_find_ptr(&ZCG(xlat_table), key)) == NULL) {
605
505k
    return NULL;
606
505k
  }
607
207k
  return retval;
608
713k
}
609
610
size_t zend_shared_alloc_get_free_memory(void)
611
98
{
612
98
  return ZSMMG(shared_free);
613
98
}
614
615
void zend_shared_alloc_save_state(void)
616
16
{
617
16
  int i;
618
619
32
  for (i = 0; i < ZSMMG(shared_segments_count); i++) {
620
16
    ZSMMG(shared_memory_state).positions[i] = ZSMMG(shared_segments)[i]->pos;
621
16
  }
622
16
  ZSMMG(shared_memory_state).shared_free = ZSMMG(shared_free);
623
16
}
624
625
void zend_shared_alloc_restore_state(void)
626
0
{
627
0
  int i;
628
629
0
  for (i = 0; i < ZSMMG(shared_segments_count); i++) {
630
0
    ZSMMG(shared_segments)[i]->pos = ZSMMG(shared_memory_state).positions[i];
631
0
  }
632
0
  ZSMMG(shared_free) = ZSMMG(shared_memory_state).shared_free;
633
0
  ZSMMG(memory_exhausted) = 0;
634
0
  ZSMMG(wasted_shared_memory) = 0;
635
0
}
636
637
const char *zend_accel_get_shared_model(void)
638
8
{
639
8
  return g_shared_model;
640
8
}
641
642
void zend_accel_shared_protect(bool protected)
643
829k
{
644
829k
#if defined(HAVE_MPROTECT) || defined(ZEND_WIN32)
645
829k
  int i;
646
647
829k
  if (!smm_shared_globals) {
648
0
    return;
649
0
  }
650
651
# ifdef ZTS
652
  /* Memory protection is process-wide, so overlapping writers must be tracked across threads. */
653
  tsrm_mutex_lock(zts_protect_lock);
654
  if (protected) {
655
    if (ZCG(unprotect_depth) && --ZCG(unprotect_depth) == 0) {
656
      ZEND_ASSERT(zts_unprotected_threads > 0);
657
      zts_unprotected_threads--;
658
    }
659
    if (zts_unprotected_threads) {
660
      tsrm_mutex_unlock(zts_protect_lock);
661
      return;
662
    }
663
  } else if (ZCG(unprotect_depth)++ == 0) {
664
    zts_unprotected_threads++;
665
  }
666
# endif
667
668
829k
# ifdef HAVE_MPROTECT
669
829k
  const int mode = protected ? PROT_READ : PROT_READ|PROT_WRITE;
670
671
1.65M
  for (i = 0; i < ZSMMG(shared_segments_count); i++) {
672
829k
    mprotect(ZSMMG(shared_segments)[i]->p, ZSMMG(shared_segments)[i]->end, mode);
673
829k
  }
674
# elif defined(ZEND_WIN32)
675
  const int mode = protected ? PAGE_READONLY : PAGE_READWRITE;
676
677
  for (i = 0; i < ZSMMG(shared_segments_count); i++) {
678
    DWORD oldProtect;
679
    if (!VirtualProtect(ZSMMG(shared_segments)[i]->p, ZSMMG(shared_segments)[i]->end, mode, &oldProtect)) {
680
      zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Failed to protect memory");
681
    }
682
  }
683
# endif
684
685
# ifdef ZTS
686
  tsrm_mutex_unlock(zts_protect_lock);
687
# endif
688
829k
#endif
689
829k
}
690
691
bool zend_accel_in_shm(void *ptr)
692
92.2k
{
693
92.2k
  int i;
694
695
92.2k
  if (!smm_shared_globals) {
696
0
    return false;
697
0
  }
698
699
181k
  for (i = 0; i < ZSMMG(shared_segments_count); i++) {
700
92.2k
    if ((char*)ptr >= (char*)ZSMMG(shared_segments)[i]->p &&
701
47.3k
        (char*)ptr < (char*)ZSMMG(shared_segments)[i]->p + ZSMMG(shared_segments)[i]->end) {
702
3.06k
      return true;
703
3.06k
    }
704
92.2k
  }
705
89.1k
  return false;
706
92.2k
}