Merge branch 'PHP-8.4'

* PHP-8.4:
  Fix GH-15169: stack overflow when var serialization in ext/standard/var
This commit is contained in:
Niels Dossche 2024-10-02 21:32:50 +02:00
commit a165f1fffc
No known key found for this signature in database
GPG key ID: B8A8AD166DF0E2E5
4 changed files with 49 additions and 28 deletions

View file

@ -27,13 +27,6 @@ class Test2 {
} }
} }
class Test3 {
public function __sleep()
{
serialize($this);
}
}
function replace() { function replace() {
return preg_replace_callback('#.#', function () { return preg_replace_callback('#.#', function () {
return replace(); return replace();
@ -52,12 +45,6 @@ try {
echo $e->getMessage(), "\n"; echo $e->getMessage(), "\n";
} }
try {
serialize(new Test3);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try { try {
replace(); replace();
} catch (Error $e) { } catch (Error $e) {
@ -79,4 +66,3 @@ array(4) {
Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion? Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?
Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion? Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?
Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion? Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?
Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?

View file

@ -25,13 +25,6 @@ class Test2 {
} }
} }
class Test3 {
public function __sleep()
{
serialize($this);
}
}
function replace() { function replace() {
return preg_replace_callback('#.#', function () { return preg_replace_callback('#.#', function () {
return replace(); return replace();
@ -51,12 +44,6 @@ $fiber = new Fiber(function (): void {
echo $e->getMessage(), "\n"; echo $e->getMessage(), "\n";
} }
try {
serialize(new Test3);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
try { try {
replace(); replace();
} catch (Error $e) { } catch (Error $e) {
@ -81,4 +68,3 @@ array(4) {
Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion? Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?
Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion? Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?
Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion? Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?
Maximum call stack size of %d bytes (zend.max_allowed_stack_size - zend.reserved_stack_size) reached. Infinite recursion?

View file

@ -0,0 +1,35 @@
--TEST--
GH-15169 (stack overflow when var serialization in ext/standard/var)
--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=512K
--FILE--
<?php
class Node
{
public $next;
}
$firstNode = new Node();
$node = $firstNode;
for ($i = 0; $i < 30000; $i++) {
$newNode = new Node();
$node->next = $newNode;
$node = $newNode;
}
try {
serialize($firstNode);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
Maximum call stack size reached. Infinite recursion?

View file

@ -1033,6 +1033,15 @@ static void php_var_serialize_class(smart_str *buf, zval *struc, HashTable *ht,
} }
/* }}} */ /* }}} */
static zend_always_inline bool php_serialize_check_stack_limit(void)
{
#ifdef ZEND_CHECK_STACK_LIMIT
return zend_call_stack_overflowed(EG(stack_limit));
#else
return false;
#endif
}
static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash, bool in_rcn_array, bool is_root) /* {{{ */ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash, bool in_rcn_array, bool is_root) /* {{{ */
{ {
zend_long var_already; zend_long var_already;
@ -1042,6 +1051,11 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
return; return;
} }
if (UNEXPECTED(php_serialize_check_stack_limit())) {
zend_throw_error(NULL, "Maximum call stack size reached. Infinite recursion?");
return;
}
if (var_hash && (var_already = php_add_var_hash(var_hash, struc, in_rcn_array))) { if (var_hash && (var_already = php_add_var_hash(var_hash, struc, in_rcn_array))) {
if (var_already == -1) { if (var_already == -1) {
/* Reference to an object that failed to serialize, replace with null. */ /* Reference to an object that failed to serialize, replace with null. */