Skip to content

Commit fd796bb

Browse files
committed
Merge branch 'PHP-8.5'
* PHP-8.5: Guard var_dump()/debug_zval_dump() against native stack overflow
2 parents ef02969 + 8f04dfd commit fd796bb

2 files changed

Lines changed: 58 additions & 0 deletions

File tree

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
--TEST--
2+
var_dump() and debug_zval_dump() guard against native stack overflow on deep structures
3+
--SKIPIF--
4+
<?php
5+
if (ini_get('zend.max_allowed_stack_size') === false) {
6+
die('skip No stack limit support');
7+
}
8+
if (getenv('SKIP_ASAN')) {
9+
die('skip ASAN needs different stack limit setting due to more stack space usage');
10+
}
11+
?>
12+
--INI--
13+
zend.max_allowed_stack_size=256K
14+
--FILE--
15+
<?php
16+
$a = [];
17+
for ($i = 0; $i < 50000; $i++) { $a = [$a]; }
18+
19+
class Node { public $next; }
20+
$firstNode = new Node();
21+
$node = $firstNode;
22+
for ($i = 0; $i < 50000; $i++) { $newNode = new Node(); $node->next = $newNode; $node = $newNode; }
23+
24+
function guarded(callable $fn): string {
25+
ob_start();
26+
$fn();
27+
return str_contains(ob_get_clean(), 'nesting level too deep') ? "guarded\n" : "NO GUARD\n";
28+
}
29+
30+
echo 'var_dump array: ', guarded(fn() => var_dump($a));
31+
echo 'debug_zval_dump array: ', guarded(fn() => debug_zval_dump($a));
32+
echo 'debug_zval_dump object: ', guarded(fn() => debug_zval_dump($firstNode));
33+
34+
while (is_array($a) && isset($a[0])) { $a = $a[0]; }
35+
while ($next = $firstNode->next) { $firstNode->next = $next->next; }
36+
?>
37+
--EXPECT--
38+
var_dump array: guarded
39+
debug_zval_dump array: guarded
40+
debug_zval_dump object: guarded

ext/standard/var.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ struct php_serialize_data {
3939

4040
static void php_array_element_dump(zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */
4141
{
42+
#ifdef ZEND_CHECK_STACK_LIMIT
43+
if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
44+
php_printf("%*cnesting level too deep", level + 1, ' ');
45+
return;
46+
}
47+
#endif
4248
if (key == NULL) { /* numeric key */
4349
php_printf("%*c[" ZEND_LONG_FMT "]=>\n", level + 1, ' ', index);
4450
} else { /* string key */
@@ -257,6 +263,12 @@ PHP_FUNCTION(var_dump)
257263

258264
static void zval_array_element_dump(zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */
259265
{
266+
#ifdef ZEND_CHECK_STACK_LIMIT
267+
if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
268+
php_printf("%*cnesting level too deep", level + 1, ' ');
269+
return;
270+
}
271+
#endif
260272
if (key == NULL) { /* numeric key */
261273
php_printf("%*c[" ZEND_LONG_FMT "]=>\n", level + 1, ' ', index);
262274
} else { /* string key */
@@ -272,6 +284,12 @@ static void zval_object_property_dump(zend_property_info *prop_info, zval *zv, z
272284
{
273285
const char *prop_name, *class_name;
274286

287+
#ifdef ZEND_CHECK_STACK_LIMIT
288+
if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
289+
php_printf("%*cnesting level too deep", level + 1, ' ');
290+
return;
291+
}
292+
#endif
275293
if (key == NULL) { /* numeric key */
276294
php_printf("%*c[" ZEND_LONG_FMT "]=>\n", level + 1, ' ', index);
277295
} else { /* string key */

0 commit comments

Comments
 (0)