[php-src] master: Detect immediate double-frees of zend_mm small slots
jvoisin via Arnaud Le Blanc <[email protected]>
| Newsgroups | gmane.comp.php.cvs.general |
|---|---|
| Message-ID | <[email protected]> |
Author: jvoisin (jvoisin)
Committer: Arnaud Le Blanc (arnaud-lb)
Date: 2026-08-27T13:25:07+02:00
Commit: https://github.com/php/php-src/commit/383b5bb7c579709fbb635fbea992b89307ec47cf
Raw diff: https://github.com/php/php-src/commit/383b5bb7c579709fbb635fbea992b89307ec47cf.diff
Detect immediate double-frees of zend_mm small slots
Freeing the same small pointer twice in a row pushed it onto the freelist
twice, so the next two allocations of that bin returned the same address.
That's a nifty primitive to obtain two live pointers of different
types to the same object. The shadow-pointer check does not catch it,
as both links are consistent.
This commit adds a simple check for when the freed pointer already is the head
of the freelist. heap->free_slot[bin_num] is loaded by the very next line, so
the check costs a single comparison on an already-hot value.
This only catches consecutive double-frees, not a free after other activity on
the same bin, but it doesn't cost ~anything performance wise, and catches real
bugs like error/cleanup paths freeing the same value twice. A quick look at `git log
--grep='double.free'` shows that this is a popular bug pattern.
Changed paths:
M Zend/zend_alloc.c
Diff:
diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c
index 575b54b11a24..02de1a543da9 100644
--- a/Zend/zend_alloc.c
+++ b/Zend/zend_alloc.c
@@ -1430,6 +1430,12 @@ static zend_always_inline void zend_mm_free_small(zend_mm_heap *heap, void *ptr,
#endif
p = (zend_mm_free_slot*)ptr;
+#if ZEND_MM_HEAP_PROTECTION
+ /* Catch the most common double-free pattern for free. */
+ if (UNEXPECTED(p == heap->free_slot[bin_num])) {
+ zend_mm_panic("zend_mm_heap corrupted (double free)");
+ }
+#endif
zend_mm_set_next_free_slot(heap, bin_num, p, heap->free_slot[bin_num]);
heap->free_slot[bin_num] = p;
}