[php-src] master: Merge branch 'PHP-8.4' into PHP-8.5
Ilia Alshanetsky <[email protected]> Fri, 24 Jul 2026 12:49:19 +0000
| Newsgroups | gmane.comp.php.cvs.general |
|---|---|
| Message-ID | <[email protected]> |
Author: Ilia Alshanetsky (iliaal)
Date: 2026-07-24T08:33:23-04:00
Commit: https://github.com/php/php-src/commit/8f04dfd7b274936458d11f9442e79ab0f4fef47b
Raw diff: https://github.com/php/php-src/commit/8f04dfd7b274936458d11f9442e79ab0f4fef47b.diff
Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4:
Guard var_dump()/debug_zval_dump() against native stack overflow
Changed paths:
A ext/standard/tests/general_functions/var_dump_stack_limit.phpt
M ext/standard/var.c
Diff:
diff --git a/ext/standard/tests/general_functions/var_dump_stack_limit.phpt b/ext/standard/tests/general_functions/var_dump_stack_limit.phpt
new file mode 100644
index 000000000000..a124e811fc9c
--- /dev/null
+++ b/ext/standard/tests/general_functions/var_dump_stack_limit.phpt
@@ -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
diff --git a/ext/standard/var.c b/ext/standard/var.c
index acb605d2eabe..99f28366ffdb 100644
--- a/ext/standard/var.c
+++ b/ext/standard/var.c
@@ -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 */
@@ -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 */
@@ -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 */