[php-src] master: Merge branch 'PHP-8.5'
Ilia Alshanetsky <[email protected]>
| Newsgroups | gmane.comp.php.cvs.general |
|---|---|
| Message-ID | <[email protected]> |
Author: Ilia Alshanetsky (iliaal)
Date: 2026-07-03T14:05:08-04:00
Commit: https://github.com/php/php-src/commit/470b424843de999dea9a98998a1f37fa9f7677f8
Raw diff: https://github.com/php/php-src/commit/470b424843de999dea9a98998a1f37fa9f7677f8.diff
Merge branch 'PHP-8.5'
* PHP-8.5:
Fix GH-22570: stack overflow serializing a deeply nested Dom\XMLDocument
Changed paths:
A ext/dom/tests/modern/xml/gh22570.phpt
M NEWS
M ext/dom/document.c
M ext/dom/inner_outer_html_mixin.c
M ext/dom/xml_serializer.c
Diff:
diff --git a/NEWS b/NEWS
index 61a49cc1750a..e5dd4913e580 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,10 @@ PHP NEWS
- DBA:
. Fixed OOB read on malformed length field in dba flatfile handler. (alhudz)
+- DOM:
+ . Fixed bug GH-22570 (Stack overflow when serializing a deeply nested
+ Dom\XMLDocument). (iliaal)
+
- Exif:
. Fixed bug GH-11020 (exif_read_data() emits a spurious "Illegal IFD size"
warning when an IFD is not followed by a next-IFD offset). (Eyüp Can Akman)
diff --git a/ext/dom/document.c b/ext/dom/document.c
index 9c6f12143ad1..bad0dd9feb1b 100644
--- a/ext/dom/document.c
+++ b/ext/dom/document.c
@@ -1670,7 +1670,9 @@ static void dom_document_save_xml(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry
}
if (!res) {
- php_error_docref(NULL, E_WARNING, "Could not save document");
+ if (!EG(exception)) {
+ php_error_docref(NULL, E_WARNING, "Could not save document");
+ }
RETURN_FALSE;
} else {
RETURN_NEW_STR(res);
diff --git a/ext/dom/inner_outer_html_mixin.c b/ext/dom/inner_outer_html_mixin.c
index 0e40f8b07a3b..2a38e966ad79 100644
--- a/ext/dom/inner_outer_html_mixin.c
+++ b/ext/dom/inner_outer_html_mixin.c
@@ -98,7 +98,9 @@ static zend_string *dom_element_html_fragment_serialize(dom_object *obj, xmlNode
}
if (UNEXPECTED(status < 0)) {
smart_str_free_ex(&str, false);
- php_dom_throw_error_with_message(SYNTAX_ERR, "The resulting XML serialization is not well-formed", true);
+ if (!EG(exception)) {
+ php_dom_throw_error_with_message(SYNTAX_ERR, "The resulting XML serialization is not well-formed", true);
+ }
return NULL;
}
return smart_str_extract(&str);
diff --git a/ext/dom/tests/modern/xml/gh22570.phpt b/ext/dom/tests/modern/xml/gh22570.phpt
new file mode 100644
index 000000000000..af78acf00dfa
--- /dev/null
+++ b/ext/dom/tests/modern/xml/gh22570.phpt
@@ -0,0 +1,40 @@
+--TEST--
+GH-22570 (Stack overflow when serializing a deeply nested Dom\XMLDocument)
+--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
+// Build via the DOM API, not the parser: libxml caps parse depth even with
+// LIBXML_PARSEHUGE on some platforms; the serializer recursion is the bug.
+$doc = Dom\XMLDocument::createEmpty();
+$node = $doc->appendChild($doc->createElement('root'));
+for ($i = 0; $i < 100000; $i++) {
+ $node = $node->appendChild($doc->createElement('a'));
+}
+
+try {
+ $doc->saveXml();
+} catch (\Error $e) {
+ echo "saveXml: ", $e::class, ": ", $e->getMessage(), "\n";
+}
+
+try {
+ $doc->documentElement->innerHTML;
+} catch (\Error $e) {
+ echo "innerHTML: ", $e::class, ": ", $e->getMessage(), "\n";
+}
+?>
+--EXPECT--
+saveXml: Error: Maximum call stack size reached. Infinite recursion?
+innerHTML: Error: Maximum call stack size reached. Infinite recursion?
diff --git a/ext/dom/xml_serializer.c b/ext/dom/xml_serializer.c
index 9c761f6d442c..0d6b320dd0ef 100644
--- a/ext/dom/xml_serializer.c
+++ b/ext/dom/xml_serializer.c
@@ -1248,6 +1248,15 @@ static int dom_xml_serializing_a_document_node(
return 0;
}
+static zend_always_inline bool dom_xml_serialize_check_stack_limit(void)
+{
+#ifdef ZEND_CHECK_STACK_LIMIT
+ return zend_call_stack_overflowed(EG(stack_limit));
+#else
+ return false;
+#endif
+}
+
/* https://w3c.github.io/DOM-Parsing/#dfn-xml-serialization-algorithm */
static int dom_xml_serialization_algorithm(
dom_xml_serialize_ctx *ctx,
@@ -1259,6 +1268,11 @@ static int dom_xml_serialization_algorithm(
bool require_well_formed
)
{
+ if (UNEXPECTED(dom_xml_serialize_check_stack_limit())) {
+ zend_throw_error(NULL, "Maximum call stack size reached. Infinite recursion?");
+ return -1;
+ }
+
/* If node's interface is: */
switch (node->type) {
case XML_ELEMENT_NODE: