Commit 16ca82827f6 for php.net
commit 16ca82827f6f88e1422e73a8f5c732f1173c85b6
Author: Julien Voisin <jvoisin@users.noreply.github.com>
Date: Thu Sep 17 16:44:05 2026 +0200
Check the links before unlinking a huge block (#23568)
The same primitive was already fixed for the chunk list in 519920b2839 and
for stream buckets in 492ebb54c1a; the huge list was missed.
Costs one pointer per huge block, growing zend_mm_huge_list from 24 to 32
bytes on 64-bit, i.e. one small slot per allocation of at least 2MB.
diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c
index f6c0a1ad0e9..d6fc959e9c4 100644
--- a/Zend/zend_alloc.c
+++ b/Zend/zend_alloc.c
@@ -356,6 +356,7 @@ struct _zend_mm_huge_list {
void *ptr;
size_t size;
zend_mm_huge_list *next;
+ zend_mm_huge_list *prev;
#if ZEND_DEBUG
zend_mm_debug_info dbg;
#endif
@@ -1931,6 +1932,10 @@ static void zend_mm_add_huge_block(zend_mm_heap *heap, void *ptr, size_t size ZE
list->ptr = ptr;
list->size = size;
list->next = heap->huge_list;
+ list->prev = NULL;
+ if (heap->huge_list) {
+ heap->huge_list->prev = list;
+ }
#if ZEND_DEBUG
list->dbg.size = dbg_size;
list->dbg.filename = __zend_filename;
@@ -1943,22 +1948,28 @@ static void zend_mm_add_huge_block(zend_mm_heap *heap, void *ptr, size_t size ZE
static size_t zend_mm_del_huge_block(zend_mm_heap *heap, void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC)
{
- zend_mm_huge_list *prev = NULL;
zend_mm_huge_list *list = heap->huge_list;
while (list != NULL) {
if (list->ptr == ptr) {
size_t size;
- if (prev) {
- prev->next = list->next;
+ /* Unlinking writes through these, so make sure they still point back
+ * at this block before trusting them. */
+ ZEND_MM_CHECK(list->prev ? list->prev->next == list : heap->huge_list == list, "zend_mm_heap corrupted");
+ ZEND_MM_CHECK(!list->next || list->next->prev == list, "zend_mm_heap corrupted");
+
+ if (list->prev) {
+ list->prev->next = list->next;
} else {
heap->huge_list = list->next;
}
+ if (list->next) {
+ list->next->prev = list->prev;
+ }
size = list->size;
zend_mm_free_heap(heap, list ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC);
return size;
}
- prev = list;
list = list->next;
}
ZEND_MM_CHECK(0, "zend_mm_heap corrupted");
@@ -2388,6 +2399,9 @@ static zend_long zend_mm_find_leaks_huge(zend_mm_heap *heap, zend_mm_huge_list *
while (p) {
if (p->dbg.filename == list->dbg.filename && p->dbg.lineno == list->dbg.lineno) {
prev->next = p->next;
+ if (p->next) {
+ p->next->prev = prev;
+ }
zend_mm_chunk_free(heap, p->ptr, p->size);
zend_mm_free_heap(heap, p, NULL, 0, NULL, 0);
count++;
@@ -2430,6 +2444,9 @@ static void zend_mm_check_leaks(zend_mm_heap *heap)
}
heap->huge_list = list = list->next;
+ if (list) {
+ list->prev = NULL;
+ }
zend_mm_chunk_free(heap, q->ptr, q->size);
zend_mm_free_heap(heap, q, NULL, 0, NULL, 0);
}