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

Arnaud Le Blanc <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Arnaud Le Blanc (arnaud-lb)
Date: 2026-08-10T12:01:05+02:00

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

Merge branch 'PHP-8.4' into PHP-8.5

* PHP-8.4:
  Add a stack limit check in php_array_replace_recursive (#23124)
  Add a stack limit check in php_array_walk() (#23125)

Changed paths:
  A  ext/standard/tests/array/gh23111.phpt
  A  ext/standard/tests/array/gh23113.phpt
  M  NEWS
  M  ext/standard/array.c


Diff:

diff --git a/NEWS b/NEWS
index 72388b5c946b..9cdb2f782101 100644
--- a/NEWS
+++ b/NEWS
@@ -76,6 +76,12 @@ PHP                                                                        NEWS
 - SQLite:
   . Fix leak when trying to close db if blob stream is still open. (ndossche)
 
+- Standard:
+  . Fixed bug GH-23111 (Stack overflow in array_walk_recursive() with deeply
+    nested arrays). (Lazizbek Ergashev)
+  . Fixed bug GH-23113 (Stack overflow in array_replace_recursive() with deeply
+    nested arrays). (Lazizbek Ergashev)
+
 - Streams:
   . Fixed bug GH-15836 (Use-after-free when a user stream filter accesses
     $this->stream during the close flush). (iliaal)
diff --git a/ext/standard/array.c b/ext/standard/array.c
index bb5f43e405ea..79ff4c8c4923 100644
--- a/ext/standard/array.c
+++ b/ext/standard/array.c
@@ -1450,6 +1450,13 @@ static zend_result php_array_walk(
 	 * levels of recursion. */
 	zend_fcall_info fci = context->fci;
 
+#ifdef ZEND_CHECK_STACK_LIMIT
+	if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
+		zend_call_stack_size_error();
+		return FAILURE;
+	}
+#endif
+
 	if (zend_hash_num_elements(target_hash) == 0) {
 		return result;
 	}
@@ -4152,6 +4159,13 @@ PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src) /* {{{ *
 	zend_ulong num_key;
 	int ret;
 
+#ifdef ZEND_CHECK_STACK_LIMIT
+	if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
+		zend_call_stack_size_error();
+		return 0;
+	}
+#endif
+
 	ZEND_HASH_FOREACH_KEY_VAL(src, num_key, string_key, src_entry) {
 		src_zval = src_entry;
 		ZVAL_DEREF(src_zval);
diff --git a/ext/standard/tests/array/gh23111.phpt b/ext/standard/tests/array/gh23111.phpt
new file mode 100644
index 000000000000..21db33a2fc75
--- /dev/null
+++ b/ext/standard/tests/array/gh23111.phpt
@@ -0,0 +1,27 @@
+--TEST--
+GH-23111 (Stack overflow in array_walk_recursive with deeply nested arrays)
+--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 < 30000; $i++) {
+    $a = [$a];
+}
+try {
+    array_walk_recursive($a, function ($v) {});
+} catch (Throwable $e) {
+    echo $e::class, ": ", $e->getMessage(), "\n";
+}
+?>
+--EXPECTF--
+Error: Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?
diff --git a/ext/standard/tests/array/gh23113.phpt b/ext/standard/tests/array/gh23113.phpt
new file mode 100644
index 000000000000..894e8d971e53
--- /dev/null
+++ b/ext/standard/tests/array/gh23113.phpt
@@ -0,0 +1,27 @@
+--TEST--
+GH-23113 (Stack overflow in array_replace_recursive with deeply nested arrays)
+--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 < 30000; $i++) {
+    $a = ['k' => $a];
+}
+try {
+    array_replace_recursive($a, $a);
+} catch (Throwable $e) {
+    echo $e::class, ": ", $e->getMessage(), "\n";
+}
+?>
+--EXPECTF--
+Error: Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.