Skip to content

Commit a788425

Browse files
committed
Merge branch 'PHP-8.3' into PHP-8.4
2 parents dbc7c5f + 4dcbd24 commit a788425

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
@@ -46,6 +46,15 @@
4646
#define IN_ISSET ZEND_GUARD_PROPERTY_ISSET
4747
#define IN_HOOK ZEND_GUARD_PROPERTY_HOOK
4848

49+
static zend_always_inline bool zend_objects_check_stack_limit(void)
50+
{
51+
#ifdef ZEND_CHECK_STACK_LIMIT
52+
return zend_call_stack_overflowed(EG(stack_limit));
53+
#else
54+
return false;
55+
#endif
56+
}
57+
4958
/*
5059
__X accessors explanation:
5160
@@ -2120,6 +2129,11 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */
21202129
{
21212130
zend_object *zobj1, *zobj2;
21222131

2132+
if (zend_objects_check_stack_limit()) {
2133+
zend_throw_error(NULL, "Maximum call stack size reached during object comparison");
2134+
return ZEND_UNCOMPARABLE;
2135+
}
2136+
21232137
if (Z_TYPE_P(o1) != Z_TYPE_P(o2)) {
21242138
/* Object and non-object */
21252139
zval *object;

0 commit comments

Comments
 (0)