Narrow the Windows VEH to undefined-function calls only#379
Merged
Conversation
realFlowControl
force-pushed
the
florian/narrow-windows-veh
branch
from
June 16, 2026 10:29
861ec62 to
4e7ac20
Compare
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.
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
force-pushed
the
florian/narrow-windows-veh
branch
from
June 16, 2026 11:01
4e7ac20 to
e14d8b4
Compare
realFlowControl
changed the base branch from
develop
to
florian/fix-future-monitor-uaf-race
June 16, 2026 11:01
realFlowControl
marked this pull request as ready for review
June 16, 2026 11:41
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.
Stacked on #381 (base =
florian/fix-future-monitor-uaf-race). Retarget todeveloponce #381 lands.Follow-up to #378, which narrowed the POSIX SIGSEGV handler. This applies the same policy to the Windows vectored exception handler (VEH).
What changed
The VEH introduced in #355 caught every access violation on a parallel worker and converted it into a catchable
IllegalInstructionvia an unconditionalzend_bailout(). Now it only does that for the one case it was built for: a worker faulting on aZEND_INIT_FCALLto a function missing from its function table (no bootstrap file). Any other access violation returnsEXCEPTION_CONTINUE_SEARCH, i.e. it is forwarded to the previously installed handler and crashes cleanly if nothing handles it — the Windows equivalent of chaining to the previousSIGSEGVhandler on POSIX.Detection is shared between both handlers via
php_parallel_scheduler_recover_missing_function().Why this is now safe
Narrowing the VEH previously unmasked a latent worker-thread teardown access violation in
tests/functional/007.phpt. That was a real use-after-free race onfuture->monitor, fixed in #381 — so with this stacked on top,007passes for the right reason instead of being masked by the catch-all.Testing
init_fcall_fix_001/002/003.