From 8f5b0cd855761175e0d3cb934446a65a97b78301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Wer=C5=82os?= Date: Sat, 15 Aug 2026 15:04:20 +0200 Subject: [PATCH 1/2] Add test --- Zend/tests/stack_limit/stack_limit_016.phpt | 24 +++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Zend/tests/stack_limit/stack_limit_016.phpt diff --git a/Zend/tests/stack_limit/stack_limit_016.phpt b/Zend/tests/stack_limit/stack_limit_016.phpt new file mode 100644 index 000000000000..14567910cb36 --- /dev/null +++ b/Zend/tests/stack_limit/stack_limit_016.phpt @@ -0,0 +1,24 @@ +--TEST-- +Stack limit 016 - Compilation of nested expressions uses little stack per nesting level +--SKIPIF-- + +--INI-- +zend.max_allowed_stack_size=1M +--FILE-- + +--EXPECT-- +int(4001) From 3f7ca4cc30bab35441b7492f352ea1161297883f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Wer=C5=82os?= Date: Sat, 15 Aug 2026 16:20:27 +0200 Subject: [PATCH 2/2] Prevent inlining of `zend_compile_expr_inner()` to guarantee low stack usage of compilation --- Zend/tests/stack_limit/stack_limit_016.phpt | 24 --------------------- Zend/zend_compile.c | 4 +++- 2 files changed, 3 insertions(+), 25 deletions(-) delete mode 100644 Zend/tests/stack_limit/stack_limit_016.phpt diff --git a/Zend/tests/stack_limit/stack_limit_016.phpt b/Zend/tests/stack_limit/stack_limit_016.phpt deleted file mode 100644 index 14567910cb36..000000000000 --- a/Zend/tests/stack_limit/stack_limit_016.phpt +++ /dev/null @@ -1,24 +0,0 @@ ---TEST-- -Stack limit 016 - Compilation of nested expressions uses little stack per nesting level ---SKIPIF-- - ---INI-- -zend.max_allowed_stack_size=1M ---FILE-- - ---EXPECT-- -int(4001) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index c7ac93f8d34e..58ae0aa55a64 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -12439,7 +12439,9 @@ static void zend_compile_stmt(zend_ast *ast) /* {{{ */ } /* }}} */ -static void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */ +/* Keep this out of zend_compile_expr(): the two form a recursion cycle, so inlining merges this + * frame into every nesting level of an expression, tripling the stack needed to compile it. */ +static zend_never_inline void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */ { /* CG(zend_lineno) = ast->lineno; */ CG(zend_lineno) = zend_ast_get_lineno(ast);