diff --git a/ext/dom/document.c b/ext/dom/document.c index 5bbe99eb364..3bf99cb4c00 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -1706,9 +1706,41 @@ static void php_dom_remove_xinclude_nodes(xmlNodePtr cur) /* {{{ */ } /* }}} */ -/* {{{ Substitutes xincludes in a DomDocument */ +static void dom_xinclude_strip_references(xmlNodePtr basep) +{ + php_libxml_node_free_resource(basep); + + xmlNodePtr current = basep->children; + + while (current) { + php_libxml_node_free_resource(current); + current = php_dom_next_in_tree_order(current, basep); + } +} + +/* See GH-14702. + * We have to remove userland references to xinclude fallback nodes because libxml2 will make clones of these + * and remove the original nodes. If the originals are removed while there are still userland references + * this will cause memory corruption. */ +static void dom_xinclude_strip_fallback_references(const xmlNode *basep) +{ + xmlNodePtr current = basep->children; + + while (current) { + if (current->type == XML_ELEMENT_NODE && current->ns != NULL && current->_private != NULL + && xmlStrEqual(current->name, XINCLUDE_FALLBACK) + && (xmlStrEqual(current->ns->href, XINCLUDE_NS) || xmlStrEqual(current->ns->href, XINCLUDE_OLD_NS))) { + dom_xinclude_strip_references(current); + } + + current = php_dom_next_in_tree_order(current, basep); + } +} + static int dom_perform_xinclude(xmlDocPtr docp, dom_object *intern, zend_long flags) { + dom_xinclude_strip_fallback_references((const xmlNode *) docp); + PHP_LIBXML_SANITIZE_GLOBALS(xinclude); int err = xmlXIncludeProcessFlags(docp, (int)flags); PHP_LIBXML_RESTORE_GLOBALS(xinclude); @@ -1730,6 +1762,7 @@ static int dom_perform_xinclude(xmlDocPtr docp, dom_object *intern, zend_long fl return err; } +/* {{{ Substitutues xincludes in a DomDocument */ PHP_METHOD(DOMDocument, xinclude) { xmlDoc *docp; diff --git a/ext/dom/tests/gh14702.phpt b/ext/dom/tests/gh14702.phpt new file mode 100644 index 00000000000..106270d8deb --- /dev/null +++ b/ext/dom/tests/gh14702.phpt @@ -0,0 +1,54 @@ +--TEST-- +GH-14702 (DOMDocument::xinclude() crash) +--EXTENSIONS-- +dom +--FILE-- +loadXML(<< + + + + + + + +XML); +$xi = $doc->createElementNS('http://www.w3.org/2001/XInclude', 'xi:include'); +$xi->setAttribute('href', 'nonexistent'); + +$fallback = $doc->createElementNS('http://www.w3.org/2001/XInclude', 'xi:fallback'); +$xi->appendChild($fallback); +$child1 = $fallback->appendChild($doc->createElement('fallback-child1')); +$child2 = $fallback->appendChild($doc->createElement('fallback-child2')); + +$xpath = new DOMXPath($doc); +$toReplace = $xpath->query('//child')->item(0); +$toReplace->parentNode->replaceChild($xi, $toReplace); + +$keep = $doc->documentElement->lastElementChild; + +var_dump(@$doc->xinclude()); +echo $doc->saveXML(); + +var_dump($keep->nodeName); + +$keep->textContent = 'still works'; +echo $doc->saveXML(); +?> +--EXPECT-- +int(2) + + + + + + +string(4) "keep" + + + + + still works +