diff --git a/NEWS b/NEWS index ba832ef05888..3a33074d7eff 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,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-- + +--INI-- +zend.max_allowed_stack_size=512K +--FILE-- +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.