Repository navigation
Conversation
SOAP_CLIENT_END_CODE() catches the bailout, frees the VM stack pages that were pushed during the call and then restores the saved stack top. Since 0390cde (2014) the live stack top is EG(vm_stack_top), but the macro still writes the saved value to EG(vm_stack)->top, which only matters for a page that is not the current one. EG(vm_stack_top) keeps pointing to where the stack was when the bailout happened: into a page that has just been freed if the call grew the stack beyond the current page. The next call frame release then trips the assertion in zend_vm_stack_free_call_frame_ex() on debug builds. Restore EG(vm_stack_top) instead, the same way zend_vm_stack_free_call_frame_ex() does when it pops a page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
likemusic
force-pushed
the
fix-soap-vm-stack-top
branch
from
October 15, 2026 20:06
cd33509 to
691ebad
Compare
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.
SOAP_CLIENT_END_CODE()catches a bailout raised during a SoapClient call, frees the VM stack pages pushed during the call and restores the saved stack top. Since 0390cde (2014), the live stack top isEG(vm_stack_top), but the macro still writes the saved value toEG(vm_stack)->top. That field only matters for a page that is not the current one. SoEG(vm_stack_top)keeps pointing to where the stack was when the bailout happened. If the call grew the stack beyond the current page, that is inside a page that has just been freed.This patch restores
EG(vm_stack_top)instead, the same wayzend_vm_stack_free_call_frame_ex()does when it pops a page.Reproducer
The test included in this PR triggers it.
__doRequest()recurses 50 000 levels deep, then raises a fatal error that the client turns into aSoapFault(eval("class Dup {} class Dup {}"), no memory exhaustion involved). After thecatch, ordinary function calls continue.On a debug build, without the fix:
The same assertion fires on master (
zend_execute.h:445).With the fix, the test passes on PHP-8.4 and on master.
Scope
I could only observe this on debug builds. A release build with ASAN (with and without
USE_ZEND_ALLOC=0) stays silent on the same script: the nextzend_vm_stack_free_call_frame_ex()takes the page-pop path and overwrites the stale pointer before anything reads through it. So I am not claiming a user-visible crash on release builds. The fix is to make the restore do what it was written to do.Testing
ext/soap+Zend/testson PHP-8.4, debug build: 5345 passed, 0 failed, 1 expected fail (interface_constructor_prototype_002.phpt), 246 skipped.ext/soapon master with this commit cherry-picked, debug build: 517 passed, 0 failed, 109 skipped. The cherry-pick conflicts only inNEWS.Found while auditing the
zend_catchsites that continue execution after a bailout. The audit started from #10026.🤖 Generated with Claude Code