From 21204bf59a2f95c395470c402d077880c8f11cca Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 7 Jul 2026 11:01:19 +0200 Subject: [PATCH 1/4] Fix performance of gh22443.phpt These millions of iterations don't seem to be necessary to trigger the original bug. --- ext/opcache/tests/jit/gh22443.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opcache/tests/jit/gh22443.phpt b/ext/opcache/tests/jit/gh22443.phpt index 869329b79b5c..4baa3f99fc61 100644 --- a/ext/opcache/tests/jit/gh22443.phpt +++ b/ext/opcache/tests/jit/gh22443.phpt @@ -45,7 +45,7 @@ for ($k = 0; $k < 8; $k++) { $cold = [makeListener([$svc, 'coldMethod'])]; $s = 0; -for ($i = 0; $i < 4000000; $i++) { +for ($i = 0; $i < 400; $i++) { $s += invokeListeners($warm, 'e', [$i & 7, ($i >> 2) & 7]); } for ($j = 0; $j < 5; $j++) { From ba32d70130533736bcad5241b62a0772a459aa4a Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 7 Jul 2026 12:55:06 +0200 Subject: [PATCH 2/4] [skip ci] Add -DZEND_VERIFY_TYPE_INFERENCE to CXXFLAGS for variation build Without this, executor globals vary in size and offsets for C vs. C++, which leads to issues since GH-22287. --- .github/workflows/test-suite.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml index 4c28ccca3561..e8f698e4e091 100644 --- a/.github/workflows/test-suite.yml +++ b/.github/workflows/test-suite.yml @@ -166,7 +166,7 @@ jobs: with: configurationParameters: >- ${{ matrix.asan && 'CFLAGS="-fsanitize=undefined,address -DZEND_TRACK_ARENA_ALLOC" LDFLAGS="-fsanitize=undefined,address"' || '' }} - ${{ matrix.variation && 'CFLAGS="-DZEND_RC_DEBUG=1 -DPROFITABILITY_CHECKS=0 -DZEND_VERIFY_FUNC_INFO=1 -DZEND_VERIFY_TYPE_INFERENCE"' || '' }} + ${{ matrix.variation && 'CFLAGS="-DZEND_RC_DEBUG=1 -DPROFITABILITY_CHECKS=0 -DZEND_VERIFY_FUNC_INFO=1 -DZEND_VERIFY_TYPE_INFERENCE" CXXFLAGS="-DZEND_VERIFY_TYPE_INFERENCE"' || '' }} ${{ (matrix.variation && fromJson(inputs.branch).jobs.LINUX_X64.config.variation_enable_zend_max_execution_timers) && '--enable-zend-max-execution-timers' || '' }} --${{ matrix.debug && 'enable' || 'disable' }}-debug ${{ matrix.debug && 'CXXFLAGS="-D_GLIBCXX_ASSERTIONS"' || '' }} From 53b80f07f0f4dc1ebdb2cc710bbf8b83e799616c Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Tue, 7 Jul 2026 08:13:14 -0400 Subject: [PATCH 3/4] Check the stack limit when calling internal functions (#22545) zend_call_function invokes an internal callee's handler directly, with no VM frame and without the stack-limit check the interpreter runs at its call opcodes. An internal function that recurses through zend_call_function, such as a self- or mutually-attached SPL iterator, never yields back to the VM, so nothing bounds the recursion and the C stack overflows into a SEGV. Check zend_call_stack_overflowed() before pushing the call frame and raise the usual "Maximum call stack size reached" error on overflow. Running the check ahead of the frame setup keeps the error path free of teardown. Fixes GH-15672 Fixes GH-15911 --- Zend/zend_execute_API.c | 8 ++++++++ ext/spl/tests/gh15672.phpt | 22 ++++++++++++++++++++++ ext/spl/tests/gh15911.phpt | 21 +++++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 ext/spl/tests/gh15672.phpt create mode 100644 ext/spl/tests/gh15911.phpt diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 71e0c56a51c8..cb48e6234371 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -861,6 +861,14 @@ zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_ } } +#ifdef ZEND_CHECK_STACK_LIMIT + if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) { + zend_call_stack_size_error(); + zend_release_fcall_info_cache(fci_cache); + return SUCCESS; + } +#endif + call = zend_vm_stack_push_call_frame(call_info, func, fci->param_count, object_or_called_scope); uint32_t consumed_args = fci->param_count ? fci->consumed_args : 0; diff --git a/ext/spl/tests/gh15672.phpt b/ext/spl/tests/gh15672.phpt new file mode 100644 index 000000000000..ff3feda46f7e --- /dev/null +++ b/ext/spl/tests/gh15672.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-15672 (MultipleIterator attached to itself overflows the C stack) +--SKIPIF-- + +--EXTENSIONS-- +zend_test +--INI-- +memory_limit=2G +zend.max_allowed_stack_size=512K +--FILE-- +attachIterator(new ArrayIterator([1, 2, 3]), "1"); +$m->attachIterator($m, 3); +foreach ($m as $key => $value) { +} +?> +--EXPECTREGEX-- +.*Maximum call stack size of \d+ bytes.*Infinite recursion\?.*thrown in .* on line \d+ diff --git a/ext/spl/tests/gh15911.phpt b/ext/spl/tests/gh15911.phpt new file mode 100644 index 000000000000..502b2baf616b --- /dev/null +++ b/ext/spl/tests/gh15911.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-15911 (AppendIterator appended to itself overflows the C stack) +--SKIPIF-- + +--EXTENSIONS-- +zend_test +--INI-- +memory_limit=2G +zend.max_allowed_stack_size=512K +--FILE-- +append($it); +foreach ($it as $v) { +} +?> +--EXPECTREGEX-- +.*Maximum call stack size of \d+ bytes.*Infinite recursion\?.*thrown in .* on line \d+ From 4afc970827a038e380551cc97e9a14493cae0854 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Tue, 7 Jul 2026 08:31:42 -0400 Subject: [PATCH 4/4] [skip ci] Add NEWS entry for GH-15672 and GH-15911 --- NEWS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS b/NEWS index 705ff85b2a6e..11c9d1db1af8 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,9 @@ PHP NEWS . Sync Boost.Context assembly with 1.91.0. (kn1g78) . Fixed bug GH-22387 (AST pretty-printing drops meaningful parentheses around RHS of instanceof). (timwolla) + . Fixed bug GH-15672 and GH-15911 (Stack overflow when an internal function + recurses through zend_call_function, such as a self-attached SPL + iterator). (iliaal) - DBA: . Fixed OOB read on malformed length field in dba flatfile handler. (alhudz)