[php-src] master: Merge branch '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-10T13:12:34+02:00

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

Merge branch 'PHP-8.5'

* PHP-8.5:
  Add a stack limit check in php_compact_var() (#23126)

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


Diff:

diff --git a/ext/standard/array.c b/ext/standard/array.c
index 4dc12d0f59cc..51b1795b9111 100644
--- a/ext/standard/array.c
+++ b/ext/standard/array.c
@@ -2569,7 +2569,7 @@ PHP_FUNCTION(extract)
 }
 /* }}} */
 
-static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_value, zval *entry, uint32_t pos) /* {{{ */
+static zend_result php_compact_var(HashTable *eg_active_symbol_table, zval *return_value, zval *entry, uint32_t pos) /* {{{ */
 {
 	zval *value_ptr, data;
 
@@ -2587,25 +2587,43 @@ static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_valu
 			}
 		} else {
 			php_error_docref(NULL, E_WARNING, "Undefined variable $%pS", Z_STR_P(entry));
+			/* A user error handler may have thrown. */
+			return EG(exception) ? FAILURE : SUCCESS;
 		}
 	} else if (Z_TYPE_P(entry) == IS_ARRAY) {
+		zend_result result = SUCCESS;
+
+#ifdef ZEND_CHECK_STACK_LIMIT
+		if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
+			zend_call_stack_size_error();
+			return FAILURE;
+		}
+#endif
 		if (Z_REFCOUNTED_P(entry)) {
 			if (Z_IS_RECURSIVE_P(entry)) {
 				zend_throw_error(NULL, "Recursion detected");
-				return;
+				return FAILURE;
 			}
 			Z_PROTECT_RECURSION_P(entry);
 		}
 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(entry), value_ptr) {
-			php_compact_var(eg_active_symbol_table, return_value, value_ptr, pos);
+			if (UNEXPECTED(php_compact_var(eg_active_symbol_table, return_value, value_ptr, pos) == FAILURE)) {
+				result = FAILURE;
+				break;
+			}
 		} ZEND_HASH_FOREACH_END();
 		if (Z_REFCOUNTED_P(entry)) {
 			Z_UNPROTECT_RECURSION_P(entry);
 		}
+
+		return result;
 	} else {
 		php_error_docref(NULL, E_WARNING, "Argument #%d must be string or array of strings, %s given", pos, zend_zval_value_name(entry));
-		return;
+		/* A user error handler may have thrown. */
+		return EG(exception) ? FAILURE : SUCCESS;
 	}
+
+	return SUCCESS;
 }
 /* }}} */
 
@@ -2637,7 +2655,9 @@ PHP_FUNCTION(compact)
 	}
 
 	for (i = 0; i < num_args; i++) {
-		php_compact_var(symbol_table, return_value, &args[i], i + 1);
+		if (UNEXPECTED(php_compact_var(symbol_table, return_value, &args[i], i + 1) == FAILURE)) {
+			RETURN_THROWS();
+		}
 	}
 }
 /* }}} */
diff --git a/ext/standard/tests/array/gh23115.phpt b/ext/standard/tests/array/gh23115.phpt
new file mode 100644
index 000000000000..7854583f9ec6
--- /dev/null
+++ b/ext/standard/tests/array/gh23115.phpt
@@ -0,0 +1,41 @@
+--TEST--
+GH-23115 (Stack overflow in compact 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
+/* Two elements per nesting level: the sibling must not be visited once the
+ * stack limit error has been thrown, so only one Error is thrown. */
+$names = [];
+for ($i = 0; $i < 30000; $i++) {
+    $names = [$names, []];
+}
+
+try {
+    compact($names);
+} catch (Throwable $e) {
+    echo $e::class, ": ", $e->getMessage(), "\n";
+    var_dump($e->getPrevious());
+}
+
+try {
+    compact($names, $names);
+} catch (Throwable $e) {
+    echo $e::class, ": ", $e->getMessage(), "\n";
+    var_dump($e->getPrevious());
+}
+?>
+--EXPECTF--
+Error: Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?
+NULL
+Error: Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?
+NULL
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.