1 | /* |
2 | +----------------------------------------------------------------------+ |
3 | | Zend Engine | |
4 | +----------------------------------------------------------------------+ |
5 | | Copyright (c) 1998-2015 Zend Technologies Ltd. (http://www.zend.com) | |
6 | +----------------------------------------------------------------------+ |
7 | | This source file is subject to version 2.00 of the Zend 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.zend.com/license/2_00.txt. | |
11 | | If you did not receive a copy of the Zend license and are unable to | |
12 | | obtain it through the world-wide-web, please send a note to | |
13 | | license@zend.com so we can mail you a copy immediately. | |
14 | +----------------------------------------------------------------------+ |
15 | | Authors: Andi Gutmans <andi@zend.com> | |
16 | | Zeev Suraski <zeev@zend.com> | |
17 | | Dmitry Stogov <dmitry@zend.com> | |
18 | +----------------------------------------------------------------------+ |
19 | */ |
20 | |
21 | /* $Id$ */ |
22 | |
23 | #include "zend.h" |
24 | #include "zend_alloc.h" |
25 | #include "zend_globals.h" |
26 | #include "zend_operators.h" |
27 | |
28 | #ifdef HAVE_SIGNAL_H |
29 | # include <signal.h> |
30 | #endif |
31 | #ifdef HAVE_UNISTD_H |
32 | # include <unistd.h> |
33 | #endif |
34 | |
35 | #ifdef ZEND_WIN32 |
36 | # include <wincrypt.h> |
37 | # include <process.h> |
38 | #endif |
39 | |
40 | #ifndef ZEND_MM_HEAP_PROTECTION |
41 | # define ZEND_MM_HEAP_PROTECTION ZEND_DEBUG |
42 | #endif |
43 | |
44 | #ifndef ZEND_MM_SAFE_UNLINKING |
45 | # define ZEND_MM_SAFE_UNLINKING 1 |
46 | #endif |
47 | |
48 | #ifndef ZEND_MM_COOKIES |
49 | # define ZEND_MM_COOKIES ZEND_DEBUG |
50 | #endif |
51 | |
52 | #ifdef _WIN64 |
53 | # define PTR_FMT "0x%0.16I64x" |
54 | /* |
55 | #elif sizeof(long) == 8 |
56 | # define PTR_FMT "0x%0.16lx" |
57 | */ |
58 | #else |
59 | # define PTR_FMT "0x%0.8lx" |
60 | #endif |
61 | |
62 | #if ZEND_DEBUG |
63 | void zend_debug_alloc_output(char *format, ...) |
64 | { |
65 | char output_buf[256]; |
66 | va_list args; |
67 | |
68 | va_start(args, format); |
69 | vsprintf(output_buf, format, args); |
70 | va_end(args); |
71 | |
72 | #ifdef ZEND_WIN32 |
73 | OutputDebugString(output_buf); |
74 | #else |
75 | fprintf(stderr, "%s" , output_buf); |
76 | #endif |
77 | } |
78 | #endif |
79 | |
80 | #if (defined (__GNUC__) && __GNUC__ > 2 ) && !defined(__INTEL_COMPILER) && !defined(DARWIN) && !defined(__hpux) && !defined(_AIX) |
81 | static void zend_mm_panic(const char *message) __attribute__ ((noreturn)); |
82 | #endif |
83 | |
84 | static void zend_mm_panic(const char *message) |
85 | { |
86 | fprintf(stderr, "%s\n" , message); |
87 | /* See http://support.microsoft.com/kb/190351 */ |
88 | #ifdef PHP_WIN32 |
89 | fflush(stderr); |
90 | #endif |
91 | #if ZEND_DEBUG && defined(HAVE_KILL) && defined(HAVE_GETPID) |
92 | kill(getpid(), SIGSEGV); |
93 | #endif |
94 | exit(1); |
95 | } |
96 | |
97 | /*******************/ |
98 | /* Storage Manager */ |
99 | /*******************/ |
100 | |
101 | #ifdef ZEND_WIN32 |
102 | # define HAVE_MEM_WIN32 /* use VirtualAlloc() to allocate memory */ |
103 | #endif |
104 | #define HAVE_MEM_MALLOC /* use malloc() to allocate segments */ |
105 | |
106 | #include <sys/types.h> |
107 | #include <sys/stat.h> |
108 | #if HAVE_LIMITS_H |
109 | #include <limits.h> |
110 | #endif |
111 | #include <fcntl.h> |
112 | #include <errno.h> |
113 | |
114 | #if defined(HAVE_MEM_MMAP_ANON) || defined(HAVE_MEM_MMAP_ZERO) |
115 | # ifdef HAVE_MREMAP |
116 | # ifndef _GNU_SOURCE |
117 | # define _GNU_SOURCE |
118 | # endif |
119 | # ifndef __USE_GNU |
120 | # define __USE_GNU |
121 | # endif |
122 | # endif |
123 | # include <sys/mman.h> |
124 | # ifndef MAP_ANON |
125 | # ifdef MAP_ANONYMOUS |
126 | # define MAP_ANON MAP_ANONYMOUS |
127 | # endif |
128 | # endif |
129 | # ifndef MREMAP_MAYMOVE |
130 | # define MREMAP_MAYMOVE 0 |
131 | # endif |
132 | # ifndef MAP_FAILED |
133 | # define MAP_FAILED ((void*)-1) |
134 | # endif |
135 | #endif |
136 | |
137 | static zend_mm_storage* zend_mm_mem_dummy_init(void *params) |
138 | { |
139 | return malloc(sizeof(zend_mm_storage)); |
140 | } |
141 | |
142 | static void zend_mm_mem_dummy_dtor(zend_mm_storage *storage) |
143 | { |
144 | free(storage); |
145 | } |
146 | |
147 | static void zend_mm_mem_dummy_compact(zend_mm_storage *storage) |
148 | { |
149 | } |
150 | |
151 | #if defined(HAVE_MEM_MMAP_ANON) || defined(HAVE_MEM_MMAP_ZERO) |
152 | |
153 | static zend_mm_segment* zend_mm_mem_mmap_realloc(zend_mm_storage *storage, zend_mm_segment* segment, size_t size) |
154 | { |
155 | zend_mm_segment *ret; |
156 | #ifdef HAVE_MREMAP |
157 | #if defined(__NetBSD__) |
158 | /* NetBSD 5 supports mremap but takes an extra newp argument */ |
159 | ret = (zend_mm_segment*)mremap(segment, segment->size, segment, size, MREMAP_MAYMOVE); |
160 | #else |
161 | ret = (zend_mm_segment*)mremap(segment, segment->size, size, MREMAP_MAYMOVE); |
162 | #endif |
163 | if (ret == MAP_FAILED) { |
164 | #endif |
165 | ret = storage->handlers->_alloc(storage, size); |
166 | if (ret) { |
167 | memcpy(ret, segment, size > segment->size ? segment->size : size); |
168 | storage->handlers->_free(storage, segment); |
169 | } |
170 | #ifdef HAVE_MREMAP |
171 | } |
172 | #endif |
173 | return ret; |
174 | } |
175 | |
176 | static void zend_mm_mem_mmap_free(zend_mm_storage *storage, zend_mm_segment* segment) |
177 | { |
178 | munmap((void*)segment, segment->size); |
179 | } |
180 | |
181 | #endif |
182 | |
183 | #ifdef HAVE_MEM_MMAP_ANON |
184 | |
185 | static zend_mm_segment* zend_mm_mem_mmap_anon_alloc(zend_mm_storage *storage, size_t size) |
186 | { |
187 | zend_mm_segment *ret = (zend_mm_segment*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); |
188 | if (ret == MAP_FAILED) { |
189 | ret = NULL; |
190 | } |
191 | return ret; |
192 | } |
193 | |
194 | # define ZEND_MM_MEM_MMAP_ANON_DSC {"mmap_anon", zend_mm_mem_dummy_init, zend_mm_mem_dummy_dtor, zend_mm_mem_dummy_compact, zend_mm_mem_mmap_anon_alloc, zend_mm_mem_mmap_realloc, zend_mm_mem_mmap_free} |
195 | |
196 | #endif |
197 | |
198 | #ifdef HAVE_MEM_MMAP_ZERO |
199 | |
200 | static int zend_mm_dev_zero_fd = -1; |
201 | |
202 | static zend_mm_storage* zend_mm_mem_mmap_zero_init(void *params) |
203 | { |
204 | if (zend_mm_dev_zero_fd == -1) { |
205 | zend_mm_dev_zero_fd = open("/dev/zero" , O_RDWR, S_IRUSR | S_IWUSR); |
206 | } |
207 | if (zend_mm_dev_zero_fd >= 0) { |
208 | return malloc(sizeof(zend_mm_storage)); |
209 | } else { |
210 | return NULL; |
211 | } |
212 | } |
213 | |
214 | static void zend_mm_mem_mmap_zero_dtor(zend_mm_storage *storage) |
215 | { |
216 | close(zend_mm_dev_zero_fd); |
217 | free(storage); |
218 | } |
219 | |
220 | static zend_mm_segment* zend_mm_mem_mmap_zero_alloc(zend_mm_storage *storage, size_t size) |
221 | { |
222 | zend_mm_segment *ret = (zend_mm_segment*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, zend_mm_dev_zero_fd, 0); |
223 | if (ret == MAP_FAILED) { |
224 | ret = NULL; |
225 | } |
226 | return ret; |
227 | } |
228 | |
229 | # define ZEND_MM_MEM_MMAP_ZERO_DSC {"mmap_zero", zend_mm_mem_mmap_zero_init, zend_mm_mem_mmap_zero_dtor, zend_mm_mem_dummy_compact, zend_mm_mem_mmap_zero_alloc, zend_mm_mem_mmap_realloc, zend_mm_mem_mmap_free} |
230 | |
231 | #endif |
232 | |
233 | #ifdef HAVE_MEM_WIN32 |
234 | |
235 | static zend_mm_storage* zend_mm_mem_win32_init(void *params) |
236 | { |
237 | HANDLE heap = HeapCreate(HEAP_NO_SERIALIZE, 0, 0); |
238 | zend_mm_storage* storage; |
239 | |
240 | if (heap == NULL) { |
241 | return NULL; |
242 | } |
243 | storage = (zend_mm_storage*)malloc(sizeof(zend_mm_storage)); |
244 | if (storage == NULL) { |
245 | HeapDestroy(heap); |
246 | return NULL; |
247 | } |
248 | storage->data = (void*) heap; |
249 | return storage; |
250 | } |
251 | |
252 | static void zend_mm_mem_win32_dtor(zend_mm_storage *storage) |
253 | { |
254 | HeapDestroy((HANDLE)storage->data); |
255 | free(storage); |
256 | } |
257 | |
258 | static void zend_mm_mem_win32_compact(zend_mm_storage *storage) |
259 | { |
260 | HeapDestroy((HANDLE)storage->data); |
261 | storage->data = (void*)HeapCreate(HEAP_NO_SERIALIZE, 0, 0); |
262 | } |
263 | |
264 | static zend_mm_segment* zend_mm_mem_win32_alloc(zend_mm_storage *storage, size_t size) |
265 | { |
266 | return (zend_mm_segment*) HeapAlloc((HANDLE)storage->data, HEAP_NO_SERIALIZE, size); |
267 | } |
268 | |
269 | static void zend_mm_mem_win32_free(zend_mm_storage *storage, zend_mm_segment* segment) |
270 | { |
271 | HeapFree((HANDLE)storage->data, HEAP_NO_SERIALIZE, segment); |
272 | } |
273 | |
274 | static zend_mm_segment* zend_mm_mem_win32_realloc(zend_mm_storage *storage, zend_mm_segment* segment, size_t size) |
275 | { |
276 | return (zend_mm_segment*) HeapReAlloc((HANDLE)storage->data, HEAP_NO_SERIALIZE, segment, size); |
277 | } |
278 | |
279 | # define ZEND_MM_MEM_WIN32_DSC {"win32", zend_mm_mem_win32_init, zend_mm_mem_win32_dtor, zend_mm_mem_win32_compact, zend_mm_mem_win32_alloc, zend_mm_mem_win32_realloc, zend_mm_mem_win32_free} |
280 | |
281 | #endif |
282 | |
283 | #ifdef HAVE_MEM_MALLOC |
284 | |
285 | static zend_mm_segment* zend_mm_mem_malloc_alloc(zend_mm_storage *storage, size_t size) |
286 | { |
287 | return (zend_mm_segment*)malloc(size); |
288 | } |
289 | |
290 | static zend_mm_segment* zend_mm_mem_malloc_realloc(zend_mm_storage *storage, zend_mm_segment *ptr, size_t size) |
291 | { |
292 | return (zend_mm_segment*)realloc(ptr, size); |
293 | } |
294 | |
295 | static void zend_mm_mem_malloc_free(zend_mm_storage *storage, zend_mm_segment *ptr) |
296 | { |
297 | free(ptr); |
298 | } |
299 | |
300 | # define ZEND_MM_MEM_MALLOC_DSC {"malloc", zend_mm_mem_dummy_init, zend_mm_mem_dummy_dtor, zend_mm_mem_dummy_compact, zend_mm_mem_malloc_alloc, zend_mm_mem_malloc_realloc, zend_mm_mem_malloc_free} |
301 | |
302 | #endif |
303 | |
304 | static const zend_mm_mem_handlers mem_handlers[] = { |
305 | #ifdef HAVE_MEM_WIN32 |
306 | ZEND_MM_MEM_WIN32_DSC, |
307 | #endif |
308 | #ifdef HAVE_MEM_MALLOC |
309 | ZEND_MM_MEM_MALLOC_DSC, |
310 | #endif |
311 | #ifdef HAVE_MEM_MMAP_ANON |
312 | ZEND_MM_MEM_MMAP_ANON_DSC, |
313 | #endif |
314 | #ifdef HAVE_MEM_MMAP_ZERO |
315 | ZEND_MM_MEM_MMAP_ZERO_DSC, |
316 | #endif |
317 | {NULL, NULL, NULL, NULL, NULL, NULL} |
318 | }; |
319 | |
320 | # define ZEND_MM_STORAGE_DTOR() heap->storage->handlers->dtor(heap->storage) |
321 | # define ZEND_MM_STORAGE_ALLOC(size) heap->storage->handlers->_alloc(heap->storage, size) |
322 | # define ZEND_MM_STORAGE_REALLOC(ptr, size) heap->storage->handlers->_realloc(heap->storage, ptr, size) |
323 | # define ZEND_MM_STORAGE_FREE(ptr) heap->storage->handlers->_free(heap->storage, ptr) |
324 | |
325 | /****************/ |
326 | /* Heap Manager */ |
327 | /****************/ |
328 | |
329 | #define MEM_BLOCK_VALID 0x7312F8DC |
330 | #define MEM_BLOCK_FREED 0x99954317 |
331 | #define MEM_BLOCK_CACHED 0xFB8277DC |
332 | #define MEM_BLOCK_GUARD 0x2A8FCC84 |
333 | #define MEM_BLOCK_LEAK 0x6C5E8F2D |
334 | |
335 | /* mm block type */ |
336 | typedef struct _zend_mm_block_info { |
337 | #if ZEND_MM_COOKIES |
338 | size_t _cookie; |
339 | #endif |
340 | size_t _size; |
341 | size_t _prev; |
342 | } zend_mm_block_info; |
343 | |
344 | #if ZEND_DEBUG |
345 | |
346 | typedef struct _zend_mm_debug_info { |
347 | const char *filename; |
348 | uint lineno; |
349 | const char *orig_filename; |
350 | uint orig_lineno; |
351 | size_t size; |
352 | #if ZEND_MM_HEAP_PROTECTION |
353 | unsigned int start_magic; |
354 | #endif |
355 | } zend_mm_debug_info; |
356 | |
357 | #elif ZEND_MM_HEAP_PROTECTION |
358 | |
359 | typedef struct _zend_mm_debug_info { |
360 | size_t size; |
361 | unsigned int start_magic; |
362 | } zend_mm_debug_info; |
363 | |
364 | #endif |
365 | |
366 | typedef struct _zend_mm_block { |
367 | zend_mm_block_info info; |
368 | #if ZEND_DEBUG |
369 | unsigned int magic; |
370 | # ifdef ZTS |
371 | THREAD_T thread_id; |
372 | # endif |
373 | zend_mm_debug_info debug; |
374 | #elif ZEND_MM_HEAP_PROTECTION |
375 | zend_mm_debug_info debug; |
376 | #endif |
377 | } zend_mm_block; |
378 | |
379 | typedef struct _zend_mm_small_free_block { |
380 | zend_mm_block_info info; |
381 | #if ZEND_DEBUG |
382 | unsigned int magic; |
383 | # ifdef ZTS |
384 | THREAD_T thread_id; |
385 | # endif |
386 | #endif |
387 | struct _zend_mm_free_block *prev_free_block; |
388 | struct _zend_mm_free_block *next_free_block; |
389 | } zend_mm_small_free_block; |
390 | |
391 | typedef struct _zend_mm_free_block { |
392 | zend_mm_block_info info; |
393 | #if ZEND_DEBUG |
394 | unsigned int magic; |
395 | # ifdef ZTS |
396 | THREAD_T thread_id; |
397 | # endif |
398 | #endif |
399 | struct _zend_mm_free_block *prev_free_block; |
400 | struct _zend_mm_free_block *next_free_block; |
401 | |
402 | struct _zend_mm_free_block **parent; |
403 | struct _zend_mm_free_block *child[2]; |
404 | } zend_mm_free_block; |
405 | |
406 | #define ZEND_MM_NUM_BUCKETS (sizeof(size_t) << 3) |
407 | |
408 | #define ZEND_MM_CACHE 1 |
409 | #define ZEND_MM_CACHE_SIZE (ZEND_MM_NUM_BUCKETS * 4 * 1024) |
410 | |
411 | #ifndef ZEND_MM_CACHE_STAT |
412 | # define ZEND_MM_CACHE_STAT 0 |
413 | #endif |
414 | |
415 | struct _zend_mm_heap { |
416 | int use_zend_alloc; |
417 | void *(*_malloc)(size_t); |
418 | void (*_free)(void*); |
419 | void *(*_realloc)(void*, size_t); |
420 | size_t free_bitmap; |
421 | size_t large_free_bitmap; |
422 | size_t block_size; |
423 | size_t compact_size; |
424 | zend_mm_segment *segments_list; |
425 | zend_mm_storage *storage; |
426 | size_t real_size; |
427 | size_t real_peak; |
428 | size_t limit; |
429 | size_t size; |
430 | size_t peak; |
431 | size_t reserve_size; |
432 | void *reserve; |
433 | int overflow; |
434 | int internal; |
435 | #if ZEND_MM_CACHE |
436 | unsigned int cached; |
437 | zend_mm_free_block *cache[ZEND_MM_NUM_BUCKETS]; |
438 | #endif |
439 | zend_mm_free_block *free_buckets[ZEND_MM_NUM_BUCKETS*2]; |
440 | zend_mm_free_block *large_free_buckets[ZEND_MM_NUM_BUCKETS]; |
441 | zend_mm_free_block *rest_buckets[2]; |
442 | int rest_count; |
443 | #if ZEND_MM_CACHE_STAT |
444 | struct { |
445 | int count; |
446 | int max_count; |
447 | int hit; |
448 | int miss; |
449 | } cache_stat[ZEND_MM_NUM_BUCKETS+1]; |
450 | #endif |
451 | }; |
452 | |
453 | #define ZEND_MM_SMALL_FREE_BUCKET(heap, index) \ |
454 | (zend_mm_free_block*) ((char*)&heap->free_buckets[index * 2] + \ |
455 | sizeof(zend_mm_free_block*) * 2 - \ |
456 | sizeof(zend_mm_small_free_block)) |
457 | |
458 | #define ZEND_MM_REST_BUCKET(heap) \ |
459 | (zend_mm_free_block*)((char*)&heap->rest_buckets[0] + \ |
460 | sizeof(zend_mm_free_block*) * 2 - \ |
461 | sizeof(zend_mm_small_free_block)) |
462 | |
463 | #define ZEND_MM_REST_BLOCK ((zend_mm_free_block**)(zend_uintptr_t)(1)) |
464 | |
465 | #define ZEND_MM_MAX_REST_BLOCKS 16 |
466 | |
467 | #if ZEND_MM_COOKIES |
468 | |
469 | static unsigned int _zend_mm_cookie = 0; |
470 | |
471 | # define ZEND_MM_COOKIE(block) \ |
472 | (((size_t)(block)) ^ _zend_mm_cookie) |
473 | # define ZEND_MM_SET_COOKIE(block) \ |
474 | (block)->info._cookie = ZEND_MM_COOKIE(block) |
475 | # define ZEND_MM_CHECK_COOKIE(block) \ |
476 | if (UNEXPECTED((block)->info._cookie != ZEND_MM_COOKIE(block))) { \ |
477 | zend_mm_panic("zend_mm_heap corrupted"); \ |
478 | } |
479 | #else |
480 | # define ZEND_MM_SET_COOKIE(block) |
481 | # define ZEND_MM_CHECK_COOKIE(block) |
482 | #endif |
483 | |
484 | /* Default memory segment size */ |
485 | #define ZEND_MM_SEG_SIZE (256 * 1024) |
486 | |
487 | /* Reserved space for error reporting in case of memory overflow */ |
488 | #define ZEND_MM_RESERVE_SIZE (8*1024) |
489 | |
490 | #ifdef _WIN64 |
491 | # define ZEND_MM_LONG_CONST(x) (x##i64) |
492 | #else |
493 | # define ZEND_MM_LONG_CONST(x) (x##L) |
494 | #endif |
495 | |
496 | #define ZEND_MM_TYPE_MASK ZEND_MM_LONG_CONST(0x3) |
497 | |
498 | #define ZEND_MM_FREE_BLOCK ZEND_MM_LONG_CONST(0x0) |
499 | #define ZEND_MM_USED_BLOCK ZEND_MM_LONG_CONST(0x1) |
500 | #define ZEND_MM_GUARD_BLOCK ZEND_MM_LONG_CONST(0x3) |
501 | |
502 | #define ZEND_MM_BLOCK(b, type, size) do { \ |
503 | size_t _size = (size); \ |
504 | (b)->info._size = (type) | _size; \ |
505 | ZEND_MM_BLOCK_AT(b, _size)->info._prev = (type) | _size; \ |
506 | ZEND_MM_SET_COOKIE(b); \ |
507 | } while (0); |
508 | #define ZEND_MM_LAST_BLOCK(b) do { \ |
509 | (b)->info._size = ZEND_MM_GUARD_BLOCK | ZEND_MM_ALIGNED_HEADER_SIZE; \ |
510 | ZEND_MM_SET_MAGIC(b, MEM_BLOCK_GUARD); \ |
511 | } while (0); |
512 | #define ZEND_MM_BLOCK_SIZE(b) ((b)->info._size & ~ZEND_MM_TYPE_MASK) |
513 | #define ZEND_MM_IS_FREE_BLOCK(b) (!((b)->info._size & ZEND_MM_USED_BLOCK)) |
514 | #define ZEND_MM_IS_USED_BLOCK(b) ((b)->info._size & ZEND_MM_USED_BLOCK) |
515 | #define ZEND_MM_IS_GUARD_BLOCK(b) (((b)->info._size & ZEND_MM_TYPE_MASK) == ZEND_MM_GUARD_BLOCK) |
516 | |
517 | #define ZEND_MM_NEXT_BLOCK(b) ZEND_MM_BLOCK_AT(b, ZEND_MM_BLOCK_SIZE(b)) |
518 | #define ZEND_MM_PREV_BLOCK(b) ZEND_MM_BLOCK_AT(b, -(ssize_t)((b)->info._prev & ~ZEND_MM_TYPE_MASK)) |
519 | |
520 | #define ZEND_MM_PREV_BLOCK_IS_FREE(b) (!((b)->info._prev & ZEND_MM_USED_BLOCK)) |
521 | |
522 | #define ZEND_MM_MARK_FIRST_BLOCK(b) ((b)->info._prev = ZEND_MM_GUARD_BLOCK) |
523 | #define ZEND_MM_IS_FIRST_BLOCK(b) ((b)->info._prev == ZEND_MM_GUARD_BLOCK) |
524 | |
525 | /* optimized access */ |
526 | #define ZEND_MM_FREE_BLOCK_SIZE(b) (b)->info._size |
527 | |
528 | /* Aligned header size */ |
529 | #define ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_block)) |
530 | #define ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_small_free_block)) |
531 | #define ZEND_MM_MIN_ALLOC_BLOCK_SIZE ZEND_MM_ALIGNED_SIZE(ZEND_MM_ALIGNED_HEADER_SIZE + END_MAGIC_SIZE) |
532 | #define (ZEND_MM_MIN_ALLOC_BLOCK_SIZE>ZEND_MM_ALIGNED_FREE_HEADER_SIZE?ZEND_MM_MIN_ALLOC_BLOCK_SIZE:ZEND_MM_ALIGNED_FREE_HEADER_SIZE) |
533 | #define ZEND_MM_ALIGNED_SEGMENT_SIZE ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_segment)) |
534 | |
535 | #define ZEND_MM_MIN_SIZE ((ZEND_MM_ALIGNED_MIN_HEADER_SIZE>(ZEND_MM_ALIGNED_HEADER_SIZE+END_MAGIC_SIZE))?(ZEND_MM_ALIGNED_MIN_HEADER_SIZE-(ZEND_MM_ALIGNED_HEADER_SIZE+END_MAGIC_SIZE)):0) |
536 | |
537 | #define ZEND_MM_MAX_SMALL_SIZE ((ZEND_MM_NUM_BUCKETS<<ZEND_MM_ALIGNMENT_LOG2)+ZEND_MM_ALIGNED_MIN_HEADER_SIZE) |
538 | |
539 | #define ZEND_MM_TRUE_SIZE(size) ((size<ZEND_MM_MIN_SIZE)?(ZEND_MM_ALIGNED_MIN_HEADER_SIZE):(ZEND_MM_ALIGNED_SIZE(size+ZEND_MM_ALIGNED_HEADER_SIZE+END_MAGIC_SIZE))) |
540 | |
541 | #define ZEND_MM_BUCKET_INDEX(true_size) ((true_size>>ZEND_MM_ALIGNMENT_LOG2)-(ZEND_MM_ALIGNED_MIN_HEADER_SIZE>>ZEND_MM_ALIGNMENT_LOG2)) |
542 | |
543 | #define ZEND_MM_SMALL_SIZE(true_size) (true_size < ZEND_MM_MAX_SMALL_SIZE) |
544 | |
545 | /* Memory calculations */ |
546 | #define ZEND_MM_BLOCK_AT(blk, offset) ((zend_mm_block *) (((char *) (blk))+(offset))) |
547 | #define ZEND_MM_DATA_OF(p) ((void *) (((char *) (p))+ZEND_MM_ALIGNED_HEADER_SIZE)) |
548 | #define (blk) ZEND_MM_BLOCK_AT(blk, -(int)ZEND_MM_ALIGNED_HEADER_SIZE) |
549 | |
550 | /* Debug output */ |
551 | #if ZEND_DEBUG |
552 | |
553 | # ifdef ZTS |
554 | # define ZEND_MM_SET_THREAD_ID(block) \ |
555 | ((zend_mm_block*)(block))->thread_id = tsrm_thread_id() |
556 | # define ZEND_MM_BAD_THREAD_ID(block) ((block)->thread_id != tsrm_thread_id()) |
557 | # else |
558 | # define ZEND_MM_SET_THREAD_ID(block) |
559 | # define ZEND_MM_BAD_THREAD_ID(block) 0 |
560 | # endif |
561 | |
562 | # define ZEND_MM_VALID_PTR(block) \ |
563 | zend_mm_check_ptr(heap, block, 1 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC) |
564 | |
565 | # define ZEND_MM_SET_MAGIC(block, val) do { \ |
566 | (block)->magic = (val); \ |
567 | } while (0) |
568 | |
569 | # define ZEND_MM_CHECK_MAGIC(block, val) do { \ |
570 | if ((block)->magic != (val)) { \ |
571 | zend_mm_panic("zend_mm_heap corrupted"); \ |
572 | } \ |
573 | } while (0) |
574 | |
575 | # define ZEND_MM_SET_DEBUG_INFO(block, __size, set_valid, set_thread) do { \ |
576 | ((zend_mm_block*)(block))->debug.filename = __zend_filename; \ |
577 | ((zend_mm_block*)(block))->debug.lineno = __zend_lineno; \ |
578 | ((zend_mm_block*)(block))->debug.orig_filename = __zend_orig_filename; \ |
579 | ((zend_mm_block*)(block))->debug.orig_lineno = __zend_orig_lineno; \ |
580 | ZEND_MM_SET_BLOCK_SIZE(block, __size); \ |
581 | if (set_valid) { \ |
582 | ZEND_MM_SET_MAGIC(block, MEM_BLOCK_VALID); \ |
583 | } \ |
584 | if (set_thread) { \ |
585 | ZEND_MM_SET_THREAD_ID(block); \ |
586 | } \ |
587 | } while (0) |
588 | |
589 | #else |
590 | |
591 | # define ZEND_MM_VALID_PTR(ptr) EXPECTED(ptr != NULL) |
592 | |
593 | # define ZEND_MM_SET_MAGIC(block, val) |
594 | |
595 | # define ZEND_MM_CHECK_MAGIC(block, val) |
596 | |
597 | # define ZEND_MM_SET_DEBUG_INFO(block, __size, set_valid, set_thread) ZEND_MM_SET_BLOCK_SIZE(block, __size) |
598 | |
599 | #endif |
600 | |
601 | |
602 | #if ZEND_MM_HEAP_PROTECTION |
603 | |
604 | # define ZEND_MM_CHECK_PROTECTION(block) \ |
605 | do { \ |
606 | if ((block)->debug.start_magic != _mem_block_start_magic || \ |
607 | memcmp(ZEND_MM_END_MAGIC_PTR(block), &_mem_block_end_magic, END_MAGIC_SIZE) != 0) { \ |
608 | zend_mm_panic("zend_mm_heap corrupted"); \ |
609 | } \ |
610 | } while (0) |
611 | |
612 | # define ZEND_MM_END_MAGIC_PTR(block) \ |
613 | (((char*)(ZEND_MM_DATA_OF(block))) + ((zend_mm_block*)(block))->debug.size) |
614 | |
615 | # define END_MAGIC_SIZE sizeof(unsigned int) |
616 | |
617 | # define ZEND_MM_SET_BLOCK_SIZE(block, __size) do { \ |
618 | char *p; \ |
619 | ((zend_mm_block*)(block))->debug.size = (__size); \ |
620 | p = ZEND_MM_END_MAGIC_PTR(block); \ |
621 | ((zend_mm_block*)(block))->debug.start_magic = _mem_block_start_magic; \ |
622 | memcpy(p, &_mem_block_end_magic, END_MAGIC_SIZE); \ |
623 | } while (0) |
624 | |
625 | static unsigned int _mem_block_start_magic = 0; |
626 | static unsigned int _mem_block_end_magic = 0; |
627 | |
628 | #else |
629 | |
630 | # if ZEND_DEBUG |
631 | # define ZEND_MM_SET_BLOCK_SIZE(block, _size) \ |
632 | ((zend_mm_block*)(block))->debug.size = (_size) |
633 | # else |
634 | # define ZEND_MM_SET_BLOCK_SIZE(block, _size) |
635 | # endif |
636 | |
637 | # define ZEND_MM_CHECK_PROTECTION(block) |
638 | |
639 | # define END_MAGIC_SIZE 0 |
640 | |
641 | #endif |
642 | |
643 | #if ZEND_MM_SAFE_UNLINKING |
644 | # define ZEND_MM_CHECK_BLOCK_LINKAGE(block) \ |
645 | if (UNEXPECTED((block)->info._size != ZEND_MM_BLOCK_AT(block, ZEND_MM_FREE_BLOCK_SIZE(block))->info._prev) || \ |
646 | UNEXPECTED(!UNEXPECTED(ZEND_MM_IS_FIRST_BLOCK(block)) && \ |
647 | UNEXPECTED(ZEND_MM_PREV_BLOCK(block)->info._size != (block)->info._prev))) { \ |
648 | zend_mm_panic("zend_mm_heap corrupted"); \ |
649 | } |
650 | #define ZEND_MM_CHECK_TREE(block) \ |
651 | if (UNEXPECTED(*((block)->parent) != (block))) { \ |
652 | zend_mm_panic("zend_mm_heap corrupted"); \ |
653 | } |
654 | #else |
655 | # define ZEND_MM_CHECK_BLOCK_LINKAGE(block) |
656 | # define ZEND_MM_CHECK_TREE(block) |
657 | #endif |
658 | |
659 | #define ZEND_MM_LARGE_BUCKET_INDEX(S) zend_mm_high_bit(S) |
660 | |
661 | static void *_zend_mm_alloc_int(zend_mm_heap *heap, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) ZEND_ATTRIBUTE_MALLOC ZEND_ATTRIBUTE_ALLOC_SIZE(2); |
662 | static void _zend_mm_free_int(zend_mm_heap *heap, void *p ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); |
663 | static void *_zend_mm_realloc_int(zend_mm_heap *heap, void *p, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) ZEND_ATTRIBUTE_ALLOC_SIZE(3); |
664 | |
665 | static inline unsigned int zend_mm_high_bit(size_t _size) |
666 | { |
667 | #if defined(__GNUC__) && (defined(__native_client__) || defined(i386)) |
668 | unsigned int n; |
669 | |
670 | __asm__("bsrl %1,%0\n\t" : "=r" (n) : "rm" (_size) : "cc" ); |
671 | return n; |
672 | #elif defined(__GNUC__) && defined(__x86_64__) |
673 | unsigned long n; |
674 | |
675 | __asm__("bsr %1,%0\n\t" : "=r" (n) : "rm" (_size) : "cc" ); |
676 | return (unsigned int)n; |
677 | #elif defined(_MSC_VER) && defined(_M_IX86) |
678 | __asm { |
679 | bsr eax, _size |
680 | } |
681 | #elif defined(__GNUC__) && (defined(__arm__) || defined(__aarch64__) || defined(__powerpc__)) |
682 | return (8 * SIZEOF_SIZE_T - 1) - __builtin_clzl(_size); |
683 | #else |
684 | unsigned int n = 0; |
685 | while (_size != 0) { |
686 | _size = _size >> 1; |
687 | n++; |
688 | } |
689 | return n-1; |
690 | #endif |
691 | } |
692 | |
693 | static inline unsigned int zend_mm_low_bit(size_t _size) |
694 | { |
695 | #if defined(__GNUC__) && (defined(__native_client__) || defined(i386)) |
696 | unsigned int n; |
697 | |
698 | __asm__("bsfl %1,%0\n\t" : "=r" (n) : "rm" (_size) : "cc" ); |
699 | return n; |
700 | #elif defined(__GNUC__) && defined(__x86_64__) |
701 | unsigned long n; |
702 | |
703 | __asm__("bsf %1,%0\n\t" : "=r" (n) : "rm" (_size) : "cc" ); |
704 | return (unsigned int)n; |
705 | #elif defined(_MSC_VER) && defined(_M_IX86) |
706 | __asm { |
707 | bsf eax, _size |
708 | } |
709 | #elif defined(__GNUC__) && (defined(__arm__) || defined(__aarch64__) || defined(__powerpc__)) |
710 | return __builtin_ctzl(_size); |
711 | #else |
712 | static const int offset[16] = {4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0}; |
713 | unsigned int n; |
714 | unsigned int index = 0; |
715 | |
716 | n = offset[_size & 15]; |
717 | while (n == 4) { |
718 | _size >>= 4; |
719 | index += n; |
720 | n = offset[_size & 15]; |
721 | } |
722 | |
723 | return index + n; |
724 | #endif |
725 | } |
726 | |
727 | static inline void zend_mm_add_to_free_list(zend_mm_heap *heap, zend_mm_free_block *mm_block) |
728 | { |
729 | size_t size; |
730 | size_t index; |
731 | |
732 | ZEND_MM_SET_MAGIC(mm_block, MEM_BLOCK_FREED); |
733 | |
734 | size = ZEND_MM_FREE_BLOCK_SIZE(mm_block); |
735 | if (EXPECTED(!ZEND_MM_SMALL_SIZE(size))) { |
736 | zend_mm_free_block **p; |
737 | |
738 | index = ZEND_MM_LARGE_BUCKET_INDEX(size); |
739 | p = &heap->large_free_buckets[index]; |
740 | mm_block->child[0] = mm_block->child[1] = NULL; |
741 | if (!*p) { |
742 | *p = mm_block; |
743 | mm_block->parent = p; |
744 | mm_block->prev_free_block = mm_block->next_free_block = mm_block; |
745 | heap->large_free_bitmap |= (ZEND_MM_LONG_CONST(1) << index); |
746 | } else { |
747 | size_t m; |
748 | |
749 | for (m = size << (ZEND_MM_NUM_BUCKETS - index); ; m <<= 1) { |
750 | zend_mm_free_block *prev = *p; |
751 | |
752 | if (ZEND_MM_FREE_BLOCK_SIZE(prev) != size) { |
753 | p = &prev->child[(m >> (ZEND_MM_NUM_BUCKETS-1)) & 1]; |
754 | if (!*p) { |
755 | *p = mm_block; |
756 | mm_block->parent = p; |
757 | mm_block->prev_free_block = mm_block->next_free_block = mm_block; |
758 | break; |
759 | } |
760 | } else { |
761 | zend_mm_free_block *next = prev->next_free_block; |
762 | |
763 | prev->next_free_block = next->prev_free_block = mm_block; |
764 | mm_block->next_free_block = next; |
765 | mm_block->prev_free_block = prev; |
766 | mm_block->parent = NULL; |
767 | break; |
768 | } |
769 | } |
770 | } |
771 | } else { |
772 | zend_mm_free_block *prev, *next; |
773 | |
774 | index = ZEND_MM_BUCKET_INDEX(size); |
775 | |
776 | prev = ZEND_MM_SMALL_FREE_BUCKET(heap, index); |
777 | if (prev->prev_free_block == prev) { |
778 | heap->free_bitmap |= (ZEND_MM_LONG_CONST(1) << index); |
779 | } |
780 | next = prev->next_free_block; |
781 | |
782 | mm_block->prev_free_block = prev; |
783 | mm_block->next_free_block = next; |
784 | prev->next_free_block = next->prev_free_block = mm_block; |
785 | } |
786 | } |
787 | |
788 | static inline void zend_mm_remove_from_free_list(zend_mm_heap *heap, zend_mm_free_block *mm_block) |
789 | { |
790 | zend_mm_free_block *prev = mm_block->prev_free_block; |
791 | zend_mm_free_block *next = mm_block->next_free_block; |
792 | |
793 | ZEND_MM_CHECK_MAGIC(mm_block, MEM_BLOCK_FREED); |
794 | |
795 | if (EXPECTED(prev == mm_block)) { |
796 | zend_mm_free_block **rp, **cp; |
797 | |
798 | #if ZEND_MM_SAFE_UNLINKING |
799 | if (UNEXPECTED(next != mm_block)) { |
800 | zend_mm_panic("zend_mm_heap corrupted" ); |
801 | } |
802 | #endif |
803 | |
804 | rp = &mm_block->child[mm_block->child[1] != NULL]; |
805 | prev = *rp; |
806 | if (EXPECTED(prev == NULL)) { |
807 | size_t index = ZEND_MM_LARGE_BUCKET_INDEX(ZEND_MM_FREE_BLOCK_SIZE(mm_block)); |
808 | |
809 | ZEND_MM_CHECK_TREE(mm_block); |
810 | *mm_block->parent = NULL; |
811 | if (mm_block->parent == &heap->large_free_buckets[index]) { |
812 | heap->large_free_bitmap &= ~(ZEND_MM_LONG_CONST(1) << index); |
813 | } |
814 | } else { |
815 | while (*(cp = &(prev->child[prev->child[1] != NULL])) != NULL) { |
816 | prev = *cp; |
817 | rp = cp; |
818 | } |
819 | *rp = NULL; |
820 | |
821 | subst_block: |
822 | ZEND_MM_CHECK_TREE(mm_block); |
823 | *mm_block->parent = prev; |
824 | prev->parent = mm_block->parent; |
825 | if ((prev->child[0] = mm_block->child[0])) { |
826 | ZEND_MM_CHECK_TREE(prev->child[0]); |
827 | prev->child[0]->parent = &prev->child[0]; |
828 | } |
829 | if ((prev->child[1] = mm_block->child[1])) { |
830 | ZEND_MM_CHECK_TREE(prev->child[1]); |
831 | prev->child[1]->parent = &prev->child[1]; |
832 | } |
833 | } |
834 | } else { |
835 | |
836 | #if ZEND_MM_SAFE_UNLINKING |
837 | if (UNEXPECTED(prev->next_free_block != mm_block) || UNEXPECTED(next->prev_free_block != mm_block)) { |
838 | zend_mm_panic("zend_mm_heap corrupted" ); |
839 | } |
840 | #endif |
841 | |
842 | prev->next_free_block = next; |
843 | next->prev_free_block = prev; |
844 | |
845 | if (EXPECTED(ZEND_MM_SMALL_SIZE(ZEND_MM_FREE_BLOCK_SIZE(mm_block)))) { |
846 | if (EXPECTED(prev == next)) { |
847 | size_t index = ZEND_MM_BUCKET_INDEX(ZEND_MM_FREE_BLOCK_SIZE(mm_block)); |
848 | |
849 | if (EXPECTED(heap->free_buckets[index*2] == heap->free_buckets[index*2+1])) { |
850 | heap->free_bitmap &= ~(ZEND_MM_LONG_CONST(1) << index); |
851 | } |
852 | } |
853 | } else if (UNEXPECTED(mm_block->parent == ZEND_MM_REST_BLOCK)) { |
854 | heap->rest_count--; |
855 | } else if (UNEXPECTED(mm_block->parent != NULL)) { |
856 | goto subst_block; |
857 | } |
858 | } |
859 | } |
860 | |
861 | static inline void zend_mm_add_to_rest_list(zend_mm_heap *heap, zend_mm_free_block *mm_block) |
862 | { |
863 | zend_mm_free_block *prev, *next; |
864 | |
865 | while (heap->rest_count >= ZEND_MM_MAX_REST_BLOCKS) { |
866 | zend_mm_free_block *p = heap->rest_buckets[1]; |
867 | |
868 | if (!ZEND_MM_SMALL_SIZE(ZEND_MM_FREE_BLOCK_SIZE(p))) { |
869 | heap->rest_count--; |
870 | } |
871 | prev = p->prev_free_block; |
872 | next = p->next_free_block; |
873 | prev->next_free_block = next; |
874 | next->prev_free_block = prev; |
875 | zend_mm_add_to_free_list(heap, p); |
876 | } |
877 | |
878 | if (!ZEND_MM_SMALL_SIZE(ZEND_MM_FREE_BLOCK_SIZE(mm_block))) { |
879 | mm_block->parent = ZEND_MM_REST_BLOCK; |
880 | heap->rest_count++; |
881 | } |
882 | |
883 | ZEND_MM_SET_MAGIC(mm_block, MEM_BLOCK_FREED); |
884 | |
885 | prev = heap->rest_buckets[0]; |
886 | next = prev->next_free_block; |
887 | mm_block->prev_free_block = prev; |
888 | mm_block->next_free_block = next; |
889 | prev->next_free_block = next->prev_free_block = mm_block; |
890 | } |
891 | |
892 | static inline void zend_mm_init(zend_mm_heap *heap) |
893 | { |
894 | zend_mm_free_block* p; |
895 | int i; |
896 | |
897 | heap->free_bitmap = 0; |
898 | heap->large_free_bitmap = 0; |
899 | #if ZEND_MM_CACHE |
900 | heap->cached = 0; |
901 | memset(heap->cache, 0, sizeof(heap->cache)); |
902 | #endif |
903 | #if ZEND_MM_CACHE_STAT |
904 | for (i = 0; i < ZEND_MM_NUM_BUCKETS; i++) { |
905 | heap->cache_stat[i].count = 0; |
906 | } |
907 | #endif |
908 | p = ZEND_MM_SMALL_FREE_BUCKET(heap, 0); |
909 | for (i = 0; i < ZEND_MM_NUM_BUCKETS; i++) { |
910 | p->next_free_block = p; |
911 | p->prev_free_block = p; |
912 | p = (zend_mm_free_block*)((char*)p + sizeof(zend_mm_free_block*) * 2); |
913 | heap->large_free_buckets[i] = NULL; |
914 | } |
915 | heap->rest_buckets[0] = heap->rest_buckets[1] = ZEND_MM_REST_BUCKET(heap); |
916 | heap->rest_count = 0; |
917 | } |
918 | |
919 | static void zend_mm_del_segment(zend_mm_heap *heap, zend_mm_segment *segment) |
920 | { |
921 | zend_mm_segment **p = &heap->segments_list; |
922 | |
923 | while (*p != segment) { |
924 | p = &(*p)->next_segment; |
925 | } |
926 | *p = segment->next_segment; |
927 | heap->real_size -= segment->size; |
928 | ZEND_MM_STORAGE_FREE(segment); |
929 | } |
930 | |
931 | #if ZEND_MM_CACHE |
932 | static void zend_mm_free_cache(zend_mm_heap *heap) |
933 | { |
934 | int i; |
935 | |
936 | for (i = 0; i < ZEND_MM_NUM_BUCKETS; i++) { |
937 | if (heap->cache[i]) { |
938 | zend_mm_free_block *mm_block = heap->cache[i]; |
939 | |
940 | while (mm_block) { |
941 | size_t size = ZEND_MM_BLOCK_SIZE(mm_block); |
942 | zend_mm_free_block *q = mm_block->prev_free_block; |
943 | zend_mm_block *next_block = ZEND_MM_NEXT_BLOCK(mm_block); |
944 | |
945 | heap->cached -= size; |
946 | |
947 | if (ZEND_MM_PREV_BLOCK_IS_FREE(mm_block)) { |
948 | mm_block = (zend_mm_free_block*)ZEND_MM_PREV_BLOCK(mm_block); |
949 | size += ZEND_MM_FREE_BLOCK_SIZE(mm_block); |
950 | zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) mm_block); |
951 | } |
952 | if (ZEND_MM_IS_FREE_BLOCK(next_block)) { |
953 | size += ZEND_MM_FREE_BLOCK_SIZE(next_block); |
954 | zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) next_block); |
955 | } |
956 | ZEND_MM_BLOCK(mm_block, ZEND_MM_FREE_BLOCK, size); |
957 | |
958 | if (ZEND_MM_IS_FIRST_BLOCK(mm_block) && |
959 | ZEND_MM_IS_GUARD_BLOCK(ZEND_MM_NEXT_BLOCK(mm_block))) { |
960 | zend_mm_del_segment(heap, (zend_mm_segment *) ((char *)mm_block - ZEND_MM_ALIGNED_SEGMENT_SIZE)); |
961 | } else { |
962 | zend_mm_add_to_free_list(heap, (zend_mm_free_block *) mm_block); |
963 | } |
964 | |
965 | mm_block = q; |
966 | } |
967 | heap->cache[i] = NULL; |
968 | #if ZEND_MM_CACHE_STAT |
969 | heap->cache_stat[i].count = 0; |
970 | #endif |
971 | } |
972 | } |
973 | } |
974 | #endif |
975 | |
976 | #if ZEND_MM_HEAP_PROTECTION || ZEND_MM_COOKIES |
977 | static void zend_mm_random(unsigned char *buf, size_t size) /* {{{ */ |
978 | { |
979 | size_t i = 0; |
980 | unsigned char t; |
981 | |
982 | #ifdef ZEND_WIN32 |
983 | HCRYPTPROV hCryptProv; |
984 | int has_context = 0; |
985 | |
986 | if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0)) { |
987 | /* Could mean that the key container does not exist, let try |
988 | again by asking for a new one */ |
989 | if (GetLastError() == NTE_BAD_KEYSET) { |
990 | if (CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) { |
991 | has_context = 1; |
992 | } |
993 | } |
994 | } else { |
995 | has_context = 1; |
996 | } |
997 | if (has_context) { |
998 | do { |
999 | BOOL ret = CryptGenRandom(hCryptProv, size, buf); |
1000 | CryptReleaseContext(hCryptProv, 0); |
1001 | if (ret) { |
1002 | while (i < size && buf[i] != 0) { |
1003 | i++; |
1004 | } |
1005 | if (i == size) { |
1006 | return; |
1007 | } |
1008 | } |
1009 | } while (0); |
1010 | } |
1011 | #elif defined(HAVE_DEV_URANDOM) |
1012 | int fd = open("/dev/urandom" , 0); |
1013 | |
1014 | if (fd >= 0) { |
1015 | if (read(fd, buf, size) == size) { |
1016 | while (i < size && buf[i] != 0) { |
1017 | i++; |
1018 | } |
1019 | if (i == size) { |
1020 | close(fd); |
1021 | return; |
1022 | } |
1023 | } |
1024 | close(fd); |
1025 | } |
1026 | #endif |
1027 | t = (unsigned char)getpid(); |
1028 | while (i < size) { |
1029 | do { |
1030 | buf[i] = ((unsigned char)rand()) ^ t; |
1031 | } while (buf[i] == 0); |
1032 | t = buf[i++] << 1; |
1033 | } |
1034 | } |
1035 | /* }}} */ |
1036 | #endif |
1037 | |
1038 | /* Notes: |
1039 | * - This function may alter the block_sizes values to match platform alignment |
1040 | * - This function does *not* perform sanity checks on the arguments |
1041 | */ |
1042 | ZEND_API zend_mm_heap *zend_mm_startup_ex(const zend_mm_mem_handlers *handlers, size_t block_size, size_t reserve_size, int internal, void *params) |
1043 | { |
1044 | zend_mm_storage *storage; |
1045 | zend_mm_heap *heap; |
1046 | |
1047 | #if 0 |
1048 | int i; |
1049 | |
1050 | printf("ZEND_MM_ALIGNMENT=%d\n" , ZEND_MM_ALIGNMENT); |
1051 | printf("ZEND_MM_ALIGNMENT_LOG2=%d\n" , ZEND_MM_ALIGNMENT_LOG2); |
1052 | printf("ZEND_MM_MIN_SIZE=%d\n" , ZEND_MM_MIN_SIZE); |
1053 | printf("ZEND_MM_MAX_SMALL_SIZE=%d\n" , ZEND_MM_MAX_SMALL_SIZE); |
1054 | printf("ZEND_MM_ALIGNED_HEADER_SIZE=%d\n" , ZEND_MM_ALIGNED_HEADER_SIZE); |
1055 | printf("ZEND_MM_ALIGNED_FREE_HEADER_SIZE=%d\n" , ZEND_MM_ALIGNED_FREE_HEADER_SIZE); |
1056 | printf("ZEND_MM_MIN_ALLOC_BLOCK_SIZE=%d\n" , ZEND_MM_MIN_ALLOC_BLOCK_SIZE); |
1057 | printf("ZEND_MM_ALIGNED_MIN_HEADER_SIZE=%d\n" , ZEND_MM_ALIGNED_MIN_HEADER_SIZE); |
1058 | printf("ZEND_MM_ALIGNED_SEGMENT_SIZE=%d\n" , ZEND_MM_ALIGNED_SEGMENT_SIZE); |
1059 | for (i = 0; i < ZEND_MM_MAX_SMALL_SIZE; i++) { |
1060 | printf("%3d%c: %3ld %d %2ld\n" , i, (i == ZEND_MM_MIN_SIZE?'*':' '), (long)ZEND_MM_TRUE_SIZE(i), ZEND_MM_SMALL_SIZE(ZEND_MM_TRUE_SIZE(i)), (long)ZEND_MM_BUCKET_INDEX(ZEND_MM_TRUE_SIZE(i))); |
1061 | } |
1062 | exit(0); |
1063 | #endif |
1064 | |
1065 | #if ZEND_MM_HEAP_PROTECTION |
1066 | if (_mem_block_start_magic == 0) { |
1067 | zend_mm_random((unsigned char*)&_mem_block_start_magic, sizeof(_mem_block_start_magic)); |
1068 | } |
1069 | if (_mem_block_end_magic == 0) { |
1070 | zend_mm_random((unsigned char*)&_mem_block_end_magic, sizeof(_mem_block_end_magic)); |
1071 | } |
1072 | #endif |
1073 | #if ZEND_MM_COOKIES |
1074 | if (_zend_mm_cookie == 0) { |
1075 | zend_mm_random((unsigned char*)&_zend_mm_cookie, sizeof(_zend_mm_cookie)); |
1076 | } |
1077 | #endif |
1078 | |
1079 | if (zend_mm_low_bit(block_size) != zend_mm_high_bit(block_size)) { |
1080 | fprintf(stderr, "'block_size' must be a power of two\n" ); |
1081 | /* See http://support.microsoft.com/kb/190351 */ |
1082 | #ifdef PHP_WIN32 |
1083 | fflush(stderr); |
1084 | #endif |
1085 | exit(255); |
1086 | } |
1087 | storage = handlers->init(params); |
1088 | if (!storage) { |
1089 | fprintf(stderr, "Cannot initialize zend_mm storage [%s]\n" , handlers->name); |
1090 | /* See http://support.microsoft.com/kb/190351 */ |
1091 | #ifdef PHP_WIN32 |
1092 | fflush(stderr); |
1093 | #endif |
1094 | exit(255); |
1095 | } |
1096 | storage->handlers = handlers; |
1097 | |
1098 | heap = malloc(sizeof(struct _zend_mm_heap)); |
1099 | if (heap == NULL) { |
1100 | fprintf(stderr, "Cannot allocate heap for zend_mm storage [%s]\n" , handlers->name); |
1101 | #ifdef PHP_WIN32 |
1102 | fflush(stderr); |
1103 | #endif |
1104 | exit(255); |
1105 | } |
1106 | heap->storage = storage; |
1107 | heap->block_size = block_size; |
1108 | heap->compact_size = 0; |
1109 | heap->segments_list = NULL; |
1110 | zend_mm_init(heap); |
1111 | # if ZEND_MM_CACHE_STAT |
1112 | memset(heap->cache_stat, 0, sizeof(heap->cache_stat)); |
1113 | # endif |
1114 | |
1115 | heap->use_zend_alloc = 1; |
1116 | heap->real_size = 0; |
1117 | heap->overflow = 0; |
1118 | heap->real_peak = 0; |
1119 | heap->limit = ZEND_MM_LONG_CONST(1)<<(ZEND_MM_NUM_BUCKETS-2); |
1120 | heap->size = 0; |
1121 | heap->peak = 0; |
1122 | heap->internal = internal; |
1123 | heap->reserve = NULL; |
1124 | heap->reserve_size = reserve_size; |
1125 | if (reserve_size > 0) { |
1126 | heap->reserve = _zend_mm_alloc_int(heap, reserve_size ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC); |
1127 | } |
1128 | if (internal) { |
1129 | int i; |
1130 | zend_mm_free_block *p, *q, *orig; |
1131 | zend_mm_heap *mm_heap = _zend_mm_alloc_int(heap, sizeof(zend_mm_heap) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC); |
1132 | |
1133 | *mm_heap = *heap; |
1134 | |
1135 | p = ZEND_MM_SMALL_FREE_BUCKET(mm_heap, 0); |
1136 | orig = ZEND_MM_SMALL_FREE_BUCKET(heap, 0); |
1137 | for (i = 0; i < ZEND_MM_NUM_BUCKETS; i++) { |
1138 | q = p; |
1139 | while (q->prev_free_block != orig) { |
1140 | q = q->prev_free_block; |
1141 | } |
1142 | q->prev_free_block = p; |
1143 | q = p; |
1144 | while (q->next_free_block != orig) { |
1145 | q = q->next_free_block; |
1146 | } |
1147 | q->next_free_block = p; |
1148 | p = (zend_mm_free_block*)((char*)p + sizeof(zend_mm_free_block*) * 2); |
1149 | orig = (zend_mm_free_block*)((char*)orig + sizeof(zend_mm_free_block*) * 2); |
1150 | if (mm_heap->large_free_buckets[i]) { |
1151 | mm_heap->large_free_buckets[i]->parent = &mm_heap->large_free_buckets[i]; |
1152 | } |
1153 | } |
1154 | mm_heap->rest_buckets[0] = mm_heap->rest_buckets[1] = ZEND_MM_REST_BUCKET(mm_heap); |
1155 | mm_heap->rest_count = 0; |
1156 | |
1157 | free(heap); |
1158 | heap = mm_heap; |
1159 | } |
1160 | return heap; |
1161 | } |
1162 | |
1163 | ZEND_API zend_mm_heap *zend_mm_startup(void) |
1164 | { |
1165 | int i; |
1166 | size_t seg_size; |
1167 | char *mem_type = getenv("ZEND_MM_MEM_TYPE" ); |
1168 | char *tmp; |
1169 | const zend_mm_mem_handlers *handlers; |
1170 | zend_mm_heap *heap; |
1171 | |
1172 | if (mem_type == NULL) { |
1173 | i = 0; |
1174 | } else { |
1175 | for (i = 0; mem_handlers[i].name; i++) { |
1176 | if (strcmp(mem_handlers[i].name, mem_type) == 0) { |
1177 | break; |
1178 | } |
1179 | } |
1180 | if (!mem_handlers[i].name) { |
1181 | fprintf(stderr, "Wrong or unsupported zend_mm storage type '%s'\n" , mem_type); |
1182 | fprintf(stderr, " supported types:\n" ); |
1183 | /* See http://support.microsoft.com/kb/190351 */ |
1184 | #ifdef PHP_WIN32 |
1185 | fflush(stderr); |
1186 | #endif |
1187 | for (i = 0; mem_handlers[i].name; i++) { |
1188 | fprintf(stderr, " '%s'\n" , mem_handlers[i].name); |
1189 | } |
1190 | /* See http://support.microsoft.com/kb/190351 */ |
1191 | #ifdef PHP_WIN32 |
1192 | fflush(stderr); |
1193 | #endif |
1194 | exit(255); |
1195 | } |
1196 | } |
1197 | handlers = &mem_handlers[i]; |
1198 | |
1199 | tmp = getenv("ZEND_MM_SEG_SIZE" ); |
1200 | if (tmp) { |
1201 | seg_size = zend_atoi(tmp, 0); |
1202 | if (zend_mm_low_bit(seg_size) != zend_mm_high_bit(seg_size)) { |
1203 | fprintf(stderr, "ZEND_MM_SEG_SIZE must be a power of two\n" ); |
1204 | /* See http://support.microsoft.com/kb/190351 */ |
1205 | #ifdef PHP_WIN32 |
1206 | fflush(stderr); |
1207 | #endif |
1208 | exit(255); |
1209 | } else if (seg_size < ZEND_MM_ALIGNED_SEGMENT_SIZE + ZEND_MM_ALIGNED_HEADER_SIZE) { |
1210 | fprintf(stderr, "ZEND_MM_SEG_SIZE is too small\n" ); |
1211 | /* See http://support.microsoft.com/kb/190351 */ |
1212 | #ifdef PHP_WIN32 |
1213 | fflush(stderr); |
1214 | #endif |
1215 | exit(255); |
1216 | } |
1217 | } else { |
1218 | seg_size = ZEND_MM_SEG_SIZE; |
1219 | } |
1220 | |
1221 | heap = zend_mm_startup_ex(handlers, seg_size, ZEND_MM_RESERVE_SIZE, 0, NULL); |
1222 | if (heap) { |
1223 | tmp = getenv("ZEND_MM_COMPACT" ); |
1224 | if (tmp) { |
1225 | heap->compact_size = zend_atoi(tmp, 0); |
1226 | } else { |
1227 | heap->compact_size = 2 * 1024 * 1024; |
1228 | } |
1229 | } |
1230 | return heap; |
1231 | } |
1232 | |
1233 | #if ZEND_DEBUG |
1234 | static long zend_mm_find_leaks(zend_mm_segment *segment, zend_mm_block *b) |
1235 | { |
1236 | long leaks = 0; |
1237 | zend_mm_block *p, *q; |
1238 | |
1239 | p = ZEND_MM_NEXT_BLOCK(b); |
1240 | while (1) { |
1241 | if (ZEND_MM_IS_GUARD_BLOCK(p)) { |
1242 | ZEND_MM_CHECK_MAGIC(p, MEM_BLOCK_GUARD); |
1243 | segment = segment->next_segment; |
1244 | if (!segment) { |
1245 | break; |
1246 | } |
1247 | p = (zend_mm_block *) ((char *) segment + ZEND_MM_ALIGNED_SEGMENT_SIZE); |
1248 | continue; |
1249 | } |
1250 | q = ZEND_MM_NEXT_BLOCK(p); |
1251 | if (q <= p || |
1252 | (char*)q > (char*)segment + segment->size || |
1253 | p->info._size != q->info._prev) { |
1254 | zend_mm_panic("zend_mm_heap corrupted" ); |
1255 | } |
1256 | if (!ZEND_MM_IS_FREE_BLOCK(p)) { |
1257 | if (p->magic == MEM_BLOCK_VALID) { |
1258 | if (p->debug.filename==b->debug.filename && p->debug.lineno==b->debug.lineno) { |
1259 | ZEND_MM_SET_MAGIC(p, MEM_BLOCK_LEAK); |
1260 | leaks++; |
1261 | } |
1262 | #if ZEND_MM_CACHE |
1263 | } else if (p->magic == MEM_BLOCK_CACHED) { |
1264 | /* skip it */ |
1265 | #endif |
1266 | } else if (p->magic != MEM_BLOCK_LEAK) { |
1267 | zend_mm_panic("zend_mm_heap corrupted" ); |
1268 | } |
1269 | } |
1270 | p = q; |
1271 | } |
1272 | return leaks; |
1273 | } |
1274 | |
1275 | static void zend_mm_check_leaks(zend_mm_heap *heap TSRMLS_DC) |
1276 | { |
1277 | zend_mm_segment *segment = heap->segments_list; |
1278 | zend_mm_block *p, *q; |
1279 | zend_uint total = 0; |
1280 | |
1281 | if (!segment) { |
1282 | return; |
1283 | } |
1284 | p = (zend_mm_block *) ((char *) segment + ZEND_MM_ALIGNED_SEGMENT_SIZE); |
1285 | while (1) { |
1286 | q = ZEND_MM_NEXT_BLOCK(p); |
1287 | if (q <= p || |
1288 | (char*)q > (char*)segment + segment->size || |
1289 | p->info._size != q->info._prev) { |
1290 | zend_mm_panic("zend_mm_heap corrupted" ); |
1291 | } |
1292 | if (!ZEND_MM_IS_FREE_BLOCK(p)) { |
1293 | if (p->magic == MEM_BLOCK_VALID) { |
1294 | long repeated; |
1295 | zend_leak_info leak; |
1296 | |
1297 | ZEND_MM_SET_MAGIC(p, MEM_BLOCK_LEAK); |
1298 | |
1299 | leak.addr = ZEND_MM_DATA_OF(p); |
1300 | leak.size = p->debug.size; |
1301 | leak.filename = p->debug.filename; |
1302 | leak.lineno = p->debug.lineno; |
1303 | leak.orig_filename = p->debug.orig_filename; |
1304 | leak.orig_lineno = p->debug.orig_lineno; |
1305 | |
1306 | zend_message_dispatcher(ZMSG_LOG_SCRIPT_NAME, NULL TSRMLS_CC); |
1307 | zend_message_dispatcher(ZMSG_MEMORY_LEAK_DETECTED, &leak TSRMLS_CC); |
1308 | repeated = zend_mm_find_leaks(segment, p); |
1309 | total += 1 + repeated; |
1310 | if (repeated) { |
1311 | zend_message_dispatcher(ZMSG_MEMORY_LEAK_REPEATED, (void *)(zend_uintptr_t)repeated TSRMLS_CC); |
1312 | } |
1313 | #if ZEND_MM_CACHE |
1314 | } else if (p->magic == MEM_BLOCK_CACHED) { |
1315 | /* skip it */ |
1316 | #endif |
1317 | } else if (p->magic != MEM_BLOCK_LEAK) { |
1318 | zend_mm_panic("zend_mm_heap corrupted" ); |
1319 | } |
1320 | } |
1321 | if (ZEND_MM_IS_GUARD_BLOCK(q)) { |
1322 | segment = segment->next_segment; |
1323 | if (!segment) { |
1324 | break; |
1325 | } |
1326 | q = (zend_mm_block *) ((char *) segment + ZEND_MM_ALIGNED_SEGMENT_SIZE); |
1327 | } |
1328 | p = q; |
1329 | } |
1330 | if (total) { |
1331 | zend_message_dispatcher(ZMSG_MEMORY_LEAKS_GRAND_TOTAL, &total TSRMLS_CC); |
1332 | } |
1333 | } |
1334 | |
1335 | static int zend_mm_check_ptr(zend_mm_heap *heap, void *ptr, int silent ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) |
1336 | { |
1337 | zend_mm_block *p; |
1338 | int no_cache_notice = 0; |
1339 | int had_problems = 0; |
1340 | int valid_beginning = 1; |
1341 | |
1342 | if (silent==2) { |
1343 | silent = 1; |
1344 | no_cache_notice = 1; |
1345 | } else if (silent==3) { |
1346 | silent = 0; |
1347 | no_cache_notice = 1; |
1348 | } |
1349 | if (!silent) { |
1350 | TSRMLS_FETCH(); |
1351 | |
1352 | zend_message_dispatcher(ZMSG_LOG_SCRIPT_NAME, NULL TSRMLS_CC); |
1353 | zend_debug_alloc_output("---------------------------------------\n" ); |
1354 | zend_debug_alloc_output("%s(%d) : Block " PTR_FMT" status:\n" ZEND_FILE_LINE_RELAY_CC, ptr); |
1355 | if (__zend_orig_filename) { |
1356 | zend_debug_alloc_output("%s(%d) : Actual location (location was relayed)\n" ZEND_FILE_LINE_ORIG_RELAY_CC); |
1357 | } |
1358 | if (!ptr) { |
1359 | zend_debug_alloc_output("NULL\n" ); |
1360 | zend_debug_alloc_output("---------------------------------------\n" ); |
1361 | return 0; |
1362 | } |
1363 | } |
1364 | |
1365 | if (!ptr) { |
1366 | if (silent) { |
1367 | return zend_mm_check_ptr(heap, ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
1368 | } |
1369 | } |
1370 | |
1371 | p = ZEND_MM_HEADER_OF(ptr); |
1372 | |
1373 | #ifdef ZTS |
1374 | if (ZEND_MM_BAD_THREAD_ID(p)) { |
1375 | if (!silent) { |
1376 | zend_debug_alloc_output("Invalid pointer: ((thread_id=0x%0.8X) != (expected=0x%0.8X))\n" , (long)p->thread_id, (long)tsrm_thread_id()); |
1377 | had_problems = 1; |
1378 | } else { |
1379 | return zend_mm_check_ptr(heap, ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
1380 | } |
1381 | } |
1382 | #endif |
1383 | |
1384 | if (p->info._size != ZEND_MM_NEXT_BLOCK(p)->info._prev) { |
1385 | if (!silent) { |
1386 | zend_debug_alloc_output("Invalid pointer: ((size=" PTR_FMT") != (next.prev=" PTR_FMT"))\n" , p->info._size, ZEND_MM_NEXT_BLOCK(p)->info._prev); |
1387 | had_problems = 1; |
1388 | } else { |
1389 | return zend_mm_check_ptr(heap, ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
1390 | } |
1391 | } |
1392 | if (p->info._prev != ZEND_MM_GUARD_BLOCK && |
1393 | ZEND_MM_PREV_BLOCK(p)->info._size != p->info._prev) { |
1394 | if (!silent) { |
1395 | zend_debug_alloc_output("Invalid pointer: ((prev=" PTR_FMT") != (prev.size=" PTR_FMT"))\n" , p->info._prev, ZEND_MM_PREV_BLOCK(p)->info._size); |
1396 | had_problems = 1; |
1397 | } else { |
1398 | return zend_mm_check_ptr(heap, ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
1399 | } |
1400 | } |
1401 | |
1402 | if (had_problems) { |
1403 | zend_debug_alloc_output("---------------------------------------\n" ); |
1404 | return 0; |
1405 | } |
1406 | |
1407 | if (!silent) { |
1408 | zend_debug_alloc_output("%10s\t" ,"Beginning: " ); |
1409 | } |
1410 | |
1411 | if (!ZEND_MM_IS_USED_BLOCK(p)) { |
1412 | if (!silent) { |
1413 | if (p->magic != MEM_BLOCK_FREED) { |
1414 | zend_debug_alloc_output("Freed (magic=0x%0.8X, expected=0x%0.8X)\n" , p->magic, MEM_BLOCK_FREED); |
1415 | } else { |
1416 | zend_debug_alloc_output("Freed\n" ); |
1417 | } |
1418 | had_problems = 1; |
1419 | } else { |
1420 | return zend_mm_check_ptr(heap, ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
1421 | } |
1422 | } else if (ZEND_MM_IS_GUARD_BLOCK(p)) { |
1423 | if (!silent) { |
1424 | if (p->magic != MEM_BLOCK_FREED) { |
1425 | zend_debug_alloc_output("Guard (magic=0x%0.8X, expected=0x%0.8X)\n" , p->magic, MEM_BLOCK_FREED); |
1426 | } else { |
1427 | zend_debug_alloc_output("Guard\n" ); |
1428 | } |
1429 | had_problems = 1; |
1430 | } else { |
1431 | return zend_mm_check_ptr(heap, ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
1432 | } |
1433 | } else { |
1434 | switch (p->magic) { |
1435 | case MEM_BLOCK_VALID: |
1436 | case MEM_BLOCK_LEAK: |
1437 | if (!silent) { |
1438 | zend_debug_alloc_output("OK (allocated on %s:%d, %d bytes)\n" , p->debug.filename, p->debug.lineno, (int)p->debug.size); |
1439 | } |
1440 | break; /* ok */ |
1441 | case MEM_BLOCK_CACHED: |
1442 | if (!no_cache_notice) { |
1443 | if (!silent) { |
1444 | zend_debug_alloc_output("Cached\n" ); |
1445 | had_problems = 1; |
1446 | } else { |
1447 | return zend_mm_check_ptr(heap, ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
1448 | } |
1449 | } |
1450 | case MEM_BLOCK_FREED: |
1451 | if (!silent) { |
1452 | zend_debug_alloc_output("Freed (invalid)\n" ); |
1453 | had_problems = 1; |
1454 | } else { |
1455 | return zend_mm_check_ptr(heap, ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
1456 | } |
1457 | break; |
1458 | case MEM_BLOCK_GUARD: |
1459 | if (!silent) { |
1460 | zend_debug_alloc_output("Guard (invalid)\n" ); |
1461 | had_problems = 1; |
1462 | } else { |
1463 | return zend_mm_check_ptr(heap, ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
1464 | } |
1465 | break; |
1466 | default: |
1467 | if (!silent) { |
1468 | zend_debug_alloc_output("Unknown (magic=0x%0.8X, expected=0x%0.8X)\n" , p->magic, MEM_BLOCK_VALID); |
1469 | had_problems = 1; |
1470 | valid_beginning = 0; |
1471 | } else { |
1472 | return zend_mm_check_ptr(heap, ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
1473 | } |
1474 | break; |
1475 | } |
1476 | } |
1477 | |
1478 | #if ZEND_MM_HEAP_PROTECTION |
1479 | if (!valid_beginning) { |
1480 | if (!silent) { |
1481 | zend_debug_alloc_output("%10s\t" , "Start:" ); |
1482 | zend_debug_alloc_output("Unknown\n" ); |
1483 | zend_debug_alloc_output("%10s\t" , "End:" ); |
1484 | zend_debug_alloc_output("Unknown\n" ); |
1485 | } |
1486 | } else { |
1487 | char *end_magic = ZEND_MM_END_MAGIC_PTR(p); |
1488 | |
1489 | if (p->debug.start_magic == _mem_block_start_magic) { |
1490 | if (!silent) { |
1491 | zend_debug_alloc_output("%10s\t" , "Start:" ); |
1492 | zend_debug_alloc_output("OK\n" ); |
1493 | } |
1494 | } else { |
1495 | char *overflow_ptr, *magic_ptr=(char *) &_mem_block_start_magic; |
1496 | int overflows=0; |
1497 | int i; |
1498 | |
1499 | if (silent) { |
1500 | return _mem_block_check(ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
1501 | } |
1502 | had_problems = 1; |
1503 | overflow_ptr = (char *) &p->debug.start_magic; |
1504 | i = END_MAGIC_SIZE; |
1505 | while (--i >= 0) { |
1506 | if (overflow_ptr[i]!=magic_ptr[i]) { |
1507 | overflows++; |
1508 | } |
1509 | } |
1510 | zend_debug_alloc_output("%10s\t" , "Start:" ); |
1511 | zend_debug_alloc_output("Overflown (magic=0x%0.8X instead of 0x%0.8X)\n" , p->debug.start_magic, _mem_block_start_magic); |
1512 | zend_debug_alloc_output("%10s\t" ,"" ); |
1513 | if (overflows >= END_MAGIC_SIZE) { |
1514 | zend_debug_alloc_output("At least %d bytes overflown\n" , END_MAGIC_SIZE); |
1515 | } else { |
1516 | zend_debug_alloc_output("%d byte(s) overflown\n" , overflows); |
1517 | } |
1518 | } |
1519 | if (memcmp(end_magic, &_mem_block_end_magic, END_MAGIC_SIZE)==0) { |
1520 | if (!silent) { |
1521 | zend_debug_alloc_output("%10s\t" , "End:" ); |
1522 | zend_debug_alloc_output("OK\n" ); |
1523 | } |
1524 | } else { |
1525 | char *overflow_ptr, *magic_ptr=(char *) &_mem_block_end_magic; |
1526 | int overflows=0; |
1527 | int i; |
1528 | |
1529 | if (silent) { |
1530 | return _mem_block_check(ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
1531 | } |
1532 | had_problems = 1; |
1533 | overflow_ptr = (char *) end_magic; |
1534 | |
1535 | for (i=0; i < END_MAGIC_SIZE; i++) { |
1536 | if (overflow_ptr[i]!=magic_ptr[i]) { |
1537 | overflows++; |
1538 | } |
1539 | } |
1540 | |
1541 | zend_debug_alloc_output("%10s\t" , "End:" ); |
1542 | zend_debug_alloc_output("Overflown (magic=0x%0.8X instead of 0x%0.8X)\n" , *end_magic, _mem_block_end_magic); |
1543 | zend_debug_alloc_output("%10s\t" ,"" ); |
1544 | if (overflows >= END_MAGIC_SIZE) { |
1545 | zend_debug_alloc_output("At least %d bytes overflown\n" , END_MAGIC_SIZE); |
1546 | } else { |
1547 | zend_debug_alloc_output("%d byte(s) overflown\n" , overflows); |
1548 | } |
1549 | } |
1550 | } |
1551 | #endif |
1552 | |
1553 | if (!silent) { |
1554 | zend_debug_alloc_output("---------------------------------------\n" ); |
1555 | } |
1556 | return ((!had_problems) ? 1 : 0); |
1557 | } |
1558 | |
1559 | static int zend_mm_check_heap(zend_mm_heap *heap, int silent ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) |
1560 | { |
1561 | zend_mm_segment *segment = heap->segments_list; |
1562 | zend_mm_block *p, *q; |
1563 | int errors = 0; |
1564 | |
1565 | if (!segment) { |
1566 | return 0; |
1567 | } |
1568 | p = (zend_mm_block *) ((char *) segment + ZEND_MM_ALIGNED_SEGMENT_SIZE); |
1569 | while (1) { |
1570 | q = ZEND_MM_NEXT_BLOCK(p); |
1571 | if (q <= p || |
1572 | (char*)q > (char*)segment + segment->size || |
1573 | p->info._size != q->info._prev) { |
1574 | zend_mm_panic("zend_mm_heap corrupted" ); |
1575 | } |
1576 | if (!ZEND_MM_IS_FREE_BLOCK(p)) { |
1577 | if (p->magic == MEM_BLOCK_VALID || p->magic == MEM_BLOCK_LEAK) { |
1578 | if (!zend_mm_check_ptr(heap, ZEND_MM_DATA_OF(p), (silent?2:3) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC)) { |
1579 | errors++; |
1580 | } |
1581 | #if ZEND_MM_CACHE |
1582 | } else if (p->magic == MEM_BLOCK_CACHED) { |
1583 | /* skip it */ |
1584 | #endif |
1585 | } else if (p->magic != MEM_BLOCK_LEAK) { |
1586 | zend_mm_panic("zend_mm_heap corrupted" ); |
1587 | } |
1588 | } |
1589 | if (ZEND_MM_IS_GUARD_BLOCK(q)) { |
1590 | segment = segment->next_segment; |
1591 | if (!segment) { |
1592 | return errors; |
1593 | } |
1594 | q = (zend_mm_block *) ((char *) segment + ZEND_MM_ALIGNED_SEGMENT_SIZE); |
1595 | } |
1596 | p = q; |
1597 | } |
1598 | } |
1599 | #endif |
1600 | |
1601 | ZEND_API void zend_mm_shutdown(zend_mm_heap *heap, int full_shutdown, int silent TSRMLS_DC) |
1602 | { |
1603 | zend_mm_storage *storage; |
1604 | zend_mm_segment *segment; |
1605 | zend_mm_segment *prev; |
1606 | int internal; |
1607 | |
1608 | if (!heap->use_zend_alloc) { |
1609 | if (full_shutdown) { |
1610 | free(heap); |
1611 | } |
1612 | return; |
1613 | } |
1614 | |
1615 | if (heap->reserve) { |
1616 | #if ZEND_DEBUG |
1617 | if (!silent) { |
1618 | _zend_mm_free_int(heap, heap->reserve ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC); |
1619 | } |
1620 | #endif |
1621 | heap->reserve = NULL; |
1622 | } |
1623 | |
1624 | #if ZEND_MM_CACHE_STAT |
1625 | if (full_shutdown) { |
1626 | FILE *f; |
1627 | |
1628 | f = fopen("zend_mm.log" , "w" ); |
1629 | if (f) { |
1630 | int i,j; |
1631 | size_t size, true_size, min_size, max_size; |
1632 | int hit = 0, miss = 0; |
1633 | |
1634 | fprintf(f, "\nidx min_size max_size true_size max_len hits misses\n" ); |
1635 | size = 0; |
1636 | while (1) { |
1637 | true_size = ZEND_MM_TRUE_SIZE(size); |
1638 | if (ZEND_MM_SMALL_SIZE(true_size)) { |
1639 | min_size = size; |
1640 | i = ZEND_MM_BUCKET_INDEX(true_size); |
1641 | size++; |
1642 | while (1) { |
1643 | true_size = ZEND_MM_TRUE_SIZE(size); |
1644 | if (ZEND_MM_SMALL_SIZE(true_size)) { |
1645 | j = ZEND_MM_BUCKET_INDEX(true_size); |
1646 | if (j > i) { |
1647 | max_size = size-1; |
1648 | break; |
1649 | } |
1650 | } else { |
1651 | max_size = size-1; |
1652 | break; |
1653 | } |
1654 | size++; |
1655 | } |
1656 | hit += heap->cache_stat[i].hit; |
1657 | miss += heap->cache_stat[i].miss; |
1658 | fprintf(f, "%2d %8d %8d %9d %8d %8d %8d\n" , i, (int)min_size, (int)max_size, ZEND_MM_TRUE_SIZE(max_size), heap->cache_stat[i].max_count, heap->cache_stat[i].hit, heap->cache_stat[i].miss); |
1659 | } else { |
1660 | break; |
1661 | } |
1662 | } |
1663 | fprintf(f, " %8d %8d\n" , hit, miss); |
1664 | fprintf(f, " %8d %8d\n" , heap->cache_stat[ZEND_MM_NUM_BUCKETS].hit, heap->cache_stat[ZEND_MM_NUM_BUCKETS].miss); |
1665 | fclose(f); |
1666 | } |
1667 | } |
1668 | #endif |
1669 | |
1670 | #if ZEND_DEBUG |
1671 | if (!silent) { |
1672 | zend_mm_check_leaks(heap TSRMLS_CC); |
1673 | } |
1674 | #endif |
1675 | |
1676 | internal = heap->internal; |
1677 | storage = heap->storage; |
1678 | segment = heap->segments_list; |
1679 | if (full_shutdown) { |
1680 | while (segment) { |
1681 | prev = segment; |
1682 | segment = segment->next_segment; |
1683 | ZEND_MM_STORAGE_FREE(prev); |
1684 | } |
1685 | heap->segments_list = NULL; |
1686 | storage->handlers->dtor(storage); |
1687 | if (!internal) { |
1688 | free(heap); |
1689 | } |
1690 | } else { |
1691 | if (segment) { |
1692 | #ifndef ZEND_WIN32 |
1693 | if (heap->reserve_size) { |
1694 | while (segment->next_segment) { |
1695 | prev = segment; |
1696 | segment = segment->next_segment; |
1697 | ZEND_MM_STORAGE_FREE(prev); |
1698 | } |
1699 | heap->segments_list = segment; |
1700 | } else { |
1701 | #endif |
1702 | do { |
1703 | prev = segment; |
1704 | segment = segment->next_segment; |
1705 | ZEND_MM_STORAGE_FREE(prev); |
1706 | } while (segment); |
1707 | heap->segments_list = NULL; |
1708 | #ifndef ZEND_WIN32 |
1709 | } |
1710 | #endif |
1711 | } |
1712 | if (heap->compact_size && |
1713 | heap->real_peak > heap->compact_size) { |
1714 | storage->handlers->compact(storage); |
1715 | } |
1716 | zend_mm_init(heap); |
1717 | if (heap->segments_list) { |
1718 | heap->real_size = heap->segments_list->size; |
1719 | heap->real_peak = heap->segments_list->size; |
1720 | } else { |
1721 | heap->real_size = 0; |
1722 | heap->real_peak = 0; |
1723 | } |
1724 | heap->size = 0; |
1725 | heap->peak = 0; |
1726 | if (heap->segments_list) { |
1727 | /* mark segment as a free block */ |
1728 | zend_mm_free_block *b = (zend_mm_free_block*)((char*)heap->segments_list + ZEND_MM_ALIGNED_SEGMENT_SIZE); |
1729 | size_t block_size = heap->segments_list->size - ZEND_MM_ALIGNED_SEGMENT_SIZE - ZEND_MM_ALIGNED_HEADER_SIZE; |
1730 | |
1731 | ZEND_MM_MARK_FIRST_BLOCK(b); |
1732 | ZEND_MM_LAST_BLOCK(ZEND_MM_BLOCK_AT(b, block_size)); |
1733 | ZEND_MM_BLOCK(b, ZEND_MM_FREE_BLOCK, block_size); |
1734 | zend_mm_add_to_free_list(heap, b); |
1735 | } |
1736 | if (heap->reserve_size) { |
1737 | heap->reserve = _zend_mm_alloc_int(heap, heap->reserve_size ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC); |
1738 | } |
1739 | heap->overflow = 0; |
1740 | } |
1741 | } |
1742 | |
1743 | static void zend_mm_safe_error(zend_mm_heap *heap, |
1744 | const char *format, |
1745 | size_t limit, |
1746 | #if ZEND_DEBUG |
1747 | const char *filename, |
1748 | uint lineno, |
1749 | #endif |
1750 | size_t size) |
1751 | { |
1752 | if (heap->reserve) { |
1753 | _zend_mm_free_int(heap, heap->reserve ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC); |
1754 | heap->reserve = NULL; |
1755 | } |
1756 | if (heap->overflow == 0) { |
1757 | const char *error_filename; |
1758 | uint error_lineno; |
1759 | TSRMLS_FETCH(); |
1760 | if (zend_is_compiling(TSRMLS_C)) { |
1761 | error_filename = zend_get_compiled_filename(TSRMLS_C); |
1762 | error_lineno = zend_get_compiled_lineno(TSRMLS_C); |
1763 | } else if (EG(in_execution)) { |
1764 | error_filename = EG(active_op_array)?EG(active_op_array)->filename:NULL; |
1765 | error_lineno = EG(opline_ptr)?(*EG(opline_ptr))->lineno:0; |
1766 | } else { |
1767 | error_filename = NULL; |
1768 | error_lineno = 0; |
1769 | } |
1770 | if (!error_filename) { |
1771 | error_filename = "Unknown" ; |
1772 | } |
1773 | heap->overflow = 1; |
1774 | zend_try { |
1775 | zend_error_noreturn(E_ERROR, |
1776 | format, |
1777 | limit, |
1778 | #if ZEND_DEBUG |
1779 | filename, |
1780 | lineno, |
1781 | #endif |
1782 | size); |
1783 | } zend_catch { |
1784 | if (heap->overflow == 2) { |
1785 | fprintf(stderr, "\nFatal error: " ); |
1786 | fprintf(stderr, |
1787 | format, |
1788 | limit, |
1789 | #if ZEND_DEBUG |
1790 | filename, |
1791 | lineno, |
1792 | #endif |
1793 | size); |
1794 | fprintf(stderr, " in %s on line %d\n" , error_filename, error_lineno); |
1795 | } |
1796 | /* See http://support.microsoft.com/kb/190351 */ |
1797 | #ifdef PHP_WIN32 |
1798 | fflush(stderr); |
1799 | #endif |
1800 | } zend_end_try(); |
1801 | } else { |
1802 | heap->overflow = 2; |
1803 | } |
1804 | zend_bailout(); |
1805 | } |
1806 | |
1807 | static zend_mm_free_block *zend_mm_search_large_block(zend_mm_heap *heap, size_t true_size) |
1808 | { |
1809 | zend_mm_free_block *best_fit; |
1810 | size_t index = ZEND_MM_LARGE_BUCKET_INDEX(true_size); |
1811 | size_t bitmap = heap->large_free_bitmap >> index; |
1812 | zend_mm_free_block *p; |
1813 | |
1814 | if (bitmap == 0) { |
1815 | return NULL; |
1816 | } |
1817 | |
1818 | if (UNEXPECTED((bitmap & 1) != 0)) { |
1819 | /* Search for best "large" free block */ |
1820 | zend_mm_free_block *rst = NULL; |
1821 | size_t m; |
1822 | size_t best_size = -1; |
1823 | |
1824 | best_fit = NULL; |
1825 | p = heap->large_free_buckets[index]; |
1826 | for (m = true_size << (ZEND_MM_NUM_BUCKETS - index); ; m <<= 1) { |
1827 | if (UNEXPECTED(ZEND_MM_FREE_BLOCK_SIZE(p) == true_size)) { |
1828 | return p->next_free_block; |
1829 | } else if (ZEND_MM_FREE_BLOCK_SIZE(p) >= true_size && |
1830 | ZEND_MM_FREE_BLOCK_SIZE(p) < best_size) { |
1831 | best_size = ZEND_MM_FREE_BLOCK_SIZE(p); |
1832 | best_fit = p; |
1833 | } |
1834 | if ((m & (ZEND_MM_LONG_CONST(1) << (ZEND_MM_NUM_BUCKETS-1))) == 0) { |
1835 | if (p->child[1]) { |
1836 | rst = p->child[1]; |
1837 | } |
1838 | if (p->child[0]) { |
1839 | p = p->child[0]; |
1840 | } else { |
1841 | break; |
1842 | } |
1843 | } else if (p->child[1]) { |
1844 | p = p->child[1]; |
1845 | } else { |
1846 | break; |
1847 | } |
1848 | } |
1849 | |
1850 | for (p = rst; p; p = p->child[p->child[0] != NULL]) { |
1851 | if (UNEXPECTED(ZEND_MM_FREE_BLOCK_SIZE(p) == true_size)) { |
1852 | return p->next_free_block; |
1853 | } else if (ZEND_MM_FREE_BLOCK_SIZE(p) > true_size && |
1854 | ZEND_MM_FREE_BLOCK_SIZE(p) < best_size) { |
1855 | best_size = ZEND_MM_FREE_BLOCK_SIZE(p); |
1856 | best_fit = p; |
1857 | } |
1858 | } |
1859 | |
1860 | if (best_fit) { |
1861 | return best_fit->next_free_block; |
1862 | } |
1863 | bitmap = bitmap >> 1; |
1864 | if (!bitmap) { |
1865 | return NULL; |
1866 | } |
1867 | index++; |
1868 | } |
1869 | |
1870 | /* Search for smallest "large" free block */ |
1871 | best_fit = p = heap->large_free_buckets[index + zend_mm_low_bit(bitmap)]; |
1872 | while ((p = p->child[p->child[0] != NULL])) { |
1873 | if (ZEND_MM_FREE_BLOCK_SIZE(p) < ZEND_MM_FREE_BLOCK_SIZE(best_fit)) { |
1874 | best_fit = p; |
1875 | } |
1876 | } |
1877 | return best_fit->next_free_block; |
1878 | } |
1879 | |
1880 | static void *_zend_mm_alloc_int(zend_mm_heap *heap, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) |
1881 | { |
1882 | zend_mm_free_block *best_fit; |
1883 | size_t true_size = ZEND_MM_TRUE_SIZE(size); |
1884 | size_t block_size; |
1885 | size_t remaining_size; |
1886 | size_t segment_size; |
1887 | zend_mm_segment *segment; |
1888 | int keep_rest = 0; |
1889 | #ifdef ZEND_SIGNALS |
1890 | TSRMLS_FETCH(); |
1891 | #endif |
1892 | |
1893 | HANDLE_BLOCK_INTERRUPTIONS(); |
1894 | |
1895 | if (EXPECTED(ZEND_MM_SMALL_SIZE(true_size))) { |
1896 | size_t index = ZEND_MM_BUCKET_INDEX(true_size); |
1897 | size_t bitmap; |
1898 | |
1899 | if (UNEXPECTED(true_size < size)) { |
1900 | goto out_of_memory; |
1901 | } |
1902 | #if ZEND_MM_CACHE |
1903 | if (EXPECTED(heap->cache[index] != NULL)) { |
1904 | /* Get block from cache */ |
1905 | #if ZEND_MM_CACHE_STAT |
1906 | heap->cache_stat[index].count--; |
1907 | heap->cache_stat[index].hit++; |
1908 | #endif |
1909 | best_fit = heap->cache[index]; |
1910 | heap->cache[index] = best_fit->prev_free_block; |
1911 | heap->cached -= true_size; |
1912 | ZEND_MM_CHECK_MAGIC(best_fit, MEM_BLOCK_CACHED); |
1913 | ZEND_MM_SET_DEBUG_INFO(best_fit, size, 1, 0); |
1914 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
1915 | return ZEND_MM_DATA_OF(best_fit); |
1916 | } |
1917 | #if ZEND_MM_CACHE_STAT |
1918 | heap->cache_stat[index].miss++; |
1919 | #endif |
1920 | #endif |
1921 | |
1922 | bitmap = heap->free_bitmap >> index; |
1923 | if (bitmap) { |
1924 | /* Found some "small" free block that can be used */ |
1925 | index += zend_mm_low_bit(bitmap); |
1926 | best_fit = heap->free_buckets[index*2]; |
1927 | #if ZEND_MM_CACHE_STAT |
1928 | heap->cache_stat[ZEND_MM_NUM_BUCKETS].hit++; |
1929 | #endif |
1930 | goto zend_mm_finished_searching_for_block; |
1931 | } |
1932 | } |
1933 | |
1934 | #if ZEND_MM_CACHE_STAT |
1935 | heap->cache_stat[ZEND_MM_NUM_BUCKETS].miss++; |
1936 | #endif |
1937 | |
1938 | best_fit = zend_mm_search_large_block(heap, true_size); |
1939 | |
1940 | if (!best_fit && heap->real_size >= heap->limit - heap->block_size) { |
1941 | zend_mm_free_block *p = heap->rest_buckets[0]; |
1942 | size_t best_size = -1; |
1943 | |
1944 | while (p != ZEND_MM_REST_BUCKET(heap)) { |
1945 | if (UNEXPECTED(ZEND_MM_FREE_BLOCK_SIZE(p) == true_size)) { |
1946 | best_fit = p; |
1947 | goto zend_mm_finished_searching_for_block; |
1948 | } else if (ZEND_MM_FREE_BLOCK_SIZE(p) > true_size && |
1949 | ZEND_MM_FREE_BLOCK_SIZE(p) < best_size) { |
1950 | best_size = ZEND_MM_FREE_BLOCK_SIZE(p); |
1951 | best_fit = p; |
1952 | } |
1953 | p = p->prev_free_block; |
1954 | } |
1955 | } |
1956 | |
1957 | if (!best_fit) { |
1958 | if (true_size > heap->block_size - (ZEND_MM_ALIGNED_SEGMENT_SIZE + ZEND_MM_ALIGNED_HEADER_SIZE)) { |
1959 | /* Make sure we add a memory block which is big enough, |
1960 | segment must have header "size" and trailer "guard" block */ |
1961 | segment_size = true_size + ZEND_MM_ALIGNED_SEGMENT_SIZE + ZEND_MM_ALIGNED_HEADER_SIZE; |
1962 | segment_size = (segment_size + (heap->block_size-1)) & ~(heap->block_size-1); |
1963 | keep_rest = 1; |
1964 | } else { |
1965 | segment_size = heap->block_size; |
1966 | } |
1967 | |
1968 | if (segment_size < true_size || |
1969 | heap->real_size + segment_size > heap->limit) { |
1970 | /* Memory limit overflow */ |
1971 | #if ZEND_MM_CACHE |
1972 | zend_mm_free_cache(heap); |
1973 | #endif |
1974 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
1975 | #if ZEND_DEBUG |
1976 | zend_mm_safe_error(heap, "Allowed memory size of %ld bytes exhausted at %s:%d (tried to allocate %lu bytes)" , heap->limit, __zend_filename, __zend_lineno, size); |
1977 | #else |
1978 | zend_mm_safe_error(heap, "Allowed memory size of %ld bytes exhausted (tried to allocate %lu bytes)" , heap->limit, size); |
1979 | #endif |
1980 | } |
1981 | |
1982 | segment = (zend_mm_segment *) ZEND_MM_STORAGE_ALLOC(segment_size); |
1983 | |
1984 | if (!segment) { |
1985 | /* Storage manager cannot allocate memory */ |
1986 | #if ZEND_MM_CACHE |
1987 | zend_mm_free_cache(heap); |
1988 | #endif |
1989 | out_of_memory: |
1990 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
1991 | #if ZEND_DEBUG |
1992 | zend_mm_safe_error(heap, "Out of memory (allocated %ld) at %s:%d (tried to allocate %lu bytes)" , heap->real_size, __zend_filename, __zend_lineno, size); |
1993 | #else |
1994 | zend_mm_safe_error(heap, "Out of memory (allocated %ld) (tried to allocate %lu bytes)" , heap->real_size, size); |
1995 | #endif |
1996 | return NULL; |
1997 | } |
1998 | |
1999 | heap->real_size += segment_size; |
2000 | if (heap->real_size > heap->real_peak) { |
2001 | heap->real_peak = heap->real_size; |
2002 | } |
2003 | |
2004 | segment->size = segment_size; |
2005 | segment->next_segment = heap->segments_list; |
2006 | heap->segments_list = segment; |
2007 | |
2008 | best_fit = (zend_mm_free_block *) ((char *) segment + ZEND_MM_ALIGNED_SEGMENT_SIZE); |
2009 | ZEND_MM_MARK_FIRST_BLOCK(best_fit); |
2010 | |
2011 | block_size = segment_size - ZEND_MM_ALIGNED_SEGMENT_SIZE - ZEND_MM_ALIGNED_HEADER_SIZE; |
2012 | |
2013 | ZEND_MM_LAST_BLOCK(ZEND_MM_BLOCK_AT(best_fit, block_size)); |
2014 | |
2015 | } else { |
2016 | zend_mm_finished_searching_for_block: |
2017 | /* remove from free list */ |
2018 | ZEND_MM_CHECK_MAGIC(best_fit, MEM_BLOCK_FREED); |
2019 | ZEND_MM_CHECK_COOKIE(best_fit); |
2020 | ZEND_MM_CHECK_BLOCK_LINKAGE(best_fit); |
2021 | zend_mm_remove_from_free_list(heap, best_fit); |
2022 | |
2023 | block_size = ZEND_MM_FREE_BLOCK_SIZE(best_fit); |
2024 | } |
2025 | |
2026 | remaining_size = block_size - true_size; |
2027 | |
2028 | if (remaining_size < ZEND_MM_ALIGNED_MIN_HEADER_SIZE) { |
2029 | true_size = block_size; |
2030 | ZEND_MM_BLOCK(best_fit, ZEND_MM_USED_BLOCK, true_size); |
2031 | } else { |
2032 | zend_mm_free_block *new_free_block; |
2033 | |
2034 | /* prepare new free block */ |
2035 | ZEND_MM_BLOCK(best_fit, ZEND_MM_USED_BLOCK, true_size); |
2036 | new_free_block = (zend_mm_free_block *) ZEND_MM_BLOCK_AT(best_fit, true_size); |
2037 | ZEND_MM_BLOCK(new_free_block, ZEND_MM_FREE_BLOCK, remaining_size); |
2038 | |
2039 | /* add the new free block to the free list */ |
2040 | if (EXPECTED(!keep_rest)) { |
2041 | zend_mm_add_to_free_list(heap, new_free_block); |
2042 | } else { |
2043 | zend_mm_add_to_rest_list(heap, new_free_block); |
2044 | } |
2045 | } |
2046 | |
2047 | ZEND_MM_SET_DEBUG_INFO(best_fit, size, 1, 1); |
2048 | |
2049 | heap->size += true_size; |
2050 | if (heap->peak < heap->size) { |
2051 | heap->peak = heap->size; |
2052 | } |
2053 | |
2054 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
2055 | |
2056 | return ZEND_MM_DATA_OF(best_fit); |
2057 | } |
2058 | |
2059 | |
2060 | static void _zend_mm_free_int(zend_mm_heap *heap, void *p ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) |
2061 | { |
2062 | zend_mm_block *mm_block; |
2063 | zend_mm_block *next_block; |
2064 | size_t size; |
2065 | #ifdef ZEND_SIGNALS |
2066 | TSRMLS_FETCH(); |
2067 | #endif |
2068 | if (!ZEND_MM_VALID_PTR(p)) { |
2069 | return; |
2070 | } |
2071 | |
2072 | HANDLE_BLOCK_INTERRUPTIONS(); |
2073 | |
2074 | mm_block = ZEND_MM_HEADER_OF(p); |
2075 | size = ZEND_MM_BLOCK_SIZE(mm_block); |
2076 | ZEND_MM_CHECK_PROTECTION(mm_block); |
2077 | |
2078 | #if ZEND_DEBUG || ZEND_MM_HEAP_PROTECTION |
2079 | memset(ZEND_MM_DATA_OF(mm_block), 0x5a, mm_block->debug.size); |
2080 | #endif |
2081 | |
2082 | #if ZEND_MM_CACHE |
2083 | if (EXPECTED(ZEND_MM_SMALL_SIZE(size)) && EXPECTED(heap->cached < ZEND_MM_CACHE_SIZE)) { |
2084 | size_t index = ZEND_MM_BUCKET_INDEX(size); |
2085 | zend_mm_free_block **cache = &heap->cache[index]; |
2086 | |
2087 | ((zend_mm_free_block*)mm_block)->prev_free_block = *cache; |
2088 | *cache = (zend_mm_free_block*)mm_block; |
2089 | heap->cached += size; |
2090 | ZEND_MM_SET_MAGIC(mm_block, MEM_BLOCK_CACHED); |
2091 | #if ZEND_MM_CACHE_STAT |
2092 | if (++heap->cache_stat[index].count > heap->cache_stat[index].max_count) { |
2093 | heap->cache_stat[index].max_count = heap->cache_stat[index].count; |
2094 | } |
2095 | #endif |
2096 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
2097 | return; |
2098 | } |
2099 | #endif |
2100 | |
2101 | heap->size -= size; |
2102 | |
2103 | next_block = ZEND_MM_BLOCK_AT(mm_block, size); |
2104 | if (ZEND_MM_IS_FREE_BLOCK(next_block)) { |
2105 | zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) next_block); |
2106 | size += ZEND_MM_FREE_BLOCK_SIZE(next_block); |
2107 | } |
2108 | if (ZEND_MM_PREV_BLOCK_IS_FREE(mm_block)) { |
2109 | mm_block = ZEND_MM_PREV_BLOCK(mm_block); |
2110 | zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) mm_block); |
2111 | size += ZEND_MM_FREE_BLOCK_SIZE(mm_block); |
2112 | } |
2113 | if (ZEND_MM_IS_FIRST_BLOCK(mm_block) && |
2114 | ZEND_MM_IS_GUARD_BLOCK(ZEND_MM_BLOCK_AT(mm_block, size))) { |
2115 | zend_mm_del_segment(heap, (zend_mm_segment *) ((char *)mm_block - ZEND_MM_ALIGNED_SEGMENT_SIZE)); |
2116 | } else { |
2117 | ZEND_MM_BLOCK(mm_block, ZEND_MM_FREE_BLOCK, size); |
2118 | zend_mm_add_to_free_list(heap, (zend_mm_free_block *) mm_block); |
2119 | } |
2120 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
2121 | } |
2122 | |
2123 | static void *_zend_mm_realloc_int(zend_mm_heap *heap, void *p, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) |
2124 | { |
2125 | zend_mm_block *mm_block = ZEND_MM_HEADER_OF(p); |
2126 | zend_mm_block *next_block; |
2127 | size_t true_size; |
2128 | size_t orig_size; |
2129 | void *ptr; |
2130 | #ifdef ZEND_SIGNALS |
2131 | TSRMLS_FETCH(); |
2132 | #endif |
2133 | if (UNEXPECTED(!p) || !ZEND_MM_VALID_PTR(p)) { |
2134 | return _zend_mm_alloc_int(heap, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
2135 | } |
2136 | |
2137 | HANDLE_BLOCK_INTERRUPTIONS(); |
2138 | |
2139 | mm_block = ZEND_MM_HEADER_OF(p); |
2140 | true_size = ZEND_MM_TRUE_SIZE(size); |
2141 | orig_size = ZEND_MM_BLOCK_SIZE(mm_block); |
2142 | ZEND_MM_CHECK_PROTECTION(mm_block); |
2143 | |
2144 | if (UNEXPECTED(true_size < size)) { |
2145 | goto out_of_memory; |
2146 | } |
2147 | |
2148 | if (true_size <= orig_size) { |
2149 | size_t remaining_size = orig_size - true_size; |
2150 | |
2151 | if (remaining_size >= ZEND_MM_ALIGNED_MIN_HEADER_SIZE) { |
2152 | zend_mm_free_block *new_free_block; |
2153 | |
2154 | next_block = ZEND_MM_BLOCK_AT(mm_block, orig_size); |
2155 | if (ZEND_MM_IS_FREE_BLOCK(next_block)) { |
2156 | remaining_size += ZEND_MM_FREE_BLOCK_SIZE(next_block); |
2157 | zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) next_block); |
2158 | } |
2159 | |
2160 | /* prepare new free block */ |
2161 | ZEND_MM_BLOCK(mm_block, ZEND_MM_USED_BLOCK, true_size); |
2162 | new_free_block = (zend_mm_free_block *) ZEND_MM_BLOCK_AT(mm_block, true_size); |
2163 | |
2164 | ZEND_MM_BLOCK(new_free_block, ZEND_MM_FREE_BLOCK, remaining_size); |
2165 | |
2166 | /* add the new free block to the free list */ |
2167 | zend_mm_add_to_free_list(heap, new_free_block); |
2168 | heap->size += (true_size - orig_size); |
2169 | } |
2170 | ZEND_MM_SET_DEBUG_INFO(mm_block, size, 0, 0); |
2171 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
2172 | return p; |
2173 | } |
2174 | |
2175 | #if ZEND_MM_CACHE |
2176 | if (ZEND_MM_SMALL_SIZE(true_size)) { |
2177 | size_t index = ZEND_MM_BUCKET_INDEX(true_size); |
2178 | |
2179 | if (heap->cache[index] != NULL) { |
2180 | zend_mm_free_block *best_fit; |
2181 | zend_mm_free_block **cache; |
2182 | |
2183 | #if ZEND_MM_CACHE_STAT |
2184 | heap->cache_stat[index].count--; |
2185 | heap->cache_stat[index].hit++; |
2186 | #endif |
2187 | best_fit = heap->cache[index]; |
2188 | heap->cache[index] = best_fit->prev_free_block; |
2189 | ZEND_MM_CHECK_MAGIC(best_fit, MEM_BLOCK_CACHED); |
2190 | ZEND_MM_SET_DEBUG_INFO(best_fit, size, 1, 0); |
2191 | |
2192 | ptr = ZEND_MM_DATA_OF(best_fit); |
2193 | |
2194 | #if ZEND_DEBUG || ZEND_MM_HEAP_PROTECTION |
2195 | memcpy(ptr, p, mm_block->debug.size); |
2196 | #else |
2197 | memcpy(ptr, p, orig_size - ZEND_MM_ALIGNED_HEADER_SIZE); |
2198 | #endif |
2199 | |
2200 | heap->cached -= true_size - orig_size; |
2201 | |
2202 | index = ZEND_MM_BUCKET_INDEX(orig_size); |
2203 | cache = &heap->cache[index]; |
2204 | |
2205 | ((zend_mm_free_block*)mm_block)->prev_free_block = *cache; |
2206 | *cache = (zend_mm_free_block*)mm_block; |
2207 | ZEND_MM_SET_MAGIC(mm_block, MEM_BLOCK_CACHED); |
2208 | #if ZEND_MM_CACHE_STAT |
2209 | if (++heap->cache_stat[index].count > heap->cache_stat[index].max_count) { |
2210 | heap->cache_stat[index].max_count = heap->cache_stat[index].count; |
2211 | } |
2212 | #endif |
2213 | |
2214 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
2215 | return ptr; |
2216 | } |
2217 | } |
2218 | #endif |
2219 | |
2220 | next_block = ZEND_MM_BLOCK_AT(mm_block, orig_size); |
2221 | |
2222 | if (ZEND_MM_IS_FREE_BLOCK(next_block)) { |
2223 | ZEND_MM_CHECK_COOKIE(next_block); |
2224 | ZEND_MM_CHECK_BLOCK_LINKAGE(next_block); |
2225 | if (orig_size + ZEND_MM_FREE_BLOCK_SIZE(next_block) >= true_size) { |
2226 | size_t block_size = orig_size + ZEND_MM_FREE_BLOCK_SIZE(next_block); |
2227 | size_t remaining_size = block_size - true_size; |
2228 | |
2229 | zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) next_block); |
2230 | |
2231 | if (remaining_size < ZEND_MM_ALIGNED_MIN_HEADER_SIZE) { |
2232 | true_size = block_size; |
2233 | ZEND_MM_BLOCK(mm_block, ZEND_MM_USED_BLOCK, true_size); |
2234 | } else { |
2235 | zend_mm_free_block *new_free_block; |
2236 | |
2237 | /* prepare new free block */ |
2238 | ZEND_MM_BLOCK(mm_block, ZEND_MM_USED_BLOCK, true_size); |
2239 | new_free_block = (zend_mm_free_block *) ZEND_MM_BLOCK_AT(mm_block, true_size); |
2240 | ZEND_MM_BLOCK(new_free_block, ZEND_MM_FREE_BLOCK, remaining_size); |
2241 | |
2242 | /* add the new free block to the free list */ |
2243 | if (ZEND_MM_IS_FIRST_BLOCK(mm_block) && |
2244 | ZEND_MM_IS_GUARD_BLOCK(ZEND_MM_BLOCK_AT(new_free_block, remaining_size))) { |
2245 | zend_mm_add_to_rest_list(heap, new_free_block); |
2246 | } else { |
2247 | zend_mm_add_to_free_list(heap, new_free_block); |
2248 | } |
2249 | } |
2250 | ZEND_MM_SET_DEBUG_INFO(mm_block, size, 0, 0); |
2251 | heap->size = heap->size + true_size - orig_size; |
2252 | if (heap->peak < heap->size) { |
2253 | heap->peak = heap->size; |
2254 | } |
2255 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
2256 | return p; |
2257 | } else if (ZEND_MM_IS_FIRST_BLOCK(mm_block) && |
2258 | ZEND_MM_IS_GUARD_BLOCK(ZEND_MM_BLOCK_AT(next_block, ZEND_MM_FREE_BLOCK_SIZE(next_block)))) { |
2259 | zend_mm_remove_from_free_list(heap, (zend_mm_free_block *) next_block); |
2260 | goto realloc_segment; |
2261 | } |
2262 | } else if (ZEND_MM_IS_FIRST_BLOCK(mm_block) && ZEND_MM_IS_GUARD_BLOCK(next_block)) { |
2263 | zend_mm_segment *segment; |
2264 | zend_mm_segment *segment_copy; |
2265 | size_t segment_size; |
2266 | size_t block_size; |
2267 | size_t remaining_size; |
2268 | |
2269 | realloc_segment: |
2270 | /* segment size, size of block and size of guard block */ |
2271 | if (true_size > heap->block_size - (ZEND_MM_ALIGNED_SEGMENT_SIZE + ZEND_MM_ALIGNED_HEADER_SIZE)) { |
2272 | segment_size = true_size+ZEND_MM_ALIGNED_SEGMENT_SIZE+ZEND_MM_ALIGNED_HEADER_SIZE; |
2273 | segment_size = (segment_size + (heap->block_size-1)) & ~(heap->block_size-1); |
2274 | } else { |
2275 | segment_size = heap->block_size; |
2276 | } |
2277 | |
2278 | segment_copy = (zend_mm_segment *) ((char *)mm_block - ZEND_MM_ALIGNED_SEGMENT_SIZE); |
2279 | if (segment_size < true_size || |
2280 | heap->real_size + segment_size - segment_copy->size > heap->limit) { |
2281 | if (ZEND_MM_IS_FREE_BLOCK(next_block)) { |
2282 | zend_mm_add_to_free_list(heap, (zend_mm_free_block *) next_block); |
2283 | } |
2284 | #if ZEND_MM_CACHE |
2285 | zend_mm_free_cache(heap); |
2286 | #endif |
2287 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
2288 | #if ZEND_DEBUG |
2289 | zend_mm_safe_error(heap, "Allowed memory size of %ld bytes exhausted at %s:%d (tried to allocate %ld bytes)" , heap->limit, __zend_filename, __zend_lineno, size); |
2290 | #else |
2291 | zend_mm_safe_error(heap, "Allowed memory size of %ld bytes exhausted (tried to allocate %ld bytes)" , heap->limit, size); |
2292 | #endif |
2293 | return NULL; |
2294 | } |
2295 | |
2296 | segment = ZEND_MM_STORAGE_REALLOC(segment_copy, segment_size); |
2297 | if (!segment) { |
2298 | #if ZEND_MM_CACHE |
2299 | zend_mm_free_cache(heap); |
2300 | #endif |
2301 | out_of_memory: |
2302 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
2303 | #if ZEND_DEBUG |
2304 | zend_mm_safe_error(heap, "Out of memory (allocated %ld) at %s:%d (tried to allocate %ld bytes)" , heap->real_size, __zend_filename, __zend_lineno, size); |
2305 | #else |
2306 | zend_mm_safe_error(heap, "Out of memory (allocated %ld) (tried to allocate %ld bytes)" , heap->real_size, size); |
2307 | #endif |
2308 | return NULL; |
2309 | } |
2310 | heap->real_size += segment_size - segment->size; |
2311 | if (heap->real_size > heap->real_peak) { |
2312 | heap->real_peak = heap->real_size; |
2313 | } |
2314 | |
2315 | segment->size = segment_size; |
2316 | |
2317 | if (segment != segment_copy) { |
2318 | zend_mm_segment **seg = &heap->segments_list; |
2319 | while (*seg != segment_copy) { |
2320 | seg = &(*seg)->next_segment; |
2321 | } |
2322 | *seg = segment; |
2323 | mm_block = (zend_mm_block *) ((char *) segment + ZEND_MM_ALIGNED_SEGMENT_SIZE); |
2324 | ZEND_MM_MARK_FIRST_BLOCK(mm_block); |
2325 | } |
2326 | |
2327 | block_size = segment_size - ZEND_MM_ALIGNED_SEGMENT_SIZE - ZEND_MM_ALIGNED_HEADER_SIZE; |
2328 | remaining_size = block_size - true_size; |
2329 | |
2330 | /* setup guard block */ |
2331 | ZEND_MM_LAST_BLOCK(ZEND_MM_BLOCK_AT(mm_block, block_size)); |
2332 | |
2333 | if (remaining_size < ZEND_MM_ALIGNED_MIN_HEADER_SIZE) { |
2334 | true_size = block_size; |
2335 | ZEND_MM_BLOCK(mm_block, ZEND_MM_USED_BLOCK, true_size); |
2336 | } else { |
2337 | zend_mm_free_block *new_free_block; |
2338 | |
2339 | /* prepare new free block */ |
2340 | ZEND_MM_BLOCK(mm_block, ZEND_MM_USED_BLOCK, true_size); |
2341 | new_free_block = (zend_mm_free_block *) ZEND_MM_BLOCK_AT(mm_block, true_size); |
2342 | ZEND_MM_BLOCK(new_free_block, ZEND_MM_FREE_BLOCK, remaining_size); |
2343 | |
2344 | /* add the new free block to the free list */ |
2345 | zend_mm_add_to_rest_list(heap, new_free_block); |
2346 | } |
2347 | |
2348 | ZEND_MM_SET_DEBUG_INFO(mm_block, size, 1, 1); |
2349 | |
2350 | heap->size = heap->size + true_size - orig_size; |
2351 | if (heap->peak < heap->size) { |
2352 | heap->peak = heap->size; |
2353 | } |
2354 | |
2355 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
2356 | return ZEND_MM_DATA_OF(mm_block); |
2357 | } |
2358 | |
2359 | ptr = _zend_mm_alloc_int(heap, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
2360 | #if ZEND_DEBUG || ZEND_MM_HEAP_PROTECTION |
2361 | memcpy(ptr, p, mm_block->debug.size); |
2362 | #else |
2363 | memcpy(ptr, p, orig_size - ZEND_MM_ALIGNED_HEADER_SIZE); |
2364 | #endif |
2365 | _zend_mm_free_int(heap, p ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
2366 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
2367 | return ptr; |
2368 | } |
2369 | |
2370 | ZEND_API void *_zend_mm_alloc(zend_mm_heap *heap, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) |
2371 | { |
2372 | return _zend_mm_alloc_int(heap, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
2373 | } |
2374 | |
2375 | ZEND_API void _zend_mm_free(zend_mm_heap *heap, void *p ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) |
2376 | { |
2377 | _zend_mm_free_int(heap, p ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
2378 | } |
2379 | |
2380 | ZEND_API void *_zend_mm_realloc(zend_mm_heap *heap, void *ptr, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) |
2381 | { |
2382 | return _zend_mm_realloc_int(heap, ptr, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
2383 | } |
2384 | |
2385 | ZEND_API size_t _zend_mm_block_size(zend_mm_heap *heap, void *p ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) |
2386 | { |
2387 | zend_mm_block *mm_block; |
2388 | |
2389 | if (!ZEND_MM_VALID_PTR(p)) { |
2390 | return 0; |
2391 | } |
2392 | mm_block = ZEND_MM_HEADER_OF(p); |
2393 | ZEND_MM_CHECK_PROTECTION(mm_block); |
2394 | #if ZEND_DEBUG || ZEND_MM_HEAP_PROTECTION |
2395 | return mm_block->debug.size; |
2396 | #else |
2397 | return ZEND_MM_BLOCK_SIZE(mm_block); |
2398 | #endif |
2399 | } |
2400 | |
2401 | /**********************/ |
2402 | /* Allocation Manager */ |
2403 | /**********************/ |
2404 | |
2405 | typedef struct _zend_alloc_globals { |
2406 | zend_mm_heap *mm_heap; |
2407 | } zend_alloc_globals; |
2408 | |
2409 | #ifdef ZTS |
2410 | static int alloc_globals_id; |
2411 | # define AG(v) TSRMG(alloc_globals_id, zend_alloc_globals *, v) |
2412 | #else |
2413 | # define AG(v) (alloc_globals.v) |
2414 | static zend_alloc_globals alloc_globals; |
2415 | #endif |
2416 | |
2417 | ZEND_API int is_zend_mm(TSRMLS_D) |
2418 | { |
2419 | return AG(mm_heap)->use_zend_alloc; |
2420 | } |
2421 | |
2422 | ZEND_API void *_emalloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) |
2423 | { |
2424 | TSRMLS_FETCH(); |
2425 | |
2426 | if (UNEXPECTED(!AG(mm_heap)->use_zend_alloc)) { |
2427 | return AG(mm_heap)->_malloc(size); |
2428 | } |
2429 | return _zend_mm_alloc_int(AG(mm_heap), size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
2430 | } |
2431 | |
2432 | ZEND_API void _efree(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) |
2433 | { |
2434 | TSRMLS_FETCH(); |
2435 | |
2436 | if (UNEXPECTED(!AG(mm_heap)->use_zend_alloc)) { |
2437 | AG(mm_heap)->_free(ptr); |
2438 | return; |
2439 | } |
2440 | _zend_mm_free_int(AG(mm_heap), ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
2441 | } |
2442 | |
2443 | ZEND_API void *_erealloc(void *ptr, size_t size, int allow_failure ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) |
2444 | { |
2445 | TSRMLS_FETCH(); |
2446 | |
2447 | if (UNEXPECTED(!AG(mm_heap)->use_zend_alloc)) { |
2448 | return AG(mm_heap)->_realloc(ptr, size); |
2449 | } |
2450 | return _zend_mm_realloc_int(AG(mm_heap), ptr, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
2451 | } |
2452 | |
2453 | ZEND_API size_t _zend_mem_block_size(void *ptr TSRMLS_DC ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) |
2454 | { |
2455 | if (UNEXPECTED(!AG(mm_heap)->use_zend_alloc)) { |
2456 | return 0; |
2457 | } |
2458 | return _zend_mm_block_size(AG(mm_heap), ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
2459 | } |
2460 | |
2461 | #if defined(__GNUC__) && (defined(__native_client__) || defined(i386)) |
2462 | |
2463 | static inline size_t safe_address(size_t nmemb, size_t size, size_t offset) |
2464 | { |
2465 | size_t res = nmemb; |
2466 | unsigned long overflow = 0; |
2467 | |
2468 | __asm__ ("mull %3\n\taddl %4,%0\n\tadcl $0,%1" |
2469 | : "=&a" (res), "=&d" (overflow) |
2470 | : "%0" (res), |
2471 | "rm" (size), |
2472 | "rm" (offset)); |
2473 | |
2474 | if (UNEXPECTED(overflow)) { |
2475 | zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)" , nmemb, size, offset); |
2476 | return 0; |
2477 | } |
2478 | return res; |
2479 | } |
2480 | |
2481 | #elif defined(__GNUC__) && defined(__x86_64__) |
2482 | |
2483 | static inline size_t safe_address(size_t nmemb, size_t size, size_t offset) |
2484 | { |
2485 | size_t res = nmemb; |
2486 | unsigned long overflow = 0; |
2487 | |
2488 | #ifdef __ILP32__ /* x32 */ |
2489 | # define LP_SUFF "l" |
2490 | #else /* amd64 */ |
2491 | # define LP_SUFF "q" |
2492 | #endif |
2493 | |
2494 | __asm__ ("mul" LP_SUFF " %3\n\t" |
2495 | "add %4,%0\n\t" |
2496 | "adc $0,%1" |
2497 | : "=&a" (res), "=&d" (overflow) |
2498 | : "%0" (res), |
2499 | "rm" (size), |
2500 | "rm" (offset)); |
2501 | |
2502 | #undef LP_SUFF |
2503 | if (UNEXPECTED(overflow)) { |
2504 | zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)" , nmemb, size, offset); |
2505 | return 0; |
2506 | } |
2507 | return res; |
2508 | } |
2509 | |
2510 | #elif defined(__GNUC__) && defined(__arm__) |
2511 | |
2512 | static inline size_t safe_address(size_t nmemb, size_t size, size_t offset) |
2513 | { |
2514 | size_t res; |
2515 | unsigned long overflow; |
2516 | |
2517 | __asm__ ("umlal %0,%1,%2,%3" |
2518 | : "=r" (res), "=r" (overflow) |
2519 | : "r" (nmemb), |
2520 | "r" (size), |
2521 | "0" (offset), |
2522 | "1" (0)); |
2523 | |
2524 | if (UNEXPECTED(overflow)) { |
2525 | zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)" , nmemb, size, offset); |
2526 | return 0; |
2527 | } |
2528 | return res; |
2529 | } |
2530 | |
2531 | #elif defined(__GNUC__) && defined(__aarch64__) |
2532 | |
2533 | static inline size_t safe_address(size_t nmemb, size_t size, size_t offset) |
2534 | { |
2535 | size_t res; |
2536 | unsigned long overflow; |
2537 | |
2538 | __asm__ ("mul %0,%2,%3\n\tumulh %1,%2,%3\n\tadds %0,%0,%4\n\tadc %1,%1,xzr" |
2539 | : "=&r" (res), "=&r" (overflow) |
2540 | : "r" (nmemb), |
2541 | "r" (size), |
2542 | "r" (offset)); |
2543 | |
2544 | if (UNEXPECTED(overflow)) { |
2545 | zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)" , nmemb, size, offset); |
2546 | return 0; |
2547 | } |
2548 | return res; |
2549 | } |
2550 | |
2551 | #elif SIZEOF_SIZE_T == 4 && defined(HAVE_ZEND_LONG64) |
2552 | |
2553 | static inline size_t safe_address(size_t nmemb, size_t size, size_t offset) |
2554 | { |
2555 | zend_ulong64 res = (zend_ulong64)nmemb * (zend_ulong64)size + (zend_ulong64)offset; |
2556 | |
2557 | if (UNEXPECTED(res > (zend_ulong64)0xFFFFFFFFL)) { |
2558 | zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)" , nmemb, size, offset); |
2559 | return 0; |
2560 | } |
2561 | return (size_t) res; |
2562 | } |
2563 | |
2564 | #else |
2565 | |
2566 | static inline size_t safe_address(size_t nmemb, size_t size, size_t offset) |
2567 | { |
2568 | size_t res = nmemb * size + offset; |
2569 | double _d = (double)nmemb * (double)size + (double)offset; |
2570 | double _delta = (double)res - _d; |
2571 | |
2572 | if (UNEXPECTED((_d + _delta ) != _d)) { |
2573 | zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)" , nmemb, size, offset); |
2574 | return 0; |
2575 | } |
2576 | return res; |
2577 | } |
2578 | #endif |
2579 | |
2580 | |
2581 | ZEND_API void *_safe_emalloc(size_t nmemb, size_t size, size_t offset ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) |
2582 | { |
2583 | return emalloc_rel(safe_address(nmemb, size, offset)); |
2584 | } |
2585 | |
2586 | ZEND_API void *_safe_malloc(size_t nmemb, size_t size, size_t offset) |
2587 | { |
2588 | return pemalloc(safe_address(nmemb, size, offset), 1); |
2589 | } |
2590 | |
2591 | ZEND_API void *_safe_erealloc(void *ptr, size_t nmemb, size_t size, size_t offset ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) |
2592 | { |
2593 | return erealloc_rel(ptr, safe_address(nmemb, size, offset)); |
2594 | } |
2595 | |
2596 | ZEND_API void *_safe_realloc(void *ptr, size_t nmemb, size_t size, size_t offset) |
2597 | { |
2598 | return perealloc(ptr, safe_address(nmemb, size, offset), 1); |
2599 | } |
2600 | |
2601 | |
2602 | ZEND_API void *_ecalloc(size_t nmemb, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) |
2603 | { |
2604 | void *p; |
2605 | #ifdef ZEND_SIGNALS |
2606 | TSRMLS_FETCH(); |
2607 | #endif |
2608 | HANDLE_BLOCK_INTERRUPTIONS(); |
2609 | |
2610 | p = _safe_emalloc(nmemb, size, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
2611 | if (UNEXPECTED(p == NULL)) { |
2612 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
2613 | return p; |
2614 | } |
2615 | memset(p, 0, size * nmemb); |
2616 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
2617 | return p; |
2618 | } |
2619 | |
2620 | ZEND_API char *_estrdup(const char *s ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) |
2621 | { |
2622 | size_t length; |
2623 | char *p; |
2624 | #ifdef ZEND_SIGNALS |
2625 | TSRMLS_FETCH(); |
2626 | #endif |
2627 | |
2628 | HANDLE_BLOCK_INTERRUPTIONS(); |
2629 | |
2630 | length = strlen(s); |
2631 | p = (char *) _emalloc(safe_address(length, 1, 1) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
2632 | if (UNEXPECTED(p == NULL)) { |
2633 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
2634 | return p; |
2635 | } |
2636 | memcpy(p, s, length+1); |
2637 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
2638 | return p; |
2639 | } |
2640 | |
2641 | ZEND_API char *_estrndup(const char *s, uint length ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) |
2642 | { |
2643 | char *p; |
2644 | #ifdef ZEND_SIGNALS |
2645 | TSRMLS_FETCH(); |
2646 | #endif |
2647 | |
2648 | HANDLE_BLOCK_INTERRUPTIONS(); |
2649 | |
2650 | p = (char *) _emalloc(safe_address(length, 1, 1) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
2651 | if (UNEXPECTED(p == NULL)) { |
2652 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
2653 | return p; |
2654 | } |
2655 | memcpy(p, s, length); |
2656 | p[length] = 0; |
2657 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
2658 | return p; |
2659 | } |
2660 | |
2661 | |
2662 | ZEND_API char *zend_strndup(const char *s, uint length) |
2663 | { |
2664 | char *p; |
2665 | #ifdef ZEND_SIGNALS |
2666 | TSRMLS_FETCH(); |
2667 | #endif |
2668 | |
2669 | HANDLE_BLOCK_INTERRUPTIONS(); |
2670 | |
2671 | p = (char *) malloc(safe_address(length, 1, 1)); |
2672 | if (UNEXPECTED(p == NULL)) { |
2673 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
2674 | return p; |
2675 | } |
2676 | if (length) { |
2677 | memcpy(p, s, length); |
2678 | } |
2679 | p[length] = 0; |
2680 | HANDLE_UNBLOCK_INTERRUPTIONS(); |
2681 | return p; |
2682 | } |
2683 | |
2684 | |
2685 | ZEND_API int zend_set_memory_limit(size_t memory_limit) |
2686 | { |
2687 | TSRMLS_FETCH(); |
2688 | |
2689 | AG(mm_heap)->limit = (memory_limit >= AG(mm_heap)->block_size) ? memory_limit : AG(mm_heap)->block_size; |
2690 | |
2691 | return SUCCESS; |
2692 | } |
2693 | |
2694 | ZEND_API size_t zend_memory_usage(int real_usage TSRMLS_DC) |
2695 | { |
2696 | if (real_usage) { |
2697 | return AG(mm_heap)->real_size; |
2698 | } else { |
2699 | size_t usage = AG(mm_heap)->size; |
2700 | #if ZEND_MM_CACHE |
2701 | usage -= AG(mm_heap)->cached; |
2702 | #endif |
2703 | return usage; |
2704 | } |
2705 | } |
2706 | |
2707 | ZEND_API size_t zend_memory_peak_usage(int real_usage TSRMLS_DC) |
2708 | { |
2709 | if (real_usage) { |
2710 | return AG(mm_heap)->real_peak; |
2711 | } else { |
2712 | return AG(mm_heap)->peak; |
2713 | } |
2714 | } |
2715 | |
2716 | ZEND_API void shutdown_memory_manager(int silent, int full_shutdown TSRMLS_DC) |
2717 | { |
2718 | zend_mm_shutdown(AG(mm_heap), full_shutdown, silent TSRMLS_CC); |
2719 | } |
2720 | |
2721 | static void alloc_globals_ctor(zend_alloc_globals *alloc_globals TSRMLS_DC) |
2722 | { |
2723 | char *tmp = getenv("USE_ZEND_ALLOC" ); |
2724 | |
2725 | if (tmp && !zend_atoi(tmp, 0)) { |
2726 | alloc_globals->mm_heap = malloc(sizeof(struct _zend_mm_heap)); |
2727 | memset(alloc_globals->mm_heap, 0, sizeof(struct _zend_mm_heap)); |
2728 | alloc_globals->mm_heap->use_zend_alloc = 0; |
2729 | alloc_globals->mm_heap->_malloc = malloc; |
2730 | alloc_globals->mm_heap->_free = free; |
2731 | alloc_globals->mm_heap->_realloc = realloc; |
2732 | } else { |
2733 | alloc_globals->mm_heap = zend_mm_startup(); |
2734 | } |
2735 | } |
2736 | |
2737 | #ifdef ZTS |
2738 | static void alloc_globals_dtor(zend_alloc_globals *alloc_globals TSRMLS_DC) |
2739 | { |
2740 | shutdown_memory_manager(1, 1 TSRMLS_CC); |
2741 | } |
2742 | #endif |
2743 | |
2744 | ZEND_API void start_memory_manager(TSRMLS_D) |
2745 | { |
2746 | #ifdef ZTS |
2747 | ts_allocate_id(&alloc_globals_id, sizeof(zend_alloc_globals), (ts_allocate_ctor) alloc_globals_ctor, (ts_allocate_dtor) alloc_globals_dtor); |
2748 | #else |
2749 | alloc_globals_ctor(&alloc_globals); |
2750 | #endif |
2751 | } |
2752 | |
2753 | ZEND_API zend_mm_heap *zend_mm_set_heap(zend_mm_heap *new_heap TSRMLS_DC) |
2754 | { |
2755 | zend_mm_heap *old_heap; |
2756 | |
2757 | old_heap = AG(mm_heap); |
2758 | AG(mm_heap) = new_heap; |
2759 | return old_heap; |
2760 | } |
2761 | |
2762 | ZEND_API zend_mm_storage *zend_mm_get_storage(zend_mm_heap *heap) |
2763 | { |
2764 | return heap->storage; |
2765 | } |
2766 | |
2767 | ZEND_API void zend_mm_set_custom_handlers(zend_mm_heap *heap, |
2768 | void* (*_malloc)(size_t), |
2769 | void (*_free)(void*), |
2770 | void* (*_realloc)(void*, size_t)) |
2771 | { |
2772 | heap->use_zend_alloc = 0; |
2773 | heap->_malloc = _malloc; |
2774 | heap->_free = _free; |
2775 | heap->_realloc = _realloc; |
2776 | } |
2777 | |
2778 | #if ZEND_DEBUG |
2779 | ZEND_API int _mem_block_check(void *ptr, int silent ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) |
2780 | { |
2781 | TSRMLS_FETCH(); |
2782 | |
2783 | if (!AG(mm_heap)->use_zend_alloc) { |
2784 | return 1; |
2785 | } |
2786 | return zend_mm_check_ptr(AG(mm_heap), ptr, silent ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
2787 | } |
2788 | |
2789 | |
2790 | ZEND_API void _full_mem_check(int silent ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) |
2791 | { |
2792 | int errors; |
2793 | TSRMLS_FETCH(); |
2794 | |
2795 | if (!AG(mm_heap)->use_zend_alloc) { |
2796 | return; |
2797 | } |
2798 | |
2799 | zend_debug_alloc_output("------------------------------------------------\n" ); |
2800 | zend_debug_alloc_output("Full Memory Check at %s:%d\n" ZEND_FILE_LINE_RELAY_CC); |
2801 | |
2802 | errors = zend_mm_check_heap(AG(mm_heap), silent ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); |
2803 | |
2804 | zend_debug_alloc_output("End of full memory check %s:%d (%d errors)\n" ZEND_FILE_LINE_RELAY_CC, errors); |
2805 | zend_debug_alloc_output("------------------------------------------------\n" ); |
2806 | } |
2807 | #endif |
2808 | |
2809 | /* |
2810 | * Local variables: |
2811 | * tab-width: 4 |
2812 | * c-basic-offset: 4 |
2813 | * indent-tabs-mode: t |
2814 | * End: |
2815 | */ |
2816 | |