Skip to content

Commit 324e0c7

Browse files
committed
Fix GH-14361: Deep recursion in zend_cfg.c causes segfault instead of error
Use the same stack limit check already used elsewhere in compilation.
1 parent c87f29f commit 324e0c7

File tree

4 files changed

+52
-8
lines changed

4 files changed

+52
-8
lines changed

Zend/Optimizer/zend_cfg.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_bloc
2828
{
2929
zend_basic_block *blocks = cfg->blocks;
3030

31+
zend_check_stack_limit("Try reducing function size");
32+
3133
while (1) {
3234
int i;
3335

Zend/zend_compile.c

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,25 +100,32 @@ static void zend_compile_stmt(zend_ast *ast);
100100
static void zend_compile_assign(znode *result, zend_ast *ast);
101101

102102
#ifdef ZEND_CHECK_STACK_LIMIT
103-
zend_never_inline static void zend_stack_limit_error(void)
103+
zend_never_inline static void zend_stack_limit_error(const char *suggestion)
104104
{
105105
zend_error_noreturn(E_COMPILE_ERROR,
106-
"Maximum call stack size of %zu bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached during compilation. Try splitting expression",
107-
(size_t) ((uintptr_t) EG(stack_base) - (uintptr_t) EG(stack_limit)));
106+
"Maximum call stack size of %zu bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached during compilation. %s",
107+
(size_t) ((uintptr_t) EG(stack_base) - (uintptr_t) EG(stack_limit)),
108+
suggestion);
108109
}
109110

110-
static void zend_check_stack_limit(void)
111+
void zend_check_stack_limit(const char *suggestion)
111112
{
112113
if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
113-
zend_stack_limit_error();
114+
zend_stack_limit_error(suggestion);
114115
}
115116
}
116117
#else /* ZEND_CHECK_STACK_LIMIT */
117-
static void zend_check_stack_limit(void)
118+
void zend_check_stack_limit(const char *suggestion)
118119
{
120+
ZEND_IGNORE_VALUE(suggestion);
119121
}
120122
#endif /* ZEND_CHECK_STACK_LIMIT */
121123

124+
static void zend_check_stack_limit_expression(void)
125+
{
126+
zend_check_stack_limit("Try splitting expression");
127+
}
128+
122129
static void init_op(zend_op *op)
123130
{
124131
MAKE_NOP(op);
@@ -10617,7 +10624,7 @@ static void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */
1061710624

1061810625
static void zend_compile_expr(znode *result, zend_ast *ast)
1061910626
{
10620-
zend_check_stack_limit();
10627+
zend_check_stack_limit_expression();
1062110628

1062210629
uint32_t checkpoint = zend_short_circuiting_checkpoint();
1062310630
zend_compile_expr_inner(result, ast);
@@ -10715,7 +10722,7 @@ static void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
1071510722
return;
1071610723
}
1071710724

10718-
zend_check_stack_limit();
10725+
zend_check_stack_limit_expression();
1071910726

1072010727
switch (ast->kind) {
1072110728
case ZEND_AST_BINARY_OP:

Zend/zend_compile.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,8 @@ ZEND_API size_t zend_get_scanned_file_offset(void);
800800

801801
ZEND_API zend_string *zend_get_compiled_variable_name(const zend_op_array *op_array, uint32_t var);
802802

803+
void zend_check_stack_limit(const char *suggestion);
804+
803805
#ifdef ZTS
804806
const char *zend_get_zendtext(void);
805807
int zend_get_zendleng(void);

ext/opcache/tests/jit/gh14361.phpt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
GH-14361 (Deep recursion in zend_cfg.c causes segfault instead of error)
3+
--SKIPIF--
4+
<?php
5+
if (!function_exists('zend_test_zend_call_stack_get')) die("skip zend_test_zend_call_stack_get() is not available");
6+
?>
7+
--EXTENSIONS--
8+
zend_test
9+
opcache
10+
--INI--
11+
zend.max_allowed_stack_size=64K
12+
opcache.enable=1
13+
opcache.enable_cli=1
14+
opcache.jit=tracing
15+
opcache.optimization_level=0x000000a0
16+
--FILE--
17+
<?php
18+
$tmp = fopen(__DIR__."/gh14361_tmp.php", "w");
19+
fwrite($tmp, '<?php ');
20+
for ($i = 0; $i < 10000; $i++) {
21+
fwrite($tmp, 'if (@((0) == (0)) !== (true)) { $f++; }');
22+
}
23+
fclose($tmp);
24+
25+
include __DIR__."/gh14361_tmp.php";
26+
?>
27+
--CLEAN--
28+
<?php
29+
@unlink(__DIR__."/gh14361_tmp.php");
30+
?>
31+
--EXPECTF--
32+
Fatal error: Maximum call stack size of 32768 bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached during compilation. Try reducing function size in %s on line %d
33+
%a

0 commit comments

Comments
 (0)