Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 4dcbd24

Browse files
committed
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
1 parent 8e5b312 commit 4dcbd24

File tree

3 files changed

+55
-0
lines changed

3 files changed

+55
-0
lines changed

‎NEWS‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ PHP NEWS
55
- Core:
66
. Fixed GH-18480 (array_splice with large values for offset/length arguments).
77
(nielsdos/David Carlier)
8+
. Partially fixed GH-18572 (nested object comparisons leading to stack overflow).
9+
(David Carlier)
810

911
- Curl:
1012
. Fixed GH-18460 (curl_easy_setopt with CURLOPT_USERPWD/CURLOPT_USERNAME/

‎Zend/tests/gh18572.phpt‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
GH-18572: Nested object comparison leading to stack overflow
3+
--SKIPIF--
4+
<?php
5+
if (getenv('SKIP_ASAN')) die('skip as it fatally crash');
6+
?>
7+
--FILE--
8+
<?php
9+
10+
#[AllowDynamicProperties]
11+
class Node {
12+
public $next;
13+
// forcing dynamic property creation is key
14+
}
15+
16+
$first = new Node();
17+
$first->previous = $first;
18+
$first->next = $first;
19+
20+
$cur = $first;
21+
22+
for ($i = 0; $i < 50000; $i++) {
23+
$new = new Node();
24+
$new->previous = $cur;
25+
$cur->next = $new;
26+
$new->next = $first;
27+
$first->previous = $new;
28+
$cur = $new;
29+
}
30+
31+
try {
32+
// Force comparison manually to trigger zend_hash_compare
33+
$first == $cur;
34+
} catch(Error $e) {
35+
echo $e->getMessage(). PHP_EOL;
36+
}
37+
?>
38+
--EXPECTREGEX--
39+
(Maximum call stack size reached during object comparison|Fatal error: Nesting level too deep - recursive dependency?.+)

‎Zend/zend_object_handlers.c‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@
4242
#define IN_UNSET ZEND_GUARD_PROPERTY_UNSET
4343
#define IN_ISSET ZEND_GUARD_PROPERTY_ISSET
4444

45+
static zend_always_inline bool zend_objects_check_stack_limit(void)
46+
{
47+
#ifdef ZEND_CHECK_STACK_LIMIT
48+
return zend_call_stack_overflowed(EG(stack_limit));
49+
#else
50+
return false;
51+
#endif
52+
}
53+
4554
/*
4655
__X accessors explanation:
4756
@@ -1714,6 +1723,11 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
17141723
{
17151724
zend_object *zobj1, *zobj2;
17161725

1726+
if (zend_objects_check_stack_limit()) {
1727+
zend_throw_error(NULL, "Maximum call stack size reached during object comparison");
1728+
return ZEND_UNCOMPARABLE;
1729+
}
1730+
17171731
if (Z_TYPE_P(o1) != Z_TYPE_P(o2)) {
17181732
/* Object and non-object */
17191733
zval *object;

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /