[php-src] PHP-8.4: Add a stack limit check in php_array_replace_recursive (#23124)

Lazizbek Ergashev via GitHub <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Lazizbek Ergashev (lazerg)
Committer: GitHub (web-flow)
Pusher: arnaud-lb
Date: 2026-08-10T11:59:11+02:00

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

Add a stack limit check in php_array_replace_recursive (#23124)

php_array_replace_recursive() recurses once per nesting level with no stack check, so array_replace_recursive() on a deeply nested array exhausts the native stack and the process dies with a segfault.

Fixes GH-23113

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


Diff:

diff --git a/NEWS b/NEWS
index 01eb4160a20a..d7dadb551bcb 100644
--- a/NEWS
+++ b/NEWS
@@ -72,6 +72,8 @@ PHP                                                                        NEWS
 - 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
diff --git a/ext/standard/array.c b/ext/standard/array.c
index 6863586c81ff..41123d43bcc6 100644
--- a/ext/standard/array.c
+++ b/ext/standard/array.c
@@ -4168,6 +4168,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/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.