Skip to content

Commit 443aa29

Browse files
committed
Support stack limit in phpdbg SAPI
Fixes GH-16041 Closes GH-16055
1 parent e02e6be commit 443aa29

File tree

5 files changed

+83
-1
lines changed

5 files changed

+83
-1
lines changed
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
GH-16041 001: Stack overflow in phpdbg
3+
--SKIPIF--
4+
<?php
5+
if (ini_get('zend.max_allowed_stack_size') === false) {
6+
die('skip No stack limit support');
7+
}
8+
?>
9+
--INI--
10+
zend.max_allowed_stack_size=512K
11+
--PHPDBG--
12+
set pagination off
13+
run
14+
continue
15+
quit
16+
--FILE--
17+
<?php
18+
19+
class Canary {
20+
public function __destruct() {
21+
new Canary();
22+
}
23+
}
24+
25+
new Canary();
26+
27+
?>
28+
--EXPECTF--
29+
[Successful compilation of %sgh16041_001.php]
30+
prompt> prompt> [Uncaught Error in %s on line %d: Maximum call stack size of %d bytes%s
31+
>00005: new Canary();
32+
00006: }
33+
00007: }
34+
prompt> [Uncaught Error in %s on line %d]
35+
Error: Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion? in %s:%d
36+
Stack trace:
37+
#0 %s(%d): Canary->__destruct()
38+
%a
39+
prompt>
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
GH-16041 002: Stack overflow in phpdbg
3+
--SKIPIF--
4+
<?php
5+
if (ini_get('zend.max_allowed_stack_size') === false) {
6+
die('skip No stack limit support');
7+
}
8+
?>
9+
--INI--
10+
zend.max_allowed_stack_size=512K
11+
--PHPDBG--
12+
set pagination off
13+
run
14+
quit
15+
--FILE--
16+
<?php
17+
18+
function map() {
19+
array_map('map', [1]);
20+
}
21+
22+
try {
23+
map();
24+
} catch (\Throwable $e) {
25+
printf("%s: %s\n", $e::class, $e->getMessage());
26+
}
27+
28+
?>
29+
--EXPECTF--
30+
[Successful compilation of %sgh16041_002.php]
31+
prompt> prompt> Error: Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?
32+
[Script ended normally]
33+
prompt>

Zend/zend_execute.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -2500,7 +2500,7 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_use_new_element_for_s
25002500
}
25012501

25022502
#ifdef ZEND_CHECK_STACK_LIMIT
2503-
static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_call_stack_size_error(void)
2503+
zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_call_stack_size_error(void)
25042504
{
25052505
size_t max_stack_size = 0;
25062506
if ((uintptr_t) EG(stack_base) > (uintptr_t) EG(stack_limit)) {

Zend/zend_execute.h

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_class_constant(const zend_
6666
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_false_to_array_deprecated(void);
6767
ZEND_COLD void ZEND_FASTCALL zend_param_must_be_ref(const zend_function *func, uint32_t arg_num);
6868
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_use_resource_as_offset(const zval *dim);
69+
zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_call_stack_size_error(void);
6970

7071
ZEND_API bool ZEND_FASTCALL zend_verify_ref_assignable_zval(zend_reference *ref, zval *zv, bool strict);
7172

sapi/phpdbg/phpdbg_prompt.c

+9
Original file line numberDiff line numberDiff line change
@@ -1652,6 +1652,15 @@ void phpdbg_execute_ex(zend_execute_data *execute_data) /* {{{ */
16521652

16531653
PHPDBG_G(in_execution) = 1;
16541654

1655+
#ifdef ZEND_CHECK_STACK_LIMIT
1656+
if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
1657+
zend_call_stack_size_error();
1658+
/* No opline was executed before exception */
1659+
EG(opline_before_exception) = NULL;
1660+
/* Fall through to handle exception below. */
1661+
}
1662+
#endif /* ZEND_CHECK_STACK_LIMIT */
1663+
16551664
while (1) {
16561665
zend_object *exception = EG(exception);
16571666

0 commit comments

Comments
 (0)