Narrow crash handler to undefined-function calls only#378
Merged
realFlowControl merged 1 commit intoJun 16, 2026
Merged
Conversation
The SIGSEGV/VEH handler added in #355 caught *every* fault on a parallel worker thread and converted it into a catchable IllegalInstruction error via zend_bailout(). This masked genuine bugs (no backtrace, no core dump) and longjmp-ing out of a signal handler is not async-signal-safe. Restrict the conversion to the single case it was built for: a worker faulting on a ZEND_INIT_FCALL to a function that is not in the worker's function table (i.e. not provided via a bootstrap file). Any other fault now falls through to the previous handler so it crashes properly with a backtrace / core dump. Detection is factored into php_parallel_scheduler_recover_missing_function() shared by both the POSIX and Windows handlers.
realFlowControl
force-pushed
the
florian/narrow-crash-handler-to-undefined-functions
branch
from
June 16, 2026 08:48
285f6e9 to
41b8bfc
Compare
realFlowControl
marked this pull request as ready for review
June 16, 2026 08:56
realFlowControl
deleted the
florian/narrow-crash-handler-to-undefined-functions
branch
June 16, 2026 09:17
realFlowControl
added a commit
that referenced
this pull request
Jun 16, 2026
Apply the same policy the POSIX SIGSEGV handler already has (#378) to the Windows vectored exception handler: only convert an access violation into a catchable IllegalInstruction error when a parallel worker faulted on a call to a function missing from its function table. Every other access violation is forwarded to the previously installed handler via EXCEPTION_CONTINUE_SEARCH (and crashes cleanly if nothing handles it) instead of being masked by an unconditional zend_bailout(). The missing-function detection is shared between both handlers via php_parallel_scheduler_recover_missing_function().
realFlowControl
added a commit
that referenced
this pull request
Jun 16, 2026
Apply the same policy the POSIX SIGSEGV handler already has (#378) to the Windows vectored exception handler: only convert an access violation into a catchable IllegalInstruction error when a parallel worker faulted on a call to a function missing from its function table. Every other access violation is forwarded to the previously installed handler via EXCEPTION_CONTINUE_SEARCH (and crashes cleanly if nothing handles it) instead of being masked by an unconditional zend_bailout(). The missing-function detection is shared between both handlers via php_parallel_scheduler_recover_missing_function().
realFlowControl
added a commit
that referenced
this pull request
Jun 16, 2026
* Fix use-after-free race on future->monitor in error paths When a task threw (normal exception) or crashed (IllegalInstruction), the worker signalled the future twice: first PHP_PARALLEL_ERROR (or READY|ERROR), then an unconditional trailing PHP_PARALLEL_READY at the end of php_parallel_scheduler_run(). Future::value() waits on READY|FAILURE|ERROR, so the first signal already releases the consumer. The consumer can then tear the future down (freeing future->monitor) before the worker runs the trailing php_parallel_monitor_set(future->monitor, PHP_PARALLEL_READY), which then dereferences a dangling monitor inside pthread_cond_signal -> access violation / use-after-free. This was observed as a hard crash on Windows (0xC0000005) in tests/functional/007.phpt, masked until now by the catch-all VEH; on Linux the freed memory happened to stay readable. Fix: the error paths now signal READY|ERROR exactly once and skip the trailing READY (via a 'ready' flag). The success and kill paths are unchanged - they have no early wake and still rely on the trailing READY. Setting READY in the error path also ensures the future destructor (which waits for READY) never blocks when value() is not called. * Narrow the Windows VEH to undefined-function calls only Apply the same policy the POSIX SIGSEGV handler already has (#378) to the Windows vectored exception handler: only convert an access violation into a catchable IllegalInstruction error when a parallel worker faulted on a call to a function missing from its function table. Every other access violation is forwarded to the previously installed handler via EXCEPTION_CONTINUE_SEARCH (and crashes cleanly if nothing handles it) instead of being masked by an unconditional zend_bailout(). The missing-function detection is shared between both handlers via php_parallel_scheduler_recover_missing_function().
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #355.
On POSIX, the SIGSEGV handler introduced in #355 caught every fault on a parallel worker thread and converted it into a catchable
IllegalInstructionerror viazend_bailout(). That was too broad:zend_bailout()is alongjmp, and longjmp-ing out of a signal handler is not async-signal-safeThis PR restricts the POSIX conversion to the single case the handler was built for: a worker faulting on a
ZEND_INIT_FCALLto a function that is not in the worker's function table (i.e. not provided via a bootstrap file). Any other fault now falls through to the previously installed handler, so it crashes properly with a backtrace / core dump (e.g. under ASAN/UBSAN or gdb).Detection lives in
php_parallel_scheduler_recover_missing_function().Windows is intentionally left unchanged
The Windows VEH is a process-wide, first-chance handler; #355 used it as a catch-all for every access violation on a worker. Narrowing it the same way unmasked a latent shutdown access violation in
functional/007(bootstrap set inside a task) that kills the process. That is a separate, Windows-only issue requiring a Windows repro to fix properly, so the VEH keeps its original catch-all behavior here.