1 /*
2    +----------------------------------------------------------------------+
3    | Zend OPcache                                                         |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1998-2018 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    | http://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 #include <errno.h>
23 #include "ZendAccelerator.h"
24 #include "zend_shared_alloc.h"
25 #ifdef HAVE_UNISTD_H
26 # include <unistd.h>
27 #endif
28 #include <fcntl.h>
29 #ifndef ZEND_WIN32
30 # include <sys/types.h>
31 # include <dirent.h>
32 # include <signal.h>
33 # include <sys/stat.h>
34 # include <stdio.h>
35 #endif
36 
37 #ifdef HAVE_MPROTECT
38 # include "sys/mman.h"
39 #endif
40 
41 #define SEM_FILENAME_PREFIX ".ZendSem."
42 #define S_H(s) g_shared_alloc_handler->s
43 
44 /* True globals */
45 /* old/new mapping. We can use true global even for ZTS because its usage
46    is wrapped with exclusive lock anyway */
47 static const zend_shared_memory_handlers *g_shared_alloc_handler = NULL;
48 static const char *g_shared_model;
49 /* pointer to globals allocated in SHM and shared across processes */
50 zend_smm_shared_globals *smm_shared_globals;
51 
52 #ifndef ZEND_WIN32
53 #ifdef ZTS
54 static MUTEX_T zts_lock;
55 #endif
56 int lock_file;
57 static char lockfile_name[MAXPATHLEN];
58 #endif
59 
60 static const zend_shared_memory_handler_entry handler_table[] = {
61 #ifdef USE_MMAP
62 	{ "mmap", &zend_alloc_mmap_handlers },
63 #endif
64 #ifdef USE_SHM
65 	{ "shm", &zend_alloc_shm_handlers },
66 #endif
67 #ifdef USE_SHM_OPEN
68 	{ "posix", &zend_alloc_posix_handlers },
69 #endif
70 #ifdef ZEND_WIN32
71 	{ "win32", &zend_alloc_win32_handlers },
72 #endif
73 	{ NULL, NULL}
74 };
75 
76 #ifndef ZEND_WIN32
zend_shared_alloc_create_lock(char * lockfile_path)77 void zend_shared_alloc_create_lock(char *lockfile_path)
78 {
79 	int val;
80 
81 #ifdef ZTS
82     zts_lock = tsrm_mutex_alloc();
83 #endif
84 
85 	snprintf(lockfile_name, sizeof(lockfile_name), "%s/%sXXXXXX", lockfile_path, SEM_FILENAME_PREFIX);
86 	lock_file = mkstemp(lockfile_name);
87 	fchmod(lock_file, 0666);
88 
89 	if (lock_file == -1) {
90 		zend_accel_error(ACCEL_LOG_FATAL, "Unable to create lock file: %s (%d)", strerror(errno), errno);
91 	}
92 	val = fcntl(lock_file, F_GETFD, 0);
93 	val |= FD_CLOEXEC;
94 	fcntl(lock_file, F_SETFD, val);
95 
96 	unlink(lockfile_name);
97 }
98 #endif
99 
no_memory_bailout(size_t allocate_size,char * error)100 static void no_memory_bailout(size_t allocate_size, char *error)
101 {
102 	zend_accel_error(ACCEL_LOG_FATAL, "Unable to allocate shared memory segment of %zu bytes: %s: %s (%d)", allocate_size, error?error:"unknown", strerror(errno), errno );
103 }
104 
copy_shared_segments(void * to,void * from,int count,int size)105 static void copy_shared_segments(void *to, void *from, int count, int size)
106 {
107 	zend_shared_segment **shared_segments_v = (zend_shared_segment **)to;
108 	void *shared_segments_to_p = ((char *)to + count*(sizeof(void *)));
109 	void *shared_segments_from_p = from;
110 	int i;
111 
112 	for (i = 0; i < count; i++) {
113 		shared_segments_v[i] = 	shared_segments_to_p;
114 		memcpy(shared_segments_to_p, shared_segments_from_p, size);
115 		shared_segments_to_p = ((char *)shared_segments_to_p + size);
116 		shared_segments_from_p = ((char *)shared_segments_from_p + size);
117 	}
118 }
119 
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,char ** error_in)120 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, char **error_in)
121 {
122 	int res;
123 	g_shared_alloc_handler = he->handler;
124 	g_shared_model = he->name;
125 	ZSMMG(shared_segments) = NULL;
126 	ZSMMG(shared_segments_count) = 0;
127 
128 	res = S_H(create_segments)(requested_size, shared_segments_p, shared_segments_count, error_in);
129 
130 	if (res) {
131 		/* this model works! */
132 		return res;
133 	}
134 	if (*shared_segments_p) {
135 		int i;
136 		/* cleanup */
137 		for (i = 0; i < *shared_segments_count; i++) {
138 			if ((*shared_segments_p)[i]->p && (*shared_segments_p)[i]->p != (void *)-1) {
139 				S_H(detach_segment)((*shared_segments_p)[i]);
140 			}
141 		}
142 		free(*shared_segments_p);
143 		*shared_segments_p = NULL;
144 	}
145 	g_shared_alloc_handler = NULL;
146 	return ALLOC_FAILURE;
147 }
148 
zend_shared_alloc_startup(size_t requested_size)149 int zend_shared_alloc_startup(size_t requested_size)
150 {
151 	zend_shared_segment **tmp_shared_segments;
152 	size_t shared_segments_array_size;
153 	zend_smm_shared_globals tmp_shared_globals, *p_tmp_shared_globals;
154 	char *error_in = NULL;
155 	const zend_shared_memory_handler_entry *he;
156 	int res = ALLOC_FAILURE;
157 
158 
159 	/* shared_free must be valid before we call zend_shared_alloc()
160 	 * - make it temporarily point to a local variable
161 	 */
162 	smm_shared_globals = &tmp_shared_globals;
163 	ZSMMG(shared_free) = requested_size; /* goes to tmp_shared_globals.shared_free */
164 
165 #ifndef ZEND_WIN32
166 	zend_shared_alloc_create_lock(ZCG(accel_directives).lockfile_path);
167 #else
168 	zend_shared_alloc_create_lock();
169 #endif
170 
171 	if (ZCG(accel_directives).memory_model && ZCG(accel_directives).memory_model[0]) {
172 		char *model = ZCG(accel_directives).memory_model;
173 		/* "cgi" is really "shm"... */
174 		if (strncmp(ZCG(accel_directives).memory_model, "cgi", sizeof("cgi")) == 0) {
175 			model = "shm";
176 		}
177 
178 		for (he = handler_table; he->name; he++) {
179 			if (strcmp(model, he->name) == 0) {
180 				res = zend_shared_alloc_try(he, requested_size, &ZSMMG(shared_segments), &ZSMMG(shared_segments_count), &error_in);
181 				if (res) {
182 					/* this model works! */
183 				}
184 				break;
185 			}
186 		}
187 	}
188 
189 	if (res == FAILED_REATTACHED) {
190 		smm_shared_globals = NULL;
191 		return res;
192 	}
193 #if ENABLE_FILE_CACHE_FALLBACK
194 	if (ALLOC_FALLBACK == res) {
195 		return ALLOC_FALLBACK;
196 	}
197 #endif
198 
199 	if (!g_shared_alloc_handler) {
200 		/* try memory handlers in order */
201 		for (he = handler_table; he->name; he++) {
202 			res = zend_shared_alloc_try(he, requested_size, &ZSMMG(shared_segments), &ZSMMG(shared_segments_count), &error_in);
203 			if (res) {
204 				/* this model works! */
205 				break;
206 			}
207 		}
208 	}
209 
210 	if (!g_shared_alloc_handler) {
211 		no_memory_bailout(requested_size, error_in);
212 		return ALLOC_FAILURE;
213 	}
214 
215 	if (res == SUCCESSFULLY_REATTACHED) {
216 		return res;
217 	}
218 #if ENABLE_FILE_CACHE_FALLBACK
219 	if (ALLOC_FALLBACK == res) {
220 		return ALLOC_FALLBACK;
221 	}
222 #endif
223 
224 	shared_segments_array_size = ZSMMG(shared_segments_count) * S_H(segment_type_size)();
225 
226 	/* move shared_segments and shared_free to shared memory */
227 	ZCG(locked) = 1; /* no need to perform a real lock at this point */
228 	p_tmp_shared_globals = (zend_smm_shared_globals *) zend_shared_alloc(sizeof(zend_smm_shared_globals));
229 	if (!p_tmp_shared_globals) {
230 		zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
231 		return ALLOC_FAILURE;
232 	}
233 	memset(p_tmp_shared_globals, 0, sizeof(zend_smm_shared_globals));
234 
235 	tmp_shared_segments = zend_shared_alloc(shared_segments_array_size + ZSMMG(shared_segments_count) * sizeof(void *));
236 	if (!tmp_shared_segments) {
237 		zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
238 		return ALLOC_FAILURE;
239 	}
240 
241 	copy_shared_segments(tmp_shared_segments, ZSMMG(shared_segments)[0], ZSMMG(shared_segments_count), S_H(segment_type_size)());
242 
243 	*p_tmp_shared_globals = tmp_shared_globals;
244 	smm_shared_globals = p_tmp_shared_globals;
245 
246 	free(ZSMMG(shared_segments));
247 	ZSMMG(shared_segments) = tmp_shared_segments;
248 
249 	ZSMMG(shared_memory_state).positions = (int *)zend_shared_alloc(sizeof(int) * ZSMMG(shared_segments_count));
250 	if (!ZSMMG(shared_memory_state).positions) {
251 		zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
252 		return ALLOC_FAILURE;
253 	}
254 
255 	ZCG(locked) = 0;
256 
257 	return res;
258 }
259 
zend_shared_alloc_shutdown(void)260 void zend_shared_alloc_shutdown(void)
261 {
262 	zend_shared_segment **tmp_shared_segments;
263 	size_t shared_segments_array_size;
264 	zend_smm_shared_globals tmp_shared_globals;
265 	int i;
266 
267 	tmp_shared_globals = *smm_shared_globals;
268 	smm_shared_globals = &tmp_shared_globals;
269 	shared_segments_array_size = ZSMMG(shared_segments_count) * (S_H(segment_type_size)() + sizeof(void *));
270 	tmp_shared_segments = emalloc(shared_segments_array_size);
271 	copy_shared_segments(tmp_shared_segments, ZSMMG(shared_segments)[0], ZSMMG(shared_segments_count), S_H(segment_type_size)());
272 	ZSMMG(shared_segments) = tmp_shared_segments;
273 
274 	for (i = 0; i < ZSMMG(shared_segments_count); i++) {
275 		S_H(detach_segment)(ZSMMG(shared_segments)[i]);
276 	}
277 	efree(ZSMMG(shared_segments));
278 	ZSMMG(shared_segments) = NULL;
279 	g_shared_alloc_handler = NULL;
280 #ifndef ZEND_WIN32
281 	close(lock_file);
282 #endif
283 }
284 
zend_shared_alloc_get_largest_free_block(void)285 static size_t zend_shared_alloc_get_largest_free_block(void)
286 {
287 	int i;
288 	size_t largest_block_size = 0;
289 
290 	for (i = 0; i < ZSMMG(shared_segments_count); i++) {
291 		size_t block_size = ZSMMG(shared_segments)[i]->size - ZSMMG(shared_segments)[i]->pos;
292 
293 		if (block_size>largest_block_size) {
294 			largest_block_size = block_size;
295 		}
296 	}
297 	return largest_block_size;
298 }
299 
300 #define MIN_FREE_MEMORY 64*1024
301 
302 #define SHARED_ALLOC_FAILED() do {		\
303 		zend_accel_error(ACCEL_LOG_WARNING, "Not enough free shared space to allocate "ZEND_LONG_FMT" bytes ("ZEND_LONG_FMT" bytes free)", (zend_long)size, (zend_long)ZSMMG(shared_free)); \
304 		if (zend_shared_alloc_get_largest_free_block() < MIN_FREE_MEMORY) { \
305 			ZSMMG(memory_exhausted) = 1; \
306 		} \
307 	} while (0)
308 
zend_shared_alloc(size_t size)309 void *zend_shared_alloc(size_t size)
310 {
311 	int i;
312 	unsigned int block_size = ZEND_ALIGNED_SIZE(size);
313 
314 #if 1
315 	if (!ZCG(locked)) {
316 		zend_accel_error(ACCEL_LOG_ERROR, "Shared memory lock not obtained");
317 	}
318 #endif
319 	if (block_size > ZSMMG(shared_free)) { /* No hope to find a big-enough block */
320 		SHARED_ALLOC_FAILED();
321 		return NULL;
322 	}
323 	for (i = 0; i < ZSMMG(shared_segments_count); i++) {
324 		if (ZSMMG(shared_segments)[i]->size - ZSMMG(shared_segments)[i]->pos >= block_size) { /* found a valid block */
325 			void *retval = (void *) (((char *) ZSMMG(shared_segments)[i]->p) + ZSMMG(shared_segments)[i]->pos);
326 
327 			ZSMMG(shared_segments)[i]->pos += block_size;
328 			ZSMMG(shared_free) -= block_size;
329 			ZEND_ASSERT(((zend_uintptr_t)retval & 0x7) == 0); /* should be 8 byte aligned */
330 			return retval;
331 		}
332 	}
333 	SHARED_ALLOC_FAILED();
334 	return NULL;
335 }
336 
zend_shared_memdup_size(void * source,size_t size)337 int zend_shared_memdup_size(void *source, size_t size)
338 {
339 	void *old_p;
340 	zend_ulong key = (zend_ulong)source;
341 
342 	key = (key >> 3) | (key << ((sizeof(key) * 8) - 3)); /* key  = _rotr(key, 3);*/
343 	if ((old_p = zend_hash_index_find_ptr(&ZCG(xlat_table), key)) != NULL) {
344 		/* we already duplicated this pointer */
345 		return 0;
346 	}
347 	zend_shared_alloc_register_xlat_entry(source, source);
348 	return ZEND_ALIGNED_SIZE(size);
349 }
350 
_zend_shared_memdup(void * source,size_t size,zend_bool free_source)351 void *_zend_shared_memdup(void *source, size_t size, zend_bool free_source)
352 {
353 	void *old_p, *retval;
354 	zend_ulong key = (zend_ulong)source;
355 
356 	key = (key >> 3) | (key << ((sizeof(key) * 8) - 3)); /* key  = _rotr(key, 3);*/
357 	if ((old_p = zend_hash_index_find_ptr(&ZCG(xlat_table), key)) != NULL) {
358 		/* we already duplicated this pointer */
359 		return old_p;
360 	}
361 	retval = ZCG(mem);
362 	ZCG(mem) = (void*)(((char*)ZCG(mem)) + ZEND_ALIGNED_SIZE(size));
363 	memcpy(retval, source, size);
364 	zend_shared_alloc_register_xlat_entry(source, retval);
365 	if (free_source) {
366 		efree(source);
367 	}
368 	return retval;
369 }
370 
zend_shared_alloc_safe_unlock(void)371 void zend_shared_alloc_safe_unlock(void)
372 {
373 	if (ZCG(locked)) {
374 		zend_shared_alloc_unlock();
375 	}
376 }
377 
zend_shared_alloc_lock(void)378 void zend_shared_alloc_lock(void)
379 {
380 #ifndef ZEND_WIN32
381 	struct flock mem_write_lock;
382 
383 	mem_write_lock.l_type = F_WRLCK;
384 	mem_write_lock.l_whence = SEEK_SET;
385 	mem_write_lock.l_start = 0;
386 	mem_write_lock.l_len = 1;
387 
388 #ifdef ZTS
389 	tsrm_mutex_lock(zts_lock);
390 #endif
391 
392 #if 0
393 	/* this will happen once per process, and will un-globalize mem_write_lock */
394 	if (mem_write_lock.l_pid == -1) {
395 		mem_write_lock.l_pid = getpid();
396 	}
397 #endif
398 
399 	while (1) {
400 		if (fcntl(lock_file, F_SETLKW, &mem_write_lock) == -1) {
401 			if (errno == EINTR) {
402 				continue;
403 			}
404 			zend_accel_error(ACCEL_LOG_ERROR, "Cannot create lock - %s (%d)", strerror(errno), errno);
405 		}
406 		break;
407 	}
408 #else
409 	zend_shared_alloc_lock_win32();
410 #endif
411 
412 	ZCG(locked) = 1;
413 }
414 
zend_shared_alloc_unlock(void)415 void zend_shared_alloc_unlock(void)
416 {
417 #ifndef ZEND_WIN32
418 	struct flock mem_write_unlock;
419 
420 	mem_write_unlock.l_type = F_UNLCK;
421 	mem_write_unlock.l_whence = SEEK_SET;
422 	mem_write_unlock.l_start = 0;
423 	mem_write_unlock.l_len = 1;
424 #endif
425 
426 	ZCG(locked) = 0;
427 
428 #ifndef ZEND_WIN32
429 	if (fcntl(lock_file, F_SETLK, &mem_write_unlock) == -1) {
430 		zend_accel_error(ACCEL_LOG_ERROR, "Cannot remove lock - %s (%d)", strerror(errno), errno);
431 	}
432 #ifdef ZTS
433 	tsrm_mutex_unlock(zts_lock);
434 #endif
435 #else
436 	zend_shared_alloc_unlock_win32();
437 #endif
438 }
439 
zend_shared_alloc_init_xlat_table(void)440 void zend_shared_alloc_init_xlat_table(void)
441 {
442 	/* Prepare translation table */
443 	zend_hash_init(&ZCG(xlat_table), 128, NULL, NULL, 0);
444 }
445 
zend_shared_alloc_destroy_xlat_table(void)446 void zend_shared_alloc_destroy_xlat_table(void)
447 {
448 	/* Destroy translation table */
449 	zend_hash_destroy(&ZCG(xlat_table));
450 }
451 
zend_shared_alloc_clear_xlat_table(void)452 void zend_shared_alloc_clear_xlat_table(void)
453 {
454 	zend_hash_clean(&ZCG(xlat_table));
455 }
456 
zend_shared_alloc_register_xlat_entry(const void * old,const void * new)457 void zend_shared_alloc_register_xlat_entry(const void *old, const void *new)
458 {
459 	zend_ulong key = (zend_ulong)old;
460 
461 	key = (key >> 3) | (key << ((sizeof(key) * 8) - 3)); /* key  = _rotr(key, 3);*/
462 	zend_hash_index_add_new_ptr(&ZCG(xlat_table), key, (void*)new);
463 }
464 
zend_shared_alloc_get_xlat_entry(const void * old)465 void *zend_shared_alloc_get_xlat_entry(const void *old)
466 {
467 	void *retval;
468 	zend_ulong key = (zend_ulong)old;
469 
470 	key = (key >> 3) | (key << ((sizeof(key) * 8) - 3)); /* key  = _rotr(key, 3);*/
471 	if ((retval = zend_hash_index_find_ptr(&ZCG(xlat_table), key)) == NULL) {
472 		return NULL;
473 	}
474 	return retval;
475 }
476 
zend_shared_alloc_get_free_memory(void)477 size_t zend_shared_alloc_get_free_memory(void)
478 {
479 	return ZSMMG(shared_free);
480 }
481 
zend_shared_alloc_save_state(void)482 void zend_shared_alloc_save_state(void)
483 {
484 	int i;
485 
486 	for (i = 0; i < ZSMMG(shared_segments_count); i++) {
487 		ZSMMG(shared_memory_state).positions[i] = ZSMMG(shared_segments)[i]->pos;
488 	}
489 	ZSMMG(shared_memory_state).shared_free = ZSMMG(shared_free);
490 }
491 
zend_shared_alloc_restore_state(void)492 void zend_shared_alloc_restore_state(void)
493 {
494 	int i;
495 
496 	for (i = 0; i < ZSMMG(shared_segments_count); i++) {
497 		ZSMMG(shared_segments)[i]->pos = ZSMMG(shared_memory_state).positions[i];
498 	}
499 	ZSMMG(shared_free) = ZSMMG(shared_memory_state).shared_free;
500 	ZSMMG(memory_exhausted) = 0;
501 	ZSMMG(wasted_shared_memory) = 0;
502 }
503 
zend_accel_get_shared_model(void)504 const char *zend_accel_get_shared_model(void)
505 {
506 	return g_shared_model;
507 }
508 
zend_accel_shared_protect(int mode)509 void zend_accel_shared_protect(int mode)
510 {
511 #ifdef HAVE_MPROTECT
512 	int i;
513 
514 	if (!smm_shared_globals) {
515 		return;
516 	}
517 
518 	if (mode) {
519 		mode = PROT_READ;
520 	} else {
521 		mode = PROT_READ|PROT_WRITE;
522 	}
523 
524 	for (i = 0; i < ZSMMG(shared_segments_count); i++) {
525 		mprotect(ZSMMG(shared_segments)[i]->p, ZSMMG(shared_segments)[i]->size, mode);
526 	}
527 #endif
528 }
529 
zend_accel_in_shm(void * ptr)530 int zend_accel_in_shm(void *ptr)
531 {
532 	int i;
533 
534 	if (!smm_shared_globals) {
535 		return 0;
536 	}
537 
538 	for (i = 0; i < ZSMMG(shared_segments_count); i++) {
539 		if ((char*)ptr >= (char*)ZSMMG(shared_segments)[i]->p &&
540 		    (char*)ptr < (char*)ZSMMG(shared_segments)[i]->p + ZSMMG(shared_segments)[i]->size) {
541 			return 1;
542 		}
543 	}
544 	return 0;
545 }
546