Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add runtime-enabled heap debugging capabilities #18172

Merged
merged 4 commits into from
Mar 31, 2025

Conversation

arnaud-lb
Copy link
Member

@arnaud-lb arnaud-lb commented Mar 28, 2025

Debugging memory corruption issues in production can be difficult when it's not possible to use a debug build or ASAN/MSAN/Valgrind (e.g. for performance reasons).

This PR makes it possible to enable some basic heap debugging helpers without rebuilding PHP. This is controlled by the environment variable ZEND_MM_DEBUG. The env var takes a comma-separated list of parameters:

  • poison_free=byte: Override freed blocks with the specified byte value (represented as a number)
  • poison_alloc=byte: Override newly allocated blocks with the specified byte value (represented as a number)
  • padding=bytes: Pad allocated blocks with the specified amount of bytes (if non-zero, a value >= 16 is recommended to not break alignments)
  • check_freelists_on_shutdown=0|1: Enable checking freelist consistency on shutdown

Example: ZEND_MM_DEBUG=poison_free=0xbe,poison_alloc=0xeb,padding=16,check_freelists_on_shutdown=1 php ....

This is based on an idea of @TimWolla.

Implementation

This is implemented by installing custom handlers when ZEND_MM_DEBUG is set.

Overhead

This has zero overhead when ZEND_MM_DEBUG is not set. When ZEND_MM_DEBUG is set, the overhead is about 8.5% on the Symfony Demo benchmark.

Goals:

  • Crash earlier after a memory corruption, to extract a useful backtrace
  • Be usable in production with reasonable overhead
  • Having zero overhead when not enabled

Non-goals:

  • Replace debug builds, valgrind, ASAN, MSAN or other sanitizers

@arnaud-lb arnaud-lb requested a review from TimWolla March 28, 2025 13:54
@arnaud-lb arnaud-lb changed the title Add optional heap poisoning and sanity checks in non-debug builds Add heap runtime-enabled heap debugging capabilities Mar 28, 2025
@arnaud-lb arnaud-lb changed the title Add heap runtime-enabled heap debugging capabilities Add runtime-enabled heap debugging capabilities Mar 28, 2025
Copy link
Member

@TimWolla TimWolla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! I can confirm that the padding option is particularly useful in catching refcounting bugs, since it will make the:

ZEND_ASSERT(p->refcount > 0);

in zend_gc_delref() functional. Without the padding, the refcount would be overwritten by ZendMM metadata on free, thus preventing the assertion from ever triggering.

The check_freelists_on_shutdown option would also detect this on shutdown, but that would happen much later.


I've got a few comments inline, but overall this looks very good and helpful in catching some of my past production bugs.

Co-authored-by: Tim Düsterhus <[email protected]>
@arnaud-lb arnaud-lb marked this pull request as ready for review March 31, 2025 12:08
Copy link
Member

@dstogov dstogov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as this doesn't affect release build performance, I don't see any problems.

@arnaud-lb arnaud-lb merged commit cb24541 into php:master Mar 31, 2025
9 checks passed
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.

3 participants