[GIT-PULLS] [php-src] PR #23376: Bind free list shadow pointers to the slot holding them
[email protected] (jvoisin)
| Newsgroups | php.git-pulls |
|---|---|
| Message-ID | <[email protected]> |
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.