[php-src] master: Merge branch 'PHP-8.5'

Ilia Alshanetsky <[email protected]> Fri, 24 Jul 2026 12:49:29 +0000
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Ilia Alshanetsky (iliaal)
Date: 2026-07-24T08:38:21-04:00

Commit: https://github.com/php/php-src/commit/fd796bb132f053477b9dcd77bf927eaba7c2c481
Raw diff: https://github.com/php/php-src/commit/fd796bb132f053477b9dcd77bf927eaba7c2c481.diff

Merge branch 'PHP-8.5'

* PHP-8.5:
  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 d79b8941634d..f94c1cf09584 100644
--- a/ext/standard/var.c
+++ b/ext/standard/var.c
@@ -39,6 +39,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 */
@@ -257,6 +263,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 */
@@ -272,6 +284,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 */