[php-src] PHP-8.4: Fix GH-23120: DOMNode::isEqualNode stack overflow on deeply nested trees (#23140)

From: Date: Sat, 08 Aug 2026 16:04:09 +0000
Subject: [php-src] PHP-8.4: Fix GH-23120: DOMNode::isEqualNode stack overflow on deeply nested trees (#23140)
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-140543@lists.php.net to get a copy of this message
Author: Weilin Du (LamentXU123) Committer: GitHub (web-flow) Pusher: LamentXU123 Date: 2026-08-09T00:04:06+08:00 Commit: https://github.com/php/php-src/commit/c4c356a7bac4627bf22bc3deaff9d587be866484 Raw diff: https://github.com/php/php-src/commit/c4c356a7bac4627bf22bc3deaff9d587be866484.diff Fix GH-23120: DOMNode::isEqualNode stack overflow on deeply nested trees (#23140) Changed paths: A ext/dom/tests/gh23120.phpt M NEWS M ext/dom/node.c Diff: diff --git a/NEWS b/NEWS index 81f448cbd56e..e4a21b1bfa44 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,8 @@ PHP NEWS . Fixed bug GH-22447 (UAF at dom_objects_free_storage when setting an attribute node that collides by local name with a namespaced attribute). (David Carlier) + . Fixed bug GH-23120 (Stack overflow when comparing deeply nested DOM nodes + with DOMNode::isEqualNode()). (Weilin Du) - MBString: . Fixed bug GH-22779 (mb_strrpos() returns the wrong position for a negative diff --git a/ext/dom/node.c b/ext/dom/node.c index 81c80cb0c8ac..a42dfedc32a5 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -1730,11 +1730,27 @@ static bool php_dom_is_equal_attr(const xmlAttr *this_attr, const xmlAttr *other && php_dom_node_is_content_equal((const xmlNode *) this_attr, (const xmlNode *) other_attr); } +static zend_always_inline bool php_dom_node_is_equal_node_check_stack_limit(void) +{ +#ifdef ZEND_CHECK_STACK_LIMIT + return zend_call_stack_overflowed(EG(stack_limit)); +#else + return false; +#endif +} + static bool php_dom_node_is_equal_node(const xmlNode *this, const xmlNode *other, bool spec_compliant) { ZEND_ASSERT(this != NULL); ZEND_ASSERT(other != NULL); + if (UNEXPECTED(php_dom_node_is_equal_node_check_stack_limit())) { + if (!EG(exception)) { + zend_throw_error(NULL, "Maximum call stack size reached."); + } + return false; + } + if (this->type != other->type) { return false; } @@ -1795,6 +1811,7 @@ static void dom_node_is_equal_node_common(INTERNAL_FUNCTION_PARAMETERS, bool mod zval *id, *node; xmlNodePtr otherp, nodep; dom_object *intern; + bool result; id = ZEND_THIS; ZEND_PARSE_PARAMETERS_START(1, 1) @@ -1817,7 +1834,11 @@ static void dom_node_is_equal_node_common(INTERNAL_FUNCTION_PARAMETERS, bool mod RETURN_BOOL(nodep == NULL && otherp == NULL); } - RETURN_BOOL(php_dom_node_is_equal_node(nodep, otherp, modern)); + result = php_dom_node_is_equal_node(nodep, otherp, modern); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } + RETURN_BOOL(result); } PHP_METHOD(DOMNode, isEqualNode) diff --git a/ext/dom/tests/gh23120.phpt b/ext/dom/tests/gh23120.phpt new file mode 100644 index 000000000000..511314d9cbba --- /dev/null +++ b/ext/dom/tests/gh23120.phpt @@ -0,0 +1,42 @@ +--TEST-- +GH-23120 (Stack overflow when comparing deeply nested DOM nodes) +--EXTENSIONS-- +dom +--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 +function create_deep_document(): DOMDocument { + $doc = new DOMDocument(); + $node = $doc->createElement('leaf', 'x'); + + for ($i = 0; $i < 10000; $i++) { + $parent = $doc->createElement('a'); + $parent->appendChild($node); + $node = $parent; + } + + $doc->appendChild($node); + return $doc; +} + +$doc1 = create_deep_document(); +$doc2 = create_deep_document(); + +try { + var_dump($doc1->documentElement->isEqualNode($doc2->documentElement)); +} catch (\Error $e) { + echo $e::class, ": ", $e->getMessage(), "\n"; +} +?> +--EXPECT-- +Error: Maximum call stack size reached.

« previous php.cvs (#140543) next »