Fix zend_analyze_calls() call_stack buffer overrun - #23454
Open
Mrmaxmeier wants to merge 1 commit into
Open
Conversation
The call stack was sized as op_array->last / 2, on the assumption that every
call needs at least an INIT and a DO_FCALL opcode. That assumption does not
hold after the optimizer has removed the DO_FCALL opcodes as dead code, in
which case nothing pops the stack again:
function test() {
new A(new B(new C(new D(match ([]) { 1 => 2 }))));
}
The match arm never matches, so everything behind the ZEND_MATCH_ERROR is
removed and the optimized op_array is just four ZEND_NEWs followed by the
ZEND_MATCH_ERROR. The buffer then holds two entries while four are pushed.
Size the stack by op_array->last instead, which is the only safe upper bound
once the pushes and pops are no longer guaranteed to be balanced.
Assisted-By: Claude Opus 5 <noreply@anthropic.com>
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.
Hi,
we ran into an out-of-bounds write with the
php-fuzz-function-jitfuzzing target:ASAN backtrace for reproducer
(Note: The buffer is a
do_alloca(), so this can either be a stack or heap buffer overflow depending on allocator behaviour. In this setup, ASAN reports it as a heap overflow.)zend_analyze_calls()assumes that thecall_stackis at mostop_array->last / 2deep:php-src/Zend/Optimizer/zend_call_graph.c
Line 53 in 5dcff37
The implicit assumption here is that every call needs at least two opcodes, an
INIT_*that pushes an entry and aDO_FCALLthat pops it again. That assumption stops holding when the optimizer removesDO_FCALLopcodes as dead code.This PR allocates
op_array->lastentries forcall_stackinstead, assuming that each opcode pushes at most one call stack entry.Thanks!
Found by the CISPA Fandango team while triaging findings in oss-fuzz harnesses.