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@zend.com> |
16 | Zeev Suraski <zeev@zend.com> |
17 | Stanislav Malyshev <stas@zend.com> |
18 | Dmitry Stogov <dmitry@zend.com> |
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
341 if ((old_p = zend_hash_index_find_ptr(&ZCG(xlat_table), (zend_ulong)source)) != NULL) {
342 /* we already duplicated this pointer */
343 return 0;
344 }
345 zend_shared_alloc_register_xlat_entry(source, source);
346 return ZEND_ALIGNED_SIZE(size);
347 }
348
_zend_shared_memdup(void * source,size_t size,zend_bool free_source)349 void *_zend_shared_memdup(void *source, size_t size, zend_bool free_source)
350 {
351 void *old_p, *retval;
352
353 if ((old_p = zend_hash_index_find_ptr(&ZCG(xlat_table), (zend_ulong)source)) != NULL) {
354 /* we already duplicated this pointer */
355 return old_p;
356 }
357 retval = ZCG(mem);
358 ZCG(mem) = (void*)(((char*)ZCG(mem)) + ZEND_ALIGNED_SIZE(size));
359 memcpy(retval, source, size);
360 zend_shared_alloc_register_xlat_entry(source, retval);
361 if (free_source) {
362 efree(source);
363 }
364 return retval;
365 }
366
zend_shared_alloc_safe_unlock(void)367 void zend_shared_alloc_safe_unlock(void)
368 {
369 if (ZCG(locked)) {
370 zend_shared_alloc_unlock();
371 }
372 }
373
zend_shared_alloc_lock(void)374 void zend_shared_alloc_lock(void)
375 {
376 #ifndef ZEND_WIN32
377 struct flock mem_write_lock;
378
379 mem_write_lock.l_type = F_WRLCK;
380 mem_write_lock.l_whence = SEEK_SET;
381 mem_write_lock.l_start = 0;
382 mem_write_lock.l_len = 1;
383
384 #ifdef ZTS
385 tsrm_mutex_lock(zts_lock);
386 #endif
387
388 #if 0
389 /* this will happen once per process, and will un-globalize mem_write_lock */
390 if (mem_write_lock.l_pid == -1) {
391 mem_write_lock.l_pid = getpid();
392 }
393 #endif
394
395 while (1) {
396 if (fcntl(lock_file, F_SETLKW, &mem_write_lock) == -1) {
397 if (errno == EINTR) {
398 continue;
399 }
400 zend_accel_error(ACCEL_LOG_ERROR, "Cannot create lock - %s (%d)", strerror(errno), errno);
401 }
402 break;
403 }
404 #else
405 zend_shared_alloc_lock_win32();
406 #endif
407
408 ZCG(locked) = 1;
409 }
410
zend_shared_alloc_unlock(void)411 void zend_shared_alloc_unlock(void)
412 {
413 #ifndef ZEND_WIN32
414 struct flock mem_write_unlock;
415
416 mem_write_unlock.l_type = F_UNLCK;
417 mem_write_unlock.l_whence = SEEK_SET;
418 mem_write_unlock.l_start = 0;
419 mem_write_unlock.l_len = 1;
420 #endif
421
422 ZCG(locked) = 0;
423
424 #ifndef ZEND_WIN32
425 if (fcntl(lock_file, F_SETLK, &mem_write_unlock) == -1) {
426 zend_accel_error(ACCEL_LOG_ERROR, "Cannot remove lock - %s (%d)", strerror(errno), errno);
427 }
428 #ifdef ZTS
429 tsrm_mutex_unlock(zts_lock);
430 #endif
431 #else
432 zend_shared_alloc_unlock_win32();
433 #endif
434 }
435
zend_shared_alloc_init_xlat_table(void)436 void zend_shared_alloc_init_xlat_table(void)
437 {
438 /* Prepare translation table */
439 zend_hash_init(&ZCG(xlat_table), 128, NULL, NULL, 0);
440 }
441
zend_shared_alloc_destroy_xlat_table(void)442 void zend_shared_alloc_destroy_xlat_table(void)
443 {
444 /* Destroy translation table */
445 zend_hash_destroy(&ZCG(xlat_table));
446 }
447
zend_shared_alloc_clear_xlat_table(void)448 void zend_shared_alloc_clear_xlat_table(void)
449 {
450 zend_hash_clean(&ZCG(xlat_table));
451 }
452
zend_shared_alloc_register_xlat_entry(const void * old,const void * new)453 void zend_shared_alloc_register_xlat_entry(const void *old, const void *new)
454 {
455 zend_hash_index_add_new_ptr(&ZCG(xlat_table), (zend_ulong)old, (void*)new);
456 }
457
zend_shared_alloc_get_xlat_entry(const void * old)458 void *zend_shared_alloc_get_xlat_entry(const void *old)
459 {
460 void *retval;
461
462 if ((retval = zend_hash_index_find_ptr(&ZCG(xlat_table), (zend_ulong)old)) == NULL) {
463 return NULL;
464 }
465 return retval;
466 }
467
zend_shared_alloc_get_free_memory(void)468 size_t zend_shared_alloc_get_free_memory(void)
469 {
470 return ZSMMG(shared_free);
471 }
472
zend_shared_alloc_save_state(void)473 void zend_shared_alloc_save_state(void)
474 {
475 int i;
476
477 for (i = 0; i < ZSMMG(shared_segments_count); i++) {
478 ZSMMG(shared_memory_state).positions[i] = ZSMMG(shared_segments)[i]->pos;
479 }
480 ZSMMG(shared_memory_state).shared_free = ZSMMG(shared_free);
481 }
482
zend_shared_alloc_restore_state(void)483 void zend_shared_alloc_restore_state(void)
484 {
485 int i;
486
487 for (i = 0; i < ZSMMG(shared_segments_count); i++) {
488 ZSMMG(shared_segments)[i]->pos = ZSMMG(shared_memory_state).positions[i];
489 }
490 ZSMMG(shared_free) = ZSMMG(shared_memory_state).shared_free;
491 ZSMMG(memory_exhausted) = 0;
492 ZSMMG(wasted_shared_memory) = 0;
493 }
494
zend_accel_get_shared_model(void)495 const char *zend_accel_get_shared_model(void)
496 {
497 return g_shared_model;
498 }
499
zend_accel_shared_protect(int mode)500 void zend_accel_shared_protect(int mode)
501 {
502 #ifdef HAVE_MPROTECT
503 int i;
504
505 if (!smm_shared_globals) {
506 return;
507 }
508
509 if (mode) {
510 mode = PROT_READ;
511 } else {
512 mode = PROT_READ|PROT_WRITE;
513 }
514
515 for (i = 0; i < ZSMMG(shared_segments_count); i++) {
516 mprotect(ZSMMG(shared_segments)[i]->p, ZSMMG(shared_segments)[i]->size, mode);
517 }
518 #endif
519 }
520
zend_accel_in_shm(void * ptr)521 int zend_accel_in_shm(void *ptr)
522 {
523 int i;
524
525 if (!smm_shared_globals) {
526 return 0;
527 }
528
529 for (i = 0; i < ZSMMG(shared_segments_count); i++) {
530 if ((char*)ptr >= (char*)ZSMMG(shared_segments)[i]->p &&
531 (char*)ptr < (char*)ZSMMG(shared_segments)[i]->p + ZSMMG(shared_segments)[i]->size) {
532 return 1;
533 }
534 }
535 return 0;
536 }
537