Skip to content

Randomize the layout of zend_mm small freelists - #23361

Closed
jvoisin wants to merge 1 commit into
php:masterfrom
jvoisin:randlist
Closed

Randomize the layout of zend_mm small freelists#23361
jvoisin wants to merge 1 commit into
php:masterfrom
jvoisin:randlist

Conversation

@jvoisin

@jvoisin jvoisin commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

zend_mm_alloc_small_slow() carved a fresh bin into a freelist ordered by ascending address and returned the first element, so the address of every small allocation was entirely determined by the allocation sequence: the n-th allocation of a given size class always landed at bin + n*slot_size, and two consecutive allocations were always adjacent.

That determinism is what makes heap feng-shui reliable. An attacker who can drive a few allocations of the right size class knows exactly where the next one lands, and can therefore place a victim object immediately after a buffer he can overflow, or reclaim a specific freed slot with an object of a chosen type.

Shuffle the slot order when a bin is created: hand out the first slot of the shuffled sequence and link the remaining ones in that order. This is the ~equivalent of Linux' SLAB_FREELIST_RANDOM.

Performance-wise, it:

  • Adds two scratch arrays of ZEND_MM_MAX_BIN_ELEMENTS entries (4KiB total) live on the stack of a non-recursive slow path.
  • Adds a per-bin-creation shuffleing, on the slow path.
  • Reduces spatial locality of allocations, but Zend/bench.php shows no measurable difference.

Can be compiled out with -DZEND_MM_FREELIST_RANDOM=0.

@jvoisin

jvoisin commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Part of #14083

Comment thread Zend/zend_alloc.c Outdated
zend_mm_alloc_small_slow() carved a fresh bin into a freelist ordered by
ascending address and returned the first element, so the address of every
small allocation was entirely determined by the allocation sequence: the
n-th allocation of a given size class always landed at bin + n*slot_size,
and two consecutive allocations were always adjacent.

That determinism is what makes heap feng-shui reliable. An attacker who
can drive a few allocations of the right size class knows exactly where
the next one lands, and can therefore place a victim object immediately
after a buffer he can overflow, or reclaim a specific freed slot with an
object of a chosen type.

Shuffle the slot order when a bin is created: hand out the first slot of
the shuffled sequence and link the remaining ones in that order. This is
the ~equivalent of Linux' SLAB_FREELIST_RANDOM.

Performance-wise, it:

- Adds two scratch arrays of ZEND_MM_MAX_BIN_ELEMENTS entries (4KiB total) live
  on the stack of a non-recursive slow path.
- Adds a per-bin-creation shuffleing, on the slow path.
- Reduces spatial locality of allocations, but Zend/bench.php shows no
  measurable difference.

Can be compiled out with -DZEND_MM_FREELIST_RANDOM=0.
@Sjord

Sjord commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Looks good to me.

@Sjord

Sjord commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

@ndossche Could you take a look at this?

@ndossche

ndossche commented Sep 2, 2026

Copy link
Copy Markdown
Member

I could, but won't. The foundation is paid to look into PRs, I'm not paid at all.

@Sjord

Sjord commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

@arnaud-lb Could you take a look at this PR?

@arnaud-lb

Copy link
Copy Markdown
Member

I'm hesitating on this one, for multiple reasons:

Overhead is not negligible: +0.7% on symfony and phpstan benchmarks.

It can make bugs more difficult to reproduce. We can disable it by default in debug builds, but this will affect bug reports anyway.

This can be bypassed in may cases. An attack that relies on overflowing to an adjacent bin can be performed by making every bin a valid victim so that their order doesn't matter. For larger bin sizes with only a few bins per page, an attack can be brute-forced.

@jvoisin

jvoisin commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Fair. I'll try to re-send something in this spirit if quanrantine is ever implemented

@jvoisin jvoisin closed this Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants