Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions ext/standard/tests/general_functions/var_dump_stack_limit.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
--TEST--
var_dump() and debug_zval_dump() guard against native stack overflow on deep structures
--SKIPIF--
<?php
if (ini_get('zend.max_allowed_stack_size') === false) {
die('skip No stack limit support');
}
if (getenv('SKIP_ASAN')) {
die('skip ASAN needs different stack limit setting due to more stack space usage');
}
?>
--INI--
zend.max_allowed_stack_size=256K
--FILE--
<?php
$a = [];
for ($i = 0; $i < 50000; $i++) { $a = [$a]; }

class Node { public $next; }
$firstNode = new Node();
$node = $firstNode;
for ($i = 0; $i < 50000; $i++) { $newNode = new Node(); $node->next = $newNode; $node = $newNode; }

function guarded(callable $fn): string {
ob_start();
$fn();
return str_contains(ob_get_clean(), 'nesting level too deep') ? "guarded\n" : "NO GUARD\n";
}

echo 'var_dump array: ', guarded(fn() => var_dump($a));
echo 'debug_zval_dump array: ', guarded(fn() => debug_zval_dump($a));
echo 'debug_zval_dump object: ', guarded(fn() => debug_zval_dump($firstNode));

while (is_array($a) && isset($a[0])) { $a = $a[0]; }
while ($next = $firstNode->next) { $firstNode->next = $next->next; }
?>
--EXPECT--
var_dump array: guarded
debug_zval_dump array: guarded
debug_zval_dump object: guarded
18 changes: 18 additions & 0 deletions ext/standard/var.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ struct php_serialize_data {

static void php_array_element_dump(zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */
{
#ifdef ZEND_CHECK_STACK_LIMIT
if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
php_printf("%*cnesting level too deep", level + 1, ' ');
return;
}
#endif
if (key == NULL) { /* numeric key */
php_printf("%*c[" ZEND_LONG_FMT "]=>\n", level + 1, ' ', index);
} else { /* string key */
Expand Down Expand Up @@ -255,6 +261,12 @@ PHP_FUNCTION(var_dump)

static void zval_array_element_dump(zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */
{
#ifdef ZEND_CHECK_STACK_LIMIT
if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
php_printf("%*cnesting level too deep", level + 1, ' ');
return;
}
#endif
if (key == NULL) { /* numeric key */
php_printf("%*c[" ZEND_LONG_FMT "]=>\n", level + 1, ' ', index);
} else { /* string key */
Expand All @@ -270,6 +282,12 @@ static void zval_object_property_dump(zend_property_info *prop_info, zval *zv, z
{
const char *prop_name, *class_name;

#ifdef ZEND_CHECK_STACK_LIMIT
if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
php_printf("%*cnesting level too deep", level + 1, ' ');
return;
}
#endif
if (key == NULL) { /* numeric key */
php_printf("%*c[" ZEND_LONG_FMT "]=>\n", level + 1, ' ', index);
} else { /* string key */
Expand Down
Loading