Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion ext/dom/document.c
Original file line number Diff line number Diff line change
Expand Up @@ -1678,7 +1678,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);
Expand Down
4 changes: 3 additions & 1 deletion ext/dom/inner_html_mixin.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ zend_result dom_element_inner_html_read(dom_object *obj, zval *retval)
}
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 FAILURE;
}
ZVAL_STR(retval, smart_str_extract(&str));
Expand Down
40 changes: 40 additions & 0 deletions ext/dom/tests/modern/xml/gh22570.phpt
Original file line number Diff line number Diff line change
@@ -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?
14 changes: 14 additions & 0 deletions ext/dom/xml_serializer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1250,6 +1250,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,
Expand All @@ -1261,6 +1270,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:
Expand Down
Loading