Skip to content

Fix GH-21974: Avoid destroying shared functions in ZTS - #24425

Open
Sree24-ui wants to merge 1 commit into
php:PHP-8.4from
Sree24-ui:fix-gh21974-zts-disable-functions
Open

Sree24-ui wants to merge 1 commit into
php:PHP-8.4from
Sree24-ui:fix-gh21974-zts-disable-functions

Conversation

@Sree24-ui

Copy link
Copy Markdown

Fixes GH-21974.
Problem
In ZTS builds, a function disabled through an FPM pool's php_admin_value[disable_functions] setting can cause a use-after-free or crash during worker shutdown.
After startup, a thread's function table contains borrowed pointers to functions owned by the global function table. Deleting a borrowed function through the normal destructor path can free an object that the global table still references.
Fix
When zend_disable_function() identifies a function in the borrowed-function prefix of the ZTS function table, it removes the entry without invoking its destructor, rehashes the table to preserve the borrowed-prefix invariant, and updates CG(copied_functions_count). The existing deletion path remains unchanged for other entries and for non-ZTS builds.
Testing

  • Added an FPM regression test that disables system in a pool and checks that the worker exits cleanly after its first request.
  • On PHP-8.4 HEAD, the new test failed on unpatched release and ASan builds and passed on patched builds.
  • The patched release FPM suite reported 110 passed, 27 skipped, and 0 failed.
  • Repeated patched regression-test runs and a custom multithreaded embed-SAPI harness passed.
    The current validation used macOS builds. Linux/glibc, Windows, PHP-8.3, and a real multithreaded web-server SAPI have not been tested.
    Scope
    This change addresses the reported worker-shutdown issue. It does not attempt to address separate problems associated with calling zend_disable_function() during an active request.

@Sree24-ui

Copy link
Copy Markdown
Author

I investigated the failing LINUX_X64_ASAN_DEBUG_ZTS check further.
In a Linux ARM64 ASan+ZTS reproduction, the unpatched build reports the original heap-use-after-free in zend_function_dtor. The patched build instead exits with code 1 due to a LeakSanitizer report originating in fpm_env_init_main(2,452 bytes across 31 allocations). The same leak occurs with and without disable_functions.
With ASAN_OPTIONS=detect_leaks=0, the patched test passes 3/3 runs, while the unpatched build still fails 3/3 with the original use-after-free.
These results support that the patch addresses the intended regression, while the test also exposes an FPM startup leak during graceful worker shutdown. The reproduction is on Linux ARM64, so I have not yet confirmed the exact cause in the x86-64 CI environment.
I have not changed the test, suppression file, or PR. Would you prefer a targeted test-side LeakSanitizer configuration or a separate entry in .github/lsan-suppressions.txt? I can implement and validate the preferred approach.

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.

1 participant