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