Commit afe5635c3ff for php

commit afe5635c3ff58f1f4f62019c971e66119752a8e0
Author: Labish Bardiya <labishjain7@gmail.com>
Date:   Sat Oct 24 20:54:54 2026 +0000

    Fix GH-24605: Guard var_export() against native stack overflow (#24671)

    Deeply nested arrays and objects can exhaust the native stack because
    php_var_export_ex() recursively exports their contents without checking
    Zend's stack limit.

    Check the native stack limit and throw a catchable Error when it is reached.
    Propagate failures from recursive object property exports while releasing
    temporary values, property tables, and recursion guards on the failure path.

    Add regression coverage for deeply nested arrays and objects with a reduced
    stack limit. This fixes only the var_export() part of GH-24605; print_r() and
    unserialize() remain outside this change's scope.

    Closes #24671

diff --git a/NEWS b/NEWS
index 60b978e801d..131c02d4c24 100644
--- a/NEWS
+++ b/NEWS
@@ -188,6 +188,8 @@ PHP                                                                        NEWS
     (Nicolas Grekas)
   . Fixed bug GH-24373 (serialize() called from a destructor during var_hash
     destruction reuses the outer var_hash). (David Carlier)
+  . Fixed bug GH-24605 (var_export() crashes on deeply nested arrays and
+    objects). (Labish Bardiya)

 - Streams:
   . Fixed bug GH-24508 (HTTP wrapper leaks the dechunk filter when header
diff --git a/ext/standard/tests/general_functions/gh24605_var_export_stack_guard.phpt b/ext/standard/tests/general_functions/gh24605_var_export_stack_guard.phpt
new file mode 100644
index 00000000000..c6dc47b85bb
--- /dev/null
+++ b/ext/standard/tests/general_functions/gh24605_var_export_stack_guard.phpt
@@ -0,0 +1,57 @@
+--TEST--
+GH-24605: var_export() checks native stack limit for arrays and objects
+--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');
+}
+?>
+--INI--
+zend.max_allowed_stack_size=256K
+--FILE--
+<?php
+function test_export(string $name, mixed $value): void {
+    try {
+        var_export($value, true);
+        echo "$name: NOT GUARDED\n";
+    } catch (Error $e) {
+        echo $name, ': ', str_contains($e->getMessage(), 'Maximum call stack size')
+            ? "guarded\n"
+            : "unexpected error\n";
+    }
+}
+
+$data = 0;
+for ($i = 0; $i < 5000; $i++) {
+    $data = [$data];
+}
+test_export('array', $data);
+
+// Detach the nested arrays without recursively destroying them.
+while (is_array($data)) {
+    $data = $data[0];
+}
+
+class StackNode { public ?StackNode $next = null; }
+$first = new StackNode();
+$node = $first;
+for ($i = 0; $i < 5000; $i++) {
+    $node->next = new StackNode();
+    $node = $node->next;
+}
+test_export('object', $first);
+
+// Break the chain iteratively to avoid teardown recursion.
+$node = $first;
+while ($node !== null) {
+    $next = $node->next;
+    $node->next = null;
+    $node = $next;
+}
+?>
+--EXPECT--
+array: guarded
+object: guarded
diff --git a/ext/standard/var.c b/ext/standard/var.c
index 8906d914d5d..6ce623bc2a4 100644
--- a/ext/standard/var.c
+++ b/ext/standard/var.c
@@ -542,6 +542,13 @@ PHPAPI zend_result php_var_export_ex(zval *struc, int level, smart_str *buf) /*
 	zend_string *key;
 	zval *val;

+#ifdef ZEND_CHECK_STACK_LIMIT
+	if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
+		zend_call_stack_size_error();
+		return FAILURE;
+	}
+#endif
+
 again:
 	switch (Z_TYPE_P(struc)) {
 		case IS_FALSE:
@@ -672,10 +679,15 @@ PHPAPI zend_result php_var_export_ex(zval *struc, int level, smart_str *buf) /*
 								return FAILURE;
 							}
 						}
-						php_object_element_export(val, index, key, level, buf);
+						zend_result result = php_object_element_export(val, index, key, level, buf);
 						if (val == &tmp) {
 							zval_ptr_dtor(val);
 						}
+						if (UNEXPECTED(result == FAILURE)) {
+							ZEND_GUARD_OR_GC_UNPROTECT_RECURSION(guard, EXPORT, zobj);
+							zend_release_properties(myht);
+							return FAILURE;
+						}
 					} ZEND_HASH_FOREACH_END();
 				}
 				zend_release_properties(myht);