diff --git a/Zend/tests/bug70258.phpt b/Zend/tests/bug70258.phpt index 40915a286ef9e..f601e0d9f2898 100644 --- a/Zend/tests/bug70258.phpt +++ b/Zend/tests/bug70258.phpt @@ -1,7 +1,7 @@ --TEST-- Bug #70258 (Segfault if do_resize fails to allocated memory) --INI-- -memory_limit=2M +memory_limit=4M --SKIPIF-- core(); ?> --EXPECTF-- -Fatal error: Allowed memory size of 2097152 bytes exhausted%s(tried to allocate %d bytes) in %s on line %d +Fatal error: Allowed memory size of 4194304 bytes exhausted%s(tried to allocate %d bytes) in %s on line %d diff --git a/Zend/tests/fibers/out-of-memory-in-fiber.phpt b/Zend/tests/fibers/out-of-memory-in-fiber.phpt index 4d31aa6f76adc..ce7a729f4c41c 100644 --- a/Zend/tests/fibers/out-of-memory-in-fiber.phpt +++ b/Zend/tests/fibers/out-of-memory-in-fiber.phpt @@ -1,7 +1,7 @@ --TEST-- Out of Memory in a fiber --INI-- -memory_limit=2M +memory_limit=4M --SKIPIF-- --INI-- -memory_limit=2M +memory_limit=4M --FILE-- --INI-- -memory_limit=2M +memory_limit=4M --FILE-- zones[0]; \ + zend_mm_zone *__end = &__heap->zones[ZEND_MM_ZONES]; \ + for (; __zone != __end; __zone++) { \ + _zone = __zone; + +# define ZEND_MM_ZONE_FOREACH_END() \ + } \ + } while (0) + +# define ZEND_MM_ZONE_FREE_SLOT(heap, num) (&(heap)->free_slot[ZEND_MM_ZONE_LEN * num]) +# define ZEND_MM_CURRENT_ZONE(heap) (&(heap)->zones[(uintptr_t)(&(heap)->zone_free_slot[0] - &(heap)->free_slot[0]) / ZEND_MM_ZONE_LEN]) +# define ZEND_MM_FREE_SLOT(heap, bin_num) ((heap)->zone_free_slot[(bin_num)]) +# define ZEND_MM_FREE_SLOT_EX(heap, chunk, bin_num) ((chunk)->zone_free_slot[(bin_num)]) +# define ZEND_MM_CHUNK_ZONE(heap, chunk) ((chunk)->zone) + +#else /* ZEND_MM_HEAP_PROTECTION */ + +# define ZEND_MM_ZONES 1 + +# define ZEND_MM_ZONE_FOREACH(_heap, _zone) do { \ + _zone = &_heap->zones[0]; \ + +# define ZEND_MM_ZONE_FOREACH_END() \ + } while (0) + +# define ZEND_MM_CURRENT_ZONE(heap) (&(heap)->zones[0]) +# define ZEND_MM_FREE_SLOT(heap, bin_num) ((heap)->free_slot[(bin_num)]) +# define ZEND_MM_FREE_SLOT_EX(heap, chunk, bin_num) ZEND_MM_FREE_SLOT(heap, bin_num) +# define ZEND_MM_CHUNK_ZONE(heap, chunk) (&(heap)->zones[0]) + +#endif /* ZEND_MM_HEAP_PROTECTION */ + #if UINTPTR_MAX == UINT64_MAX # define BSWAPPTR(u) ZEND_BYTES_SWAP64(u) #else @@ -227,10 +275,15 @@ typedef struct _zend_mm_page zend_mm_page; typedef struct _zend_mm_bin zend_mm_bin; typedef struct _zend_mm_free_slot zend_mm_free_slot; typedef struct _zend_mm_chunk zend_mm_chunk; +typedef struct _zend_mm_zone zend_mm_zone; typedef struct _zend_mm_huge_list zend_mm_huge_list; static bool zend_mm_use_huge_pages = false; +struct _zend_mm_zone { + zend_mm_chunk *chunks; +}; + /* * Memory is retrieved from OS by chunks of fixed size 2MB. * Inside chunk it's managed by pages of fixed size 4096B. @@ -275,7 +328,10 @@ struct _zend_mm_heap { size_t peak; /* peak memory usage */ #endif uintptr_t shadow_key; /* free slot shadow ptr xor key */ - zend_mm_free_slot *free_slot[ZEND_MM_BINS]; /* free lists for small sizes */ +#if ZEND_MM_HEAP_PROTECTION + zend_mm_free_slot **zone_free_slot; +#endif + zend_mm_free_slot *free_slot[ZEND_MM_FREE_SLOT_LEN]; /* free lists for small sizes */ #if ZEND_MM_STAT || ZEND_MM_LIMIT size_t real_size; /* current size of allocated pages */ #endif @@ -297,6 +353,7 @@ struct _zend_mm_heap { double avg_chunks_count; /* average number of chunks allocated per request */ int last_chunks_delete_boundary; /* number of chunks after last deletion */ int last_chunks_delete_count; /* number of deletion over the last boundary */ + zend_mm_zone zones[ZEND_MM_ZONES]; #if ZEND_MM_CUSTOM struct { void *(*_malloc)(size_t ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); @@ -313,17 +370,26 @@ struct _zend_mm_heap { struct _zend_mm_chunk { zend_mm_heap *heap; +#if ZEND_MM_HEAP_PROTECTION + zend_mm_free_slot **zone_free_slot; +#endif zend_mm_chunk *next; zend_mm_chunk *prev; uint32_t free_pages; /* number of free pages */ uint32_t free_tail; /* number of free pages at the end of chunk */ uint32_t num; - char reserve[64 - (sizeof(void*) * 3 + sizeof(uint32_t) * 3)]; + /* align heap_slot to cache line boundary (assumed to be 64 bytes) */ + char reserve[64 - (sizeof(void*) * 4 + sizeof(uint32_t) * 3)]; zend_mm_heap heap_slot; /* used only in main chunk */ +#if ZEND_MM_HEAP_PROTECTION + zend_mm_zone *zone; +#endif zend_mm_page_map free_map; /* 512 bits or 64 bytes */ zend_mm_page_info map[ZEND_MM_PAGES]; /* 2 KB = 512 * 4 */ }; +ZEND_STATIC_ASSERT(sizeof(zend_mm_chunk) <= ZEND_MM_FIRST_PAGE * ZEND_MM_PAGE_SIZE, ""); + struct _zend_mm_page { char bytes[ZEND_MM_PAGE_SIZE]; }; @@ -873,13 +939,23 @@ static int zend_mm_chunk_extend(zend_mm_heap *heap, void *addr, size_t old_size, #endif } -static zend_always_inline void zend_mm_chunk_init(zend_mm_heap *heap, zend_mm_chunk *chunk) +static zend_always_inline void zend_mm_chunk_init(zend_mm_heap *heap, zend_mm_zone *zone, zend_mm_chunk *chunk) { chunk->heap = heap; - chunk->next = heap->main_chunk; - chunk->prev = heap->main_chunk->prev; - chunk->prev->next = chunk; - chunk->next->prev = chunk; + if (UNEXPECTED(zone->chunks == NULL)) { + zone->chunks = chunk; + chunk->next = chunk; + chunk->prev = chunk; + } else { + chunk->next = zone->chunks; + chunk->prev = zone->chunks->prev; + chunk->prev->next = chunk; + chunk->next->prev = chunk; + } +#if ZEND_MM_HEAP_PROTECTION + chunk->zone_free_slot = ZEND_MM_ZONE_FREE_SLOT(heap, (uintptr_t)(zone - &heap->zones[0])); + chunk->zone = zone; +#endif /* mark first pages as allocated */ chunk->free_pages = ZEND_MM_PAGES - ZEND_MM_FIRST_PAGE; chunk->free_tail = ZEND_MM_FIRST_PAGE; @@ -914,10 +990,15 @@ static void *zend_mm_alloc_pages(zend_mm_heap *heap, uint32_t pages_count, size_ static void *zend_mm_alloc_pages(zend_mm_heap *heap, uint32_t pages_count ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) #endif { - zend_mm_chunk *chunk = heap->main_chunk; + zend_mm_zone *zone = ZEND_MM_CURRENT_ZONE(heap); + zend_mm_chunk *chunk = zone->chunks; uint32_t page_num, len; int steps = 0; + if (UNEXPECTED(!chunk)) { + goto get_chunk; + } + while (1) { if (UNEXPECTED(chunk->free_pages < pages_count)) { goto not_found; @@ -1033,7 +1114,7 @@ static void *zend_mm_alloc_pages(zend_mm_heap *heap, uint32_t pages_count ZEND_F } not_found: - if (chunk->next == heap->main_chunk) { + if (chunk->next == zone->chunks) { get_chunk: if (heap->cached_chunks) { heap->cached_chunks_count--; @@ -1087,7 +1168,7 @@ static void *zend_mm_alloc_pages(zend_mm_heap *heap, uint32_t pages_count ZEND_F if (heap->chunks_count > heap->peak_chunks_count) { heap->peak_chunks_count = heap->chunks_count; } - zend_mm_chunk_init(heap, chunk); + zend_mm_chunk_init(heap, zone, chunk); page_num = ZEND_MM_FIRST_PAGE; len = ZEND_MM_PAGES - ZEND_MM_FIRST_PAGE; goto found; @@ -1105,8 +1186,8 @@ static void *zend_mm_alloc_pages(zend_mm_heap *heap, uint32_t pages_count ZEND_F /* move chunk into the head of the linked-list */ chunk->prev->next = chunk->next; chunk->next->prev = chunk->prev; - chunk->next = heap->main_chunk->next; - chunk->prev = heap->main_chunk; + chunk->next = zone->chunks->next; + chunk->prev = zone->chunks; chunk->prev->next = chunk; chunk->next->prev = chunk; } @@ -1149,8 +1230,13 @@ static zend_always_inline void zend_mm_delete_chunk(zend_mm_heap *heap, zend_mm_ ZEND_MM_CHECK(chunk->next->prev == chunk, "zend_mm_heap corrupted"); ZEND_MM_CHECK(chunk->prev->next == chunk, "zend_mm_heap corrupted"); - chunk->next->prev = chunk->prev; - chunk->prev->next = chunk->next; + if (ZEND_MM_CHUNK_ZONE(heap, chunk)->chunks == chunk && chunk->next == chunk) { + ZEND_ASSERT(chunk->prev == chunk); + ZEND_MM_CHUNK_ZONE(heap, chunk)->chunks = NULL; + } else { + chunk->next->prev = chunk->prev; + chunk->prev->next = chunk->next; + } heap->chunks_count--; if (heap->chunks_count + heap->cached_chunks_count < heap->avg_chunks_count + 0.1 || (heap->chunks_count == heap->last_chunks_delete_boundary @@ -1367,7 +1453,7 @@ static zend_never_inline void *zend_mm_alloc_small_slow(zend_mm_heap *heap, uint /* create a linked list of elements from 1 to last */ end = (zend_mm_free_slot*)((char*)bin + (bin_data_size[bin_num] * (bin_elements[bin_num] - 1))); - heap->free_slot[bin_num] = p = (zend_mm_free_slot*)((char*)bin + bin_data_size[bin_num]); + ZEND_MM_FREE_SLOT(heap, bin_num) = p = (zend_mm_free_slot*)((char*)bin + bin_data_size[bin_num]); do { zend_mm_set_next_free_slot(heap, bin_num, p, (zend_mm_free_slot*)((char*)p + bin_data_size[bin_num])); #if ZEND_DEBUG @@ -1394,6 +1480,7 @@ static zend_never_inline void *zend_mm_alloc_small_slow(zend_mm_heap *heap, uint static zend_always_inline void *zend_mm_alloc_small(zend_mm_heap *heap, int bin_num ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { + ZEND_ASSERT(bin_num <= ZEND_MM_BINS); ZEND_ASSERT(bin_data_size[bin_num] >= ZEND_MM_MIN_USEABLE_BIN_SIZE); #if ZEND_MM_STAT @@ -1405,17 +1492,18 @@ static zend_always_inline void *zend_mm_alloc_small(zend_mm_heap *heap, int bin_ } while (0); #endif - if (EXPECTED(heap->free_slot[bin_num] != NULL)) { - zend_mm_free_slot *p = heap->free_slot[bin_num]; - heap->free_slot[bin_num] = zend_mm_get_next_free_slot(heap, bin_num, p); + if (EXPECTED(ZEND_MM_FREE_SLOT(heap, bin_num) != NULL)) { + zend_mm_free_slot *p = ZEND_MM_FREE_SLOT(heap, bin_num); + ZEND_MM_FREE_SLOT(heap, bin_num) = zend_mm_get_next_free_slot(heap, bin_num, p); return p; } else { return zend_mm_alloc_small_slow(heap, bin_num ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } } -static zend_always_inline void zend_mm_free_small(zend_mm_heap *heap, void *ptr, int bin_num) +static zend_always_inline void zend_mm_free_small(zend_mm_heap *heap, zend_mm_chunk *chunk, void *ptr, int bin_num) { + ZEND_ASSERT(bin_num <= ZEND_MM_BINS); ZEND_ASSERT(bin_data_size[bin_num] >= ZEND_MM_MIN_USEABLE_BIN_SIZE); zend_mm_free_slot *p; @@ -1432,8 +1520,8 @@ static zend_always_inline void zend_mm_free_small(zend_mm_heap *heap, void *ptr, #endif p = (zend_mm_free_slot*)ptr; - zend_mm_set_next_free_slot(heap, bin_num, p, heap->free_slot[bin_num]); - heap->free_slot[bin_num] = p; + zend_mm_set_next_free_slot(heap, bin_num, p, ZEND_MM_FREE_SLOT_EX(heap, chunk, bin_num)); + ZEND_MM_FREE_SLOT_EX(heap, chunk, bin_num) = p; } /********/ @@ -1529,7 +1617,7 @@ static zend_always_inline void zend_mm_free_heap(zend_mm_heap *heap, void *ptr Z ZEND_MM_CHECK(chunk->heap == heap, "zend_mm_heap corrupted"); if (EXPECTED(info & ZEND_MM_IS_SRUN)) { - zend_mm_free_small(heap, ptr, ZEND_MM_SRUN_BIN_NUM(info)); + zend_mm_free_small(heap, chunk, ptr, ZEND_MM_SRUN_BIN_NUM(info)); } else /* if (info & ZEND_MM_IS_LRUN) */ { int pages_count = ZEND_MM_LRUN_PAGES(info); @@ -1719,7 +1807,7 @@ static zend_always_inline void *zend_mm_realloc_heap(zend_mm_heap *heap, void *p ret = zend_mm_alloc_small(heap, ZEND_MM_SMALL_SIZE_TO_BIN(size) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); copy_size = use_copy_size ? MIN(size, copy_size) : size; memcpy(ret, ptr, copy_size); - zend_mm_free_small(heap, ptr, old_bin_num); + zend_mm_free_small(heap, chunk, ptr, old_bin_num); } else { /* reallocation in-place */ ret = ptr; @@ -1734,7 +1822,7 @@ static zend_always_inline void *zend_mm_realloc_heap(zend_mm_heap *heap, void *p ret = zend_mm_alloc_small(heap, ZEND_MM_SMALL_SIZE_TO_BIN(size) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); copy_size = use_copy_size ? MIN(old_size, copy_size) : old_size; memcpy(ret, ptr, copy_size); - zend_mm_free_small(heap, ptr, old_bin_num); + zend_mm_free_small(heap, chunk, ptr, old_bin_num); #if ZEND_MM_STAT heap->peak = MAX(orig_peak, heap->size); } while (0); @@ -2030,6 +2118,10 @@ static zend_mm_heap *zend_mm_init(void) } heap = &chunk->heap_slot; chunk->heap = heap; +#if ZEND_MM_HEAP_PROTECTION + chunk->zone_free_slot = ZEND_MM_ZONE_FREE_SLOT(heap, ZEND_MM_ZONE_DEFAULT); + chunk->zone = &heap->zones[0]; +#endif chunk->next = chunk; chunk->prev = chunk; chunk->free_pages = ZEND_MM_PAGES - ZEND_MM_FIRST_PAGE; @@ -2039,6 +2131,13 @@ static zend_mm_heap *zend_mm_init(void) chunk->map[0] = ZEND_MM_LRUN(ZEND_MM_FIRST_PAGE); heap->main_chunk = chunk; heap->cached_chunks = NULL; +#if ZEND_MM_HEAP_PROTECTION + heap->zone_free_slot = ZEND_MM_ZONE_FREE_SLOT(heap, ZEND_MM_ZONE_DEFAULT); +#endif + heap->zones[0].chunks = chunk; +#if ZEND_MM_HEAP_PROTECTION + heap->zones[1].chunks = NULL; +#endif heap->chunks_count = 1; heap->peak_chunks_count = 1; heap->cached_chunks_count = 0; @@ -2076,9 +2175,10 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap) size_t page_offset; int page_num; zend_mm_page_info info; - uint32_t i, free_counter; + uint32_t i, free_counter, bin_num; bool has_free_pages; size_t collected = 0; + zend_mm_zone *zone; #if ZEND_MM_CUSTOM if (heap->use_custom_heap) { @@ -2090,9 +2190,10 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap) } #endif - for (i = 0; i < ZEND_MM_BINS; i++) { + for (i = 0; i < ZEND_MM_FREE_SLOT_LEN; i++) { has_free_pages = false; p = heap->free_slot[i]; + bin_num = i % ZEND_MM_ZONE_LEN; while (p != NULL) { chunk = (zend_mm_chunk*)ZEND_MM_ALIGNED_BASE(p, ZEND_MM_CHUNK_SIZE); ZEND_MM_CHECK(chunk->heap == heap, "zend_mm_heap corrupted"); @@ -2107,13 +2208,13 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap) ZEND_ASSERT(info & ZEND_MM_IS_SRUN); ZEND_ASSERT(!(info & ZEND_MM_IS_LRUN)); } - ZEND_ASSERT(ZEND_MM_SRUN_BIN_NUM(info) == i); + ZEND_ASSERT(ZEND_MM_SRUN_BIN_NUM(info) == bin_num); free_counter = ZEND_MM_SRUN_FREE_COUNTER(info) + 1; - if (free_counter == bin_elements[i]) { + if (free_counter == bin_elements[bin_num]) { has_free_pages = true; } - chunk->map[page_num] = ZEND_MM_SRUN_EX(i, free_counter); - p = zend_mm_get_next_free_slot(heap, i, p); + chunk->map[page_num] = ZEND_MM_SRUN_EX(bin_num, free_counter); + p = zend_mm_get_next_free_slot(heap, bin_num, p); } if (!has_free_pages) { @@ -2136,61 +2237,66 @@ ZEND_API size_t zend_mm_gc(zend_mm_heap *heap) ZEND_ASSERT(info & ZEND_MM_IS_SRUN); ZEND_ASSERT(!(info & ZEND_MM_IS_LRUN)); } - ZEND_ASSERT(ZEND_MM_SRUN_BIN_NUM(info) == i); - if (ZEND_MM_SRUN_FREE_COUNTER(info) == bin_elements[i]) { + ZEND_ASSERT(ZEND_MM_SRUN_BIN_NUM(info) == bin_num); + if (ZEND_MM_SRUN_FREE_COUNTER(info) == bin_elements[bin_num]) { /* remove from cache */ - p = zend_mm_get_next_free_slot(heap, i, p); + p = zend_mm_get_next_free_slot(heap, bin_num, p); if (q == (zend_mm_free_slot*)&heap->free_slot[i]) { q->next_free_slot = p; } else { - zend_mm_set_next_free_slot(heap, i, q, p); + zend_mm_set_next_free_slot(heap, bin_num, q, p); } } else { q = p; if (q == (zend_mm_free_slot*)&heap->free_slot[i]) { p = q->next_free_slot; } else { - p = zend_mm_get_next_free_slot(heap, i, q); + p = zend_mm_get_next_free_slot(heap, bin_num, q); } } } } - chunk = heap->main_chunk; - do { - i = ZEND_MM_FIRST_PAGE; - while (i < chunk->free_tail) { - if (zend_mm_bitset_is_set(chunk->free_map, i)) { - info = chunk->map[i]; - if (info & ZEND_MM_IS_SRUN) { - int bin_num = ZEND_MM_SRUN_BIN_NUM(info); - int pages_count = bin_pages[bin_num]; - - if (ZEND_MM_SRUN_FREE_COUNTER(info) == bin_elements[bin_num]) { - /* all elements are free */ - zend_mm_free_pages_ex(heap, chunk, i, pages_count, 0); - collected += pages_count; - } else { - /* reset counter */ - chunk->map[i] = ZEND_MM_SRUN(bin_num); + ZEND_MM_ZONE_FOREACH(heap, zone) { + chunk = zone->chunks; + if (chunk == NULL) { + continue; + } + do { + i = ZEND_MM_FIRST_PAGE; + while (i < chunk->free_tail) { + if (zend_mm_bitset_is_set(chunk->free_map, i)) { + info = chunk->map[i]; + if (info & ZEND_MM_IS_SRUN) { + bin_num = ZEND_MM_SRUN_BIN_NUM(info); + int pages_count = bin_pages[bin_num]; + + if (ZEND_MM_SRUN_FREE_COUNTER(info) == bin_elements[bin_num]) { + /* all elements are free */ + zend_mm_free_pages_ex(heap, chunk, i, pages_count, 0); + collected += pages_count; + } else { + /* reset counter */ + chunk->map[i] = ZEND_MM_SRUN(bin_num); + } + i += bin_pages[bin_num]; + } else /* if (info & ZEND_MM_IS_LRUN) */ { + i += ZEND_MM_LRUN_PAGES(info); } - i += bin_pages[bin_num]; - } else /* if (info & ZEND_MM_IS_LRUN) */ { - i += ZEND_MM_LRUN_PAGES(info); + } else { + i++; } - } else { - i++; } - } - if (chunk->free_pages == ZEND_MM_PAGES - ZEND_MM_FIRST_PAGE) { - zend_mm_chunk *next_chunk = chunk->next; + if (chunk->free_pages == ZEND_MM_PAGES - ZEND_MM_FIRST_PAGE) { + zend_mm_chunk *next_chunk = chunk->next; - zend_mm_delete_chunk(heap, chunk); - chunk = next_chunk; - } else { - chunk = chunk->next; - } - } while (chunk != heap->main_chunk); + zend_mm_delete_chunk(heap, chunk); + chunk = next_chunk; + } else { + chunk = chunk->next; + } + } while (zone->chunks && chunk != zone->chunks); + } ZEND_MM_ZONE_FOREACH_END(); return collected * ZEND_MM_PAGE_SIZE; } @@ -2227,7 +2333,7 @@ static zend_long zend_mm_find_leaks_small(zend_mm_chunk *p, uint32_t i, uint32_t return count; } -static zend_long zend_mm_find_leaks(zend_mm_heap *heap, zend_mm_chunk *p, uint32_t i, zend_leak_info *leak) +static zend_long zend_mm_find_leaks(zend_mm_zone *zone, zend_mm_chunk *p, uint32_t i, zend_leak_info *leak) { zend_long count = 0; @@ -2254,7 +2360,7 @@ static zend_long zend_mm_find_leaks(zend_mm_heap *heap, zend_mm_chunk *p, uint32 } p = p->next; i = ZEND_MM_FIRST_PAGE; - } while (p != heap->main_chunk); + } while (p != zone->chunks); return count; } @@ -2287,6 +2393,7 @@ static void zend_mm_check_leaks(zend_mm_heap *heap) zend_long repeated = 0; uint32_t total = 0; uint32_t i, j; + zend_mm_zone *zone; /* find leaked huge blocks and free them */ list = heap->huge_list; @@ -2313,73 +2420,78 @@ static void zend_mm_check_leaks(zend_mm_heap *heap) zend_mm_free_heap(heap, q, NULL, 0, NULL, 0); } - /* for each chunk */ - p = heap->main_chunk; - do { - i = ZEND_MM_FIRST_PAGE; - while (i < p->free_tail) { - if (zend_mm_bitset_is_set(p->free_map, i)) { - if (p->map[i] & ZEND_MM_IS_SRUN) { - int bin_num = ZEND_MM_SRUN_BIN_NUM(p->map[i]); - zend_mm_debug_info *dbg = (zend_mm_debug_info*)((char*)p + ZEND_MM_PAGE_SIZE * i + bin_data_size[bin_num] - ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info))); - - j = 0; - while (j < bin_elements[bin_num]) { - if (dbg->size != 0) { - leak.addr = (zend_mm_debug_info*)((char*)p + ZEND_MM_PAGE_SIZE * i + bin_data_size[bin_num] * j); - leak.size = dbg->size; - leak.filename = dbg->filename; - leak.orig_filename = dbg->orig_filename; - leak.lineno = dbg->lineno; - leak.orig_lineno = dbg->orig_lineno; - - zend_message_dispatcher(ZMSG_LOG_SCRIPT_NAME, NULL); - zend_message_dispatcher(ZMSG_MEMORY_LEAK_DETECTED, &leak); - - dbg->size = 0; - dbg->filename = NULL; - dbg->lineno = 0; - - repeated = zend_mm_find_leaks_small(p, i, j + 1, &leak) + - zend_mm_find_leaks(heap, p, i + bin_pages[bin_num], &leak); - total += 1 + repeated; - if (repeated) { - zend_message_dispatcher(ZMSG_MEMORY_LEAK_REPEATED, (void *)(uintptr_t)repeated); + ZEND_MM_ZONE_FOREACH(heap, zone) { + /* for each chunk */ + p = zone->chunks; + if (p == NULL) { + continue; + } + do { + i = ZEND_MM_FIRST_PAGE; + while (i < p->free_tail) { + if (zend_mm_bitset_is_set(p->free_map, i)) { + if (p->map[i] & ZEND_MM_IS_SRUN) { + int bin_num = ZEND_MM_SRUN_BIN_NUM(p->map[i]); + zend_mm_debug_info *dbg = (zend_mm_debug_info*)((char*)p + ZEND_MM_PAGE_SIZE * i + bin_data_size[bin_num] - ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info))); + + j = 0; + while (j < bin_elements[bin_num]) { + if (dbg->size != 0) { + leak.addr = (zend_mm_debug_info*)((char*)p + ZEND_MM_PAGE_SIZE * i + bin_data_size[bin_num] * j); + leak.size = dbg->size; + leak.filename = dbg->filename; + leak.orig_filename = dbg->orig_filename; + leak.lineno = dbg->lineno; + leak.orig_lineno = dbg->orig_lineno; + + zend_message_dispatcher(ZMSG_LOG_SCRIPT_NAME, NULL); + zend_message_dispatcher(ZMSG_MEMORY_LEAK_DETECTED, &leak); + + dbg->size = 0; + dbg->filename = NULL; + dbg->lineno = 0; + + repeated = zend_mm_find_leaks_small(p, i, j + 1, &leak) + + zend_mm_find_leaks(zone, p, i + bin_pages[bin_num], &leak); + total += 1 + repeated; + if (repeated) { + zend_message_dispatcher(ZMSG_MEMORY_LEAK_REPEATED, (void *)(uintptr_t)repeated); + } } + dbg = (zend_mm_debug_info*)((char*)dbg + bin_data_size[bin_num]); + j++; } - dbg = (zend_mm_debug_info*)((char*)dbg + bin_data_size[bin_num]); - j++; - } - i += bin_pages[bin_num]; - } else /* if (p->map[i] & ZEND_MM_IS_LRUN) */ { - int pages_count = ZEND_MM_LRUN_PAGES(p->map[i]); - zend_mm_debug_info *dbg = (zend_mm_debug_info*)((char*)p + ZEND_MM_PAGE_SIZE * (i + pages_count) - ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info))); - - leak.addr = (void*)((char*)p + ZEND_MM_PAGE_SIZE * i); - leak.size = dbg->size; - leak.filename = dbg->filename; - leak.orig_filename = dbg->orig_filename; - leak.lineno = dbg->lineno; - leak.orig_lineno = dbg->orig_lineno; - - zend_message_dispatcher(ZMSG_LOG_SCRIPT_NAME, NULL); - zend_message_dispatcher(ZMSG_MEMORY_LEAK_DETECTED, &leak); - - zend_mm_bitset_reset_range(p->free_map, i, pages_count); - - repeated = zend_mm_find_leaks(heap, p, i + pages_count, &leak); - total += 1 + repeated; - if (repeated) { - zend_message_dispatcher(ZMSG_MEMORY_LEAK_REPEATED, (void *)(uintptr_t)repeated); + i += bin_pages[bin_num]; + } else /* if (p->map[i] & ZEND_MM_IS_LRUN) */ { + int pages_count = ZEND_MM_LRUN_PAGES(p->map[i]); + zend_mm_debug_info *dbg = (zend_mm_debug_info*)((char*)p + ZEND_MM_PAGE_SIZE * (i + pages_count) - ZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_debug_info))); + + leak.addr = (void*)((char*)p + ZEND_MM_PAGE_SIZE * i); + leak.size = dbg->size; + leak.filename = dbg->filename; + leak.orig_filename = dbg->orig_filename; + leak.lineno = dbg->lineno; + leak.orig_lineno = dbg->orig_lineno; + + zend_message_dispatcher(ZMSG_LOG_SCRIPT_NAME, NULL); + zend_message_dispatcher(ZMSG_MEMORY_LEAK_DETECTED, &leak); + + zend_mm_bitset_reset_range(p->free_map, i, pages_count); + + repeated = zend_mm_find_leaks(zone, p, i + pages_count, &leak); + total += 1 + repeated; + if (repeated) { + zend_message_dispatcher(ZMSG_MEMORY_LEAK_REPEATED, (void *)(uintptr_t)repeated); + } + i += pages_count; } - i += pages_count; + } else { + i++; } - } else { - i++; } - } - p = p->next; - } while (p != heap->main_chunk); + p = p->next; + } while (p != zone->chunks); + } ZEND_MM_ZONE_FOREACH_END(); if (total) { zend_message_dispatcher(ZMSG_MEMORY_LEAKS_GRAND_TOTAL, &total); } @@ -2395,6 +2507,7 @@ ZEND_API void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) { zend_mm_chunk *p; zend_mm_huge_list *list; + zend_mm_zone *zone; #if ZEND_MM_CUSTOM if (heap->use_custom_heap) { @@ -2444,16 +2557,25 @@ ZEND_API void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) zend_mm_chunk_free(heap, q->ptr, q->size); } - /* move all chunks except of the first one into the cache */ - p = heap->main_chunk->next; - while (p != heap->main_chunk) { - zend_mm_chunk *q = p->next; - p->next = heap->cached_chunks; - heap->cached_chunks = p; - p = q; - heap->chunks_count--; - heap->cached_chunks_count++; - } + /* move all chunks except of the main one into the cache */ + ZEND_MM_ZONE_FOREACH(heap, zone) { + p = zone->chunks; + if (p == NULL) { + continue; + } + do { + zend_mm_chunk *q = p->next; + if (p == heap->main_chunk) { + p = q; + continue; + } + p->next = heap->cached_chunks; + heap->cached_chunks = p; + p = q; + heap->chunks_count--; + heap->cached_chunks_count++; + } while (p != zone->chunks); + } ZEND_MM_ZONE_FOREACH_END(); if (full) { /* free all cached chunks */ @@ -2467,6 +2589,7 @@ ZEND_API void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) } else { /* free some cached chunks to keep average count */ heap->avg_chunks_count = (heap->avg_chunks_count + (double)heap->peak_chunks_count) / 2.0; + while ((double)heap->cached_chunks_count + 0.9 > heap->avg_chunks_count && heap->cached_chunks) { p = heap->cached_chunks; @@ -2507,6 +2630,16 @@ ZEND_API void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent) heap->last_chunks_delete_boundary = 0; heap->last_chunks_delete_count = 0; +#if ZEND_MM_HEAP_PROTECTION + heap->zone_free_slot = ZEND_MM_ZONE_FREE_SLOT(heap, ZEND_MM_ZONE_DEFAULT); +#endif + heap->zones[0].chunks = p; +#if ZEND_MM_HEAP_PROTECTION + heap->zones[1].chunks = NULL; + ZEND_MM_CHECK(p->zone == &heap->zones[0], "zend_mm_heap corrupted"); + ZEND_MM_CHECK(p->zone_free_slot == ZEND_MM_ZONE_FREE_SLOT(heap, ZEND_MM_ZONE_DEFAULT), "zend_mm_heap corrupted"); +#endif + memset(p->free_map, 0, sizeof(p->free_map) + sizeof(p->map)); p->free_map[0] = (1L << ZEND_MM_FIRST_PAGE) - 1; p->map[0] = ZEND_MM_LRUN(ZEND_MM_FIRST_PAGE); @@ -2568,6 +2701,7 @@ ZEND_API size_t ZEND_FASTCALL _zend_mm_block_size(zend_mm_heap *heap, void *ptr typedef struct _zend_alloc_globals { zend_mm_heap *mm_heap; + uint32_t use_userinput_zone; } zend_alloc_globals; #ifdef ZTS @@ -2579,6 +2713,10 @@ static size_t alloc_globals_offset; static zend_alloc_globals alloc_globals; #endif +#if ZEND_MM_HEAP_PROTECTION +# define ZEND_MM_ZONE_USERINPUT 1 +#endif + ZEND_API bool is_zend_mm(void) { #if ZEND_MM_CUSTOM @@ -2627,6 +2765,33 @@ ZEND_API bool is_zend_ptr(const void *ptr) return 0; } +ZEND_API void zend_mm_userinput_begin(void) +{ +#if ZEND_MM_HEAP_PROTECTION + AG(use_userinput_zone)++; + AG(mm_heap)->zone_free_slot = ZEND_MM_ZONE_FREE_SLOT(AG(mm_heap), ZEND_MM_ZONE_USERINPUT); +#endif +} + +ZEND_API void zend_mm_userinput_end(void) +{ +#if ZEND_MM_HEAP_PROTECTION + AG(use_userinput_zone)--; + if (!AG(use_userinput_zone)) { + AG(mm_heap)->zone_free_slot = ZEND_MM_ZONE_FREE_SLOT(AG(mm_heap), ZEND_MM_ZONE_DEFAULT); + } +#endif +} + +ZEND_API bool zend_mm_check_in_userinput(void) +{ +#if ZEND_MM_HEAP_PROTECTION + return AG(use_userinput_zone); +#else + return true; +#endif +} + #if !ZEND_DEBUG && defined(HAVE_BUILTIN_CONSTANT_P) #undef _emalloc @@ -2685,7 +2850,7 @@ ZEND_API void* ZEND_FASTCALL _emalloc_huge(size_t size) ZEND_MM_CHECK(chunk->heap == AG(mm_heap), "zend_mm_heap corrupted"); \ ZEND_ASSERT(chunk->map[page_num] & ZEND_MM_IS_SRUN); \ ZEND_ASSERT(ZEND_MM_SRUN_BIN_NUM(chunk->map[page_num]) == _num); \ - zend_mm_free_small(AG(mm_heap), ptr, _num); \ + zend_mm_free_small(AG(mm_heap), chunk, ptr, _num); \ } \ } #else @@ -2699,7 +2864,7 @@ ZEND_API void* ZEND_FASTCALL _emalloc_huge(size_t size) { \ zend_mm_chunk *chunk = (zend_mm_chunk*)ZEND_MM_ALIGNED_BASE(ptr, ZEND_MM_CHUNK_SIZE); \ ZEND_MM_CHECK(chunk->heap == AG(mm_heap), "zend_mm_heap corrupted"); \ - zend_mm_free_small(AG(mm_heap), ptr, _num); \ + zend_mm_free_small(AG(mm_heap), chunk, ptr, _num); \ } \ } #endif @@ -2922,6 +3087,12 @@ ZEND_API void zend_memory_reset_peak_usage(void) ZEND_API void shutdown_memory_manager(bool silent, bool full_shutdown) { zend_mm_shutdown(AG(mm_heap), full_shutdown, silent); + + if (!full_shutdown) { + ZEND_ASSERT(AG(use_userinput_zone) == 0 || silent); + AG(use_userinput_zone) = 0; + zend_mm_userinput_begin(); + } } static ZEND_COLD ZEND_NORETURN void zend_out_of_memory(void) @@ -3025,6 +3196,8 @@ static void alloc_globals_ctor(zend_alloc_globals *alloc_globals) { char *tmp; + alloc_globals->use_userinput_zone = 0; + #if ZEND_MM_CUSTOM tmp = getenv("USE_ZEND_ALLOC"); if (tmp && !ZEND_ATOL(tmp)) { diff --git a/Zend/zend_alloc.h b/Zend/zend_alloc.h index 541989a2a13e0..7f7d0e95e621e 100644 --- a/Zend/zend_alloc.h +++ b/Zend/zend_alloc.h @@ -222,6 +222,9 @@ ZEND_API void start_memory_manager(void); ZEND_API void shutdown_memory_manager(bool silent, bool full_shutdown); ZEND_API bool is_zend_mm(void); ZEND_API bool is_zend_ptr(const void *ptr); +ZEND_API void zend_mm_userinput_begin(void); +ZEND_API void zend_mm_userinput_end(void); +ZEND_API bool zend_mm_check_in_userinput(void); ZEND_API size_t zend_memory_usage(bool real_usage); ZEND_API size_t zend_memory_peak_usage(bool real_usage); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 73bc661cd40e3..3d2395d40ebb6 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1974,7 +1974,9 @@ ZEND_API bool zend_is_auto_global_str(const char *name, size_t len) /* {{{ */ { if ((auto_global = zend_hash_str_find_ptr(CG(auto_globals), name, len)) != NULL) { if (auto_global->armed) { + zend_mm_userinput_begin(); auto_global->armed = auto_global->auto_global_callback(auto_global->name); + zend_mm_userinput_end(); } return 1; } @@ -1988,7 +1990,9 @@ ZEND_API bool zend_is_auto_global(zend_string *name) /* {{{ */ if ((auto_global = zend_hash_find_ptr(CG(auto_globals), name)) != NULL) { if (auto_global->armed) { + zend_mm_userinput_begin(); auto_global->armed = auto_global->auto_global_callback(auto_global->name); + zend_mm_userinput_end(); } return 1; } @@ -2015,6 +2019,8 @@ ZEND_API void zend_activate_auto_globals(void) /* {{{ */ { zend_auto_global *auto_global; + ZEND_ASSERT(zend_mm_check_in_userinput()); + ZEND_HASH_MAP_FOREACH_PTR(CG(auto_globals), auto_global) { if (auto_global->jit) { auto_global->armed = 1; diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 406c539f44a27..85c6dbf1ee3be 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -22,6 +22,7 @@ #include "main/php.h" #include "main/php_globals.h" #include "zend.h" +#include "zend_alloc.h" #include "zend_extensions.h" #include "zend_compile.h" #include "ZendAccelerator.h" @@ -4640,6 +4641,7 @@ static zend_result accel_finish_startup_preload(bool in_child) orig_error_reporting = EG(error_reporting); EG(error_reporting) = 0; + zend_mm_userinput_begin(); const zend_result rc = php_request_startup(); EG(error_reporting) = orig_error_reporting; diff --git a/ext/spl/tests/gh14639.phpt b/ext/spl/tests/gh14639.phpt index 520ee20839c28..b1a87851255ef 100644 --- a/ext/spl/tests/gh14639.phpt +++ b/ext/spl/tests/gh14639.phpt @@ -1,7 +1,7 @@ --TEST-- GH-14639 (Member access within null pointer in ext/spl/spl_observer.c) --INI-- -memory_limit=2M +memory_limit=4M --SKIPIF-- 0) { fputs($fp, str_pad('', min($chunk,$size))); diff --git a/ext/zend_test/tests/observer_error_01.phpt b/ext/zend_test/tests/observer_error_01.phpt index d20a23b00b293..36a8f6e8f7879 100644 --- a/ext/zend_test/tests/observer_error_01.phpt +++ b/ext/zend_test/tests/observer_error_01.phpt @@ -7,7 +7,7 @@ zend_test.observer.enabled=1 zend_test.observer.show_output=1 zend_test.observer.observe_all=1 zend_test.observer.show_return_value=1 -memory_limit=2M +memory_limit=4M --SKIPIF-- -Fatal error: Allowed memory size of 2097152 bytes exhausted%s(tried to allocate %d bytes) in %s on line %d +Fatal error: Allowed memory size of 4194304 bytes exhausted%s(tried to allocate %d bytes) in %s on line %d diff --git a/main/main.c b/main/main.c index 0b38f303c58fc..cda35ccbe2bce 100644 --- a/main/main.c +++ b/main/main.c @@ -1802,6 +1802,8 @@ zend_result php_request_startup(void) { zend_result retval = SUCCESS; + ZEND_ASSERT(zend_mm_check_in_userinput()); + zend_interned_strings_activate(); #ifdef HAVE_DTRACE @@ -1877,6 +1879,8 @@ zend_result php_request_startup(void) SG(sapi_started) = 1; + zend_mm_userinput_end(); + return retval; } /* }}} */ diff --git a/tests/lang/bug45392.phpt b/tests/lang/bug45392.phpt index f470f2ccddbd7..44bc041df2b0f 100644 --- a/tests/lang/bug45392.phpt +++ b/tests/lang/bug45392.phpt @@ -17,10 +17,10 @@ if (PHP_OS_FAMILY === "Windows" && PHP_INT_SIZE == 8 && $tracing) { --FILE--