1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

GH-18572: infinite stack recursion in fallback object comparison.

With nested objects and recursive comparisons, it is for now unavoidable
to have a stack overflow we do some early damage control attempt early
on with zend.max_allowed_stack_size check but ultimately more a band-aid
than a definitive solution.

close GH-18577
This commit is contained in:
David Carlier
2025-05-16 20:54:56 +01:00
parent 8e5b3129de
commit 4dcbd24bad
3 changed files with 55 additions and 0 deletions

2
NEWS
View File

@@ -5,6 +5,8 @@ PHP NEWS
- Core:
. Fixed GH-18480 (array_splice with large values for offset/length arguments).
(nielsdos/David Carlier)
. Partially fixed GH-18572 (nested object comparisons leading to stack overflow).
(David Carlier)
- Curl:
. Fixed GH-18460 (curl_easy_setopt with CURLOPT_USERPWD/CURLOPT_USERNAME/

39
Zend/tests/gh18572.phpt Normal file
View File

@@ -0,0 +1,39 @@
--TEST--
GH-18572: Nested object comparison leading to stack overflow
--SKIPIF--
<?php
if (getenv('SKIP_ASAN')) die('skip as it fatally crash');
?>
--FILE--
<?php
#[AllowDynamicProperties]
class Node {
public $next;
// forcing dynamic property creation is key
}
$first = new Node();
$first->previous = $first;
$first->next = $first;
$cur = $first;
for ($i = 0; $i < 50000; $i++) {
$new = new Node();
$new->previous = $cur;
$cur->next = $new;
$new->next = $first;
$first->previous = $new;
$cur = $new;
}
try {
// Force comparison manually to trigger zend_hash_compare
$first == $cur;
} catch(Error $e) {
echo $e->getMessage(). PHP_EOL;
}
?>
--EXPECTREGEX--
(Maximum call stack size reached during object comparison|Fatal error: Nesting level too deep - recursive dependency?.+)

View File

@@ -42,6 +42,15 @@
#define IN_UNSET ZEND_GUARD_PROPERTY_UNSET
#define IN_ISSET ZEND_GUARD_PROPERTY_ISSET
static zend_always_inline bool zend_objects_check_stack_limit(void)
{
#ifdef ZEND_CHECK_STACK_LIMIT
return zend_call_stack_overflowed(EG(stack_limit));
#else
return false;
#endif
}
/*
__X accessors explanation:
@@ -1714,6 +1723,11 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
{
zend_object *zobj1, *zobj2;
if (zend_objects_check_stack_limit()) {
zend_throw_error(NULL, "Maximum call stack size reached during object comparison");
return ZEND_UNCOMPARABLE;
}
if (Z_TYPE_P(o1) != Z_TYPE_P(o2)) {
/* Object and non-object */
zval *object;