[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