[php-src] PR #23376: Bind free list shadow pointers to the slot holding them

From: Date: Wed, 19 Aug 2026 11:43:58 +0000
Subject: [php-src] PR #23376: Bind free list shadow pointers to the slot holding them
Groups: php.git-pulls 
Request: Send a blank email to git-pulls+get-38005@lists.php.net to get a copy of this message
Pull Request: https://github.com/php/php-src/pull/23376 Author: jvoisin The shadow of a free list pointer is currently BSWAP(next) ^ shadow_key, which does not depend on where it is stored, meaning that: - zend_mm_free_small() encodes whatever heap->free_slot[bin] happens to be, including NULL when the bin has been drained, meaning the slot ends up holding shadow_key verbatim. - A (next, shadow) pair harvested from one free slot is valid in every other slot of every bin. An attacker who can read one free slot can therefore forge a link anywhere in the heap without needing the key. This commit adds the address of the slot that holds the shadow into the mix: shadow = BSWAP(next) ^ shadow_key ^ (uintptr_t)holder The holder term cancels on decode, so this is one extra xor on a register that is already live, with no branch. Encoding NULL now yields shadow_key ^ holder rather than the key, and a shadow only verifies in the slot it was written for. This was verified under GDB: Freeing into a drained bin used to store shadow_key exactly; it now stores shadow_key ^ holder (xoring the two back gives the slot address). Naïvely replaying a valid (next, shadow) pair from one slot into another and traversing from it is accepted before this change and aborts with "zend_mm_heap corrupted" after. Performance-wise, the impact is in the noise level, which is expected as it more or less adds a single xor instruction per zend_mm_set_next_free_slot(). This commit is a follow up on 25360ef2495 and c561f7da858.

« previous php.git-pulls (#38005) next »