Skip to content

Zend: Add opt-in request-scoped allocation no-reuse mode - #24473

Open
jvoisin wants to merge 2 commits into
php:masterfrom
jvoisin:noreuse
Open

jvoisin wants to merge 2 commits into
php:masterfrom
jvoisin:noreuse

Conversation

@jvoisin

@jvoisin jvoisin commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Enable with ZEND_MM_NO_REUSE=1 before engine startup.

Clear and retain freed small allocations, page runs, and huge mappings until heap shutdown. Move reallocations to fresh storage and prevent allocator GC from reclaiming retired ranges.

Keep retained mappings charged against memory_limit and reject incompatible allocator overrides rather than silently allowing reuse.

This mitigates UAF exploitation through allocation reclamation like CVE-2026-109618 and CVE-2026-109619, at the cost of memory usage approaching cumulative allocation volume per request.

Add tests for address non-overlap, clearing, reallocations, shutdown, accounting, and configuration errors. Document usage and limitations.

This provides a nice alternative to #14304.

@jvoisin

jvoisin commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

cc @arnaud-lb

Enable with ZEND_MM_NO_REUSE=1 before engine startup.

Clear and retain freed small allocations, page runs, and huge mappings
until heap shutdown. Move reallocations to fresh storage and prevent
allocator GC from reclaiming retired ranges.

Keep retained mappings charged against memory_limit and reject
incompatible allocator overrides rather than silently allowing reuse.

This mitigates UAF exploitation through allocation reclamation like
CVE-2026-109618 and CVE-2026-109619, at the cost of memory usage approaching
cumulative allocation volume per request.

Add tests for address non-overlap, clearing, reallocations, shutdown,
accounting, and configuration errors. Document usage and limitations.

This provides a nice alternative to php#14304.
@arnaud-lb

Copy link
Copy Markdown
Member

I measured a 4x memory usage increase on Symfony demo, and +50% wall time. The overhead is huge, but not as much as I anticipated.

This makes sense as an opt-in protection, but I fear that it falls behind in maintenance as it will rarely be used.

@jvoisin

jvoisin commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

The implementation is pretty simple/self-contained, and I added a bunch of tests. But I agree that it's a valid concern. I think™ it might/will be use nonetheless either by the paranoid folks around, or as an emergency mitigation for when the next RCE/UAF drops.

Use MADV_DONTNEED for complete OS pages in retired large runs and huge
mappings. This releases resident memory while keeping virtual addresses
reserved, preserving the no-reuse guarantee and memory-limit accounting.

Continue clearing partial pages and fall back to memset() when madvise()
is unavailable or fails.

On Linux/aarch64 with 16 KiB pages, 40 alternating benchmark runs showed:

- 256 x 1 MiB allocations: RSS fell from 275,200 KiB to 17,120 KiB
  (-94%); median time changed from 31.156 ms to 30.262 ms.
- 32 x 8 MiB allocations: RSS fell from 271,632 KiB to 8,944 KiB
  (-97%); median time changed from 30.997 ms to 29.545 ms.
- 1,000,000 x 64-byte allocations were unaffected: RSS remained about
  104 MiB and median time changed from 49.878 ms to 49.973 ms.

Reserved memory reported by memory_get_usage(true) remained unchanged.
@jvoisin

jvoisin commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

I added another commit using MADV_DONTNEED based on advice from @thestinger, it significantly reduce the memory impact.

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.

2 participants