[php-src] PR #23454: Fix zend_analyze_calls() call_stack buffer overrun

From: Date: Tue, 25 Aug 2026 14:09:41 +0000
Subject: [php-src] PR #23454: Fix zend_analyze_calls() call_stack buffer overrun
Groups: php.git-pulls 
Request: Send a blank email to git-pulls+get-38132@lists.php.net to get a copy of this message
Pull Request: https://github.com/php/php-src/pull/23454 Author: Mrmaxmeier Hi, we ran into an out-of-bounds write with the php-fuzz-function-jit fuzzing target: ```php <?php function test() { new A(new B(new C(new D(match ([]) { 1 => 2 })))); } ``` <details> <summary>ASAN backtrace for reproducer</summary> ``` /out/php-fuzz-function-jit: Running 1 inputs 100 time(s) each. Running: /testcase ================================================================= ==14==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7b62989e2e60 at pc 0x556927643788 bp 0x7ffca0c77cd0 sp 0x7ffca0c77cc8 WRITE of size 8 at 0x7b62989e2e60 thread T0 SCARINESS: 42 (8-byte-write-heap-buffer-overflow) #0 0x556927643787 in zend_analyze_calls /src/php-src/Zend/Optimizer/zend_call_graph.c:100:22 #1 0x5569276440fb in zend_analyze_call_graph /src/php-src/Zend/Optimizer/zend_call_graph.c:253:3 #2 0x5569276c28c2 in zend_optimize_script /src/php-src/Zend/Optimizer/zend_optimizer.c:1631:6 #3 0x556926e37b79 in cache_script_in_shared_memory /src/php-src/ext/opcache/ZendAccelerator.c:1598:2 #4 0x556926e39b6d in persistent_compile_file /src/php-src/ext/opcache/ZendAccelerator.c:2399:24 #5 0x556927d1538e in fuzzer_do_request_from_buffer /src/php-src/sapi/fuzzer/fuzzer-sapi.c:289:29 #6 0x556927d13954 in LLVMFuzzerTestOneInput /src/php-src/sapi/fuzzer/fuzzer-function-jit.c:32:2 [..] DEDUP_TOKEN: zend_analyze_calls--zend_analyze_call_graph--zend_optimize_script 0x7b62989e2e60 is located 0 bytes after 16-byte region [0x7b62989e2e50,0x7b62989e2e60) allocated by thread T0 here: #0 0x5569269a9df4 in malloc /src/llvm-project/compiler-rt/lib/asan/asan_malloc_linux.cpp:67:3 #1 0x5569276e0231 in tracked_malloc /src/php-src/Zend/zend_alloc.c:3016:14 #2 0x556927642767 in zend_analyze_calls /src/php-src/Zend/Optimizer/zend_call_graph.c:53:15 #3 0x5569276440fb in zend_analyze_call_graph /src/php-src/Zend/Optimizer/zend_call_graph.c:253:3 #4 0x5569276c28c2 in zend_optimize_script /src/php-src/Zend/Optimizer/zend_optimizer.c:1631:6 #5 0x556926e37b79 in cache_script_in_shared_memory /src/php-src/ext/opcache/ZendAccelerator.c:1598:2 #6 0x556926e39b6d in persistent_compile_file /src/php-src/ext/opcache/ZendAccelerator.c:2399:24 #7 0x556927d1538e in fuzzer_do_request_from_buffer /src/php-src/sapi/fuzzer/fuzzer-sapi.c:289:29 #8 0x556927d13954 in LLVMFuzzerTestOneInput /src/php-src/sapi/fuzzer/fuzzer-function-jit.c:32:2 [..] DEDUP_TOKEN: __interceptor_malloc--tracked_malloc--zend_analyze_calls SUMMARY: AddressSanitizer: heap-buffer-overflow /src/php-src/Zend/Optimizer/zend_call_graph.c:100:22 in zend_analyze_calls ``` (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.) </details> zend_analyze_calls() assumes that the call_stack is at most op_array->last / 2 deep: https://github.com/php/php-src/blob/5dcff379188117c965db4d2888a3ef8b20193e8e/Zend/Optimizer/zend_call_graph.c#L53 The implicit assumption here is that every call needs at least two opcodes, an INIT_* that pushes an entry and a DO_FCALL that pops it again. That assumption stops holding when the optimizer removes DO_FCALL opcodes as dead code. This PR allocates op_array->last entries for call_stack instead, 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._

« previous php.git-pulls (#38132) next »