Fix block pass dropping a JMP across an unreachable_free block - #23538
Open
Mrmaxmeier wants to merge 1 commit into
Open
Fix block pass dropping a JMP across an unreachable_free block#23538Mrmaxmeier wants to merge 1 commit into
Mrmaxmeier wants to merge 1 commit into
Conversation
An unreachable block that frees a loop variable created in a reachable block is
marked ZEND_BB_UNREACHABLE_FREE and is still emitted, because the FREE opcodes
it contains determine where the live range of that variable ends. Two places
that decide whether a ZEND_JMP can be dropped because it targets the block that
physically follows skipped over every block that is not ZEND_BB_REACHABLE,
including those.
The jump was therefore removed even though an unreachable_free block still sat
between the two, and control fell straight into a FREE opcode that was never
meant to be executed:
function test($a, $b, $c) {
do {
if ($a) {
switch ($b[0]) {
case 'x':
switch ($c[0]) {
default:
return "returned";
}
default:
continue 2;
}
}
} while (false);
return $b[0];
}
Taking the "continue 2" path freed the switch subject twice, and the resulting
live range was inverted, which also tripped the start < end assertion in
emit_live_range_raw().
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 assertion failure with the
fuzzer-function-jitfuzzing target:(On release builds this might turn into a double-free or break program semantics)
Assertion and backtrace for reproducer
The block pass marks an unreachable block that frees a loop variable created in a reachable block as
ZEND_BB_UNREACHABLE_FREE, and still emits it: theFREEopcodes it contains determine where the live range of that variable ends.Two places in the block pass drop a
ZEND_JMPwhen it targets the block that physically follows it. Both walk forward over the block list and skip every block that is notZEND_BB_REACHABLE. That skips theZEND_BB_UNREACHABLE_FREEblocks as well, even though those do get emitted:So the pass removes the jump while an unreachable_free block still physically sits between the two, and control falls straight into a
FREEopcode that is never meant to run. In the reproducer, thecontinue 2path frees the switch subject twice and leaves an inverted live range behind.The fix teaches both walks that a non-empty
ZEND_BB_UNREACHABLE_FREEblock still separates a block from its successor, so the jump stays.Thanks!
Found by the CISPA Fandango team while triaging findings in oss-fuzz harnesses.