Skip to content

Commit c742440

Browse files
committed
Merge branch 'PHP-8.5'
* PHP-8.5: Fix GH-23120: DOMNode::isEqualNode stack overflow on deeply nested trees (#23140)
2 parents 1423149 + 19eda22 commit c742440

3 files changed

Lines changed: 66 additions & 1 deletion

File tree

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ PHP NEWS
9797
- DOM:
9898
. Fixed bug GH-22825 (DOMElement::setAttribute() fails silently when the DTD
9999
declares a default value for the attribute). (iliaal)
100+
. Fixed bug GH-23120 (Stack overflow when comparing deeply nested DOM nodes
101+
with DOMNode::isEqualNode()). (Weilin Du)
100102

101103
- Embed:
102104
. Made php-cli functionality available in embed builds. (henderkes)

ext/dom/node.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1735,11 +1735,27 @@ static bool php_dom_is_equal_attr(const xmlAttr *this_attr, const xmlAttr *other
17351735
&& php_dom_node_is_content_equal((const xmlNode *) this_attr, (const xmlNode *) other_attr);
17361736
}
17371737

1738+
static zend_always_inline bool php_dom_node_is_equal_node_check_stack_limit(void)
1739+
{
1740+
#ifdef ZEND_CHECK_STACK_LIMIT
1741+
return zend_call_stack_overflowed(EG(stack_limit));
1742+
#else
1743+
return false;
1744+
#endif
1745+
}
1746+
17381747
static bool php_dom_node_is_equal_node(const xmlNode *this, const xmlNode *other, bool spec_compliant)
17391748
{
17401749
ZEND_ASSERT(this != NULL);
17411750
ZEND_ASSERT(other != NULL);
17421751

1752+
if (UNEXPECTED(php_dom_node_is_equal_node_check_stack_limit())) {
1753+
if (!EG(exception)) {
1754+
zend_throw_error(NULL, "Maximum call stack size reached.");
1755+
}
1756+
return false;
1757+
}
1758+
17431759
if (this->type != other->type) {
17441760
return false;
17451761
}
@@ -1800,6 +1816,7 @@ static void dom_node_is_equal_node_common(INTERNAL_FUNCTION_PARAMETERS, bool mod
18001816
zval *id, *node;
18011817
xmlNodePtr otherp, nodep;
18021818
dom_object *intern;
1819+
bool result;
18031820

18041821
id = ZEND_THIS;
18051822
ZEND_PARSE_PARAMETERS_START(1, 1)
@@ -1822,7 +1839,11 @@ static void dom_node_is_equal_node_common(INTERNAL_FUNCTION_PARAMETERS, bool mod
18221839
RETURN_BOOL(nodep == NULL && otherp == NULL);
18231840
}
18241841

1825-
RETURN_BOOL(php_dom_node_is_equal_node(nodep, otherp, modern));
1842+
result = php_dom_node_is_equal_node(nodep, otherp, modern);
1843+
if (UNEXPECTED(EG(exception))) {
1844+
RETURN_THROWS();
1845+
}
1846+
RETURN_BOOL(result);
18261847
}
18271848

18281849
PHP_METHOD(DOMNode, isEqualNode)

ext/dom/tests/gh23120.phpt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
--TEST--
2+
GH-23120 (Stack overflow when comparing deeply nested DOM nodes)
3+
--EXTENSIONS--
4+
dom
5+
--SKIPIF--
6+
<?php
7+
if (ini_get('zend.max_allowed_stack_size') === false) {
8+
die('skip No stack limit support');
9+
}
10+
if (getenv('SKIP_ASAN')) {
11+
die('skip ASAN needs different stack limit setting due to more stack space usage');
12+
}
13+
?>
14+
--INI--
15+
zend.max_allowed_stack_size=512K
16+
--FILE--
17+
<?php
18+
function create_deep_document(): DOMDocument {
19+
$doc = new DOMDocument();
20+
$node = $doc->createElement('leaf', 'x');
21+
22+
for ($i = 0; $i < 10000; $i++) {
23+
$parent = $doc->createElement('a');
24+
$parent->appendChild($node);
25+
$node = $parent;
26+
}
27+
28+
$doc->appendChild($node);
29+
return $doc;
30+
}
31+
32+
$doc1 = create_deep_document();
33+
$doc2 = create_deep_document();
34+
35+
try {
36+
var_dump($doc1->documentElement->isEqualNode($doc2->documentElement));
37+
} catch (\Error $e) {
38+
echo $e::class, ": ", $e->getMessage(), "\n";
39+
}
40+
?>
41+
--EXPECT--
42+
Error: Maximum call stack size reached.

0 commit comments

Comments
 (0)