Skip to content

Fix GH-15169: stack overflow when var serialization in ext/standard/var #16159

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 0 additions & 14 deletions Zend/tests/stack_limit/stack_limit_001.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,6 @@ class Test2 {
}
}

class Test3 {
public function __sleep()
{
serialize($this);
}
}

function replace() {
return preg_replace_callback('#.#', function () {
return replace();
Expand All @@ -52,12 +45,6 @@ try {
echo $e->getMessage(), "\n";
}

try {
serialize(new Test3);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

try {
replace();
} catch (Error $e) {
Expand All @@ -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?
14 changes: 0 additions & 14 deletions Zend/tests/stack_limit/stack_limit_002.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,6 @@ class Test2 {
}
}

class Test3 {
public function __sleep()
{
serialize($this);
}
}

function replace() {
return preg_replace_callback('#.#', function () {
return replace();
Expand All @@ -52,12 +45,6 @@ $fiber = new Fiber(function (): void {
echo $e->getMessage(), "\n";
}

try {
serialize(new Test3);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}

try {
replace();
} catch (Error $e) {
Expand All @@ -82,4 +69,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?
35 changes: 35 additions & 0 deletions ext/standard/tests/serialize/gh15169.phpt
Original file line number Diff line number Diff line change
@@ -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?
14 changes: 14 additions & 0 deletions ext/standard/var.c
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,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) /* {{{ */
{
zend_long var_already;
Expand All @@ -995,6 +1004,11 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_
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_already == -1) {
/* Reference to an object that failed to serialize, replace with null. */
Expand Down
Loading