Skip to content

Fix GH-17847: xinclude destroys live node #17878

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
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
65 changes: 7 additions & 58 deletions ext/dom/document.c
Original file line number Diff line number Diff line change
Expand Up @@ -1586,47 +1586,6 @@ PHP_METHOD(DOMDocument, saveXML)
}
/* }}} end dom_document_savexml */

static xmlNodePtr php_dom_free_xinclude_node(xmlNodePtr cur) /* {{{ */
{
xmlNodePtr xincnode;

xincnode = cur;
cur = cur->next;
xmlUnlinkNode(xincnode);
php_libxml_node_free_resource(xincnode);

return cur;
}
/* }}} */

static void php_dom_remove_xinclude_nodes(xmlNodePtr cur) /* {{{ */
{
while(cur) {
if (cur->type == XML_XINCLUDE_START) {
cur = php_dom_free_xinclude_node(cur);

/* XML_XINCLUDE_END node will be a sibling of XML_XINCLUDE_START */
while(cur && cur->type != XML_XINCLUDE_END) {
/* remove xinclude processing nodes from recursive xincludes */
if (cur->type == XML_ELEMENT_NODE) {
php_dom_remove_xinclude_nodes(cur->children);
}
cur = cur->next;
}

if (cur && cur->type == XML_XINCLUDE_END) {
cur = php_dom_free_xinclude_node(cur);
}
} else {
if (cur->type == XML_ELEMENT_NODE) {
php_dom_remove_xinclude_nodes(cur->children);
}
cur = cur->next;
}
}
}
/* }}} */

/* Backported from master branch xml_common.h */
static zend_always_inline xmlNodePtr php_dom_next_in_tree_order(const xmlNode *nodep, const xmlNode *basep)
{
Expand Down Expand Up @@ -1660,17 +1619,19 @@ static void dom_xinclude_strip_references(xmlNodePtr basep)
}
}

/* See GH-14702.
* We have to remove userland references to xinclude fallback nodes because libxml2 will make clones of these
/* See GH-14702 and GH-17847.
* We have to remove userland references to xinclude 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;

/* TODO: try to improve loop search performance */
while (current) {
if (current->type == XML_ELEMENT_NODE && current->ns != NULL && current->_private != NULL
&& xmlStrEqual(current->name, XINCLUDE_FALLBACK)
if (current->type == XML_ELEMENT_NODE
&& current->ns != NULL
&& xmlStrEqual(current->name, XINCLUDE_NODE)
&& (xmlStrEqual(current->ns->href, XINCLUDE_NS) || xmlStrEqual(current->ns->href, XINCLUDE_OLD_NS))) {
dom_xinclude_strip_references(current);
}
Expand All @@ -1684,7 +1645,6 @@ PHP_METHOD(DOMDocument, xinclude)
{
zval *id;
xmlDoc *docp;
xmlNodePtr root;
zend_long flags = 0;
int err;
dom_object *intern;
Expand All @@ -1703,22 +1663,11 @@ PHP_METHOD(DOMDocument, xinclude)

dom_xinclude_strip_fallback_references((const xmlNode *) docp);

flags |= XML_PARSE_NOXINCNODE;
PHP_LIBXML_SANITIZE_GLOBALS(xinclude);
err = xmlXIncludeProcessFlags(docp, (int)flags);
PHP_LIBXML_RESTORE_GLOBALS(xinclude);

/* XML_XINCLUDE_START and XML_XINCLUDE_END nodes need to be removed as these
are added via xmlXIncludeProcess to mark beginning and ending of xincluded document
but are not wanted in resulting document - must be done even if err as it could fail after
having processed some xincludes */
root = (xmlNodePtr) docp->children;
while(root && root->type != XML_ELEMENT_NODE && root->type != XML_XINCLUDE_START) {
root = root->next;
}
if (root) {
php_dom_remove_xinclude_nodes(root);
}

php_libxml_invalidate_node_list_cache(intern->document);

if (err) {
Expand Down
49 changes: 49 additions & 0 deletions ext/dom/tests/gh17847.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
--TEST--
GH-17847 (xinclude destroys live node)
--EXTENSIONS--
dom
--FILE--
<?php
$doc = new DOMDocument();
$doc->loadXML(<<<XML
<root>
<xi:include href="thisisnonexistent" xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:fallback>fallback<p>garbage</p></xi:fallback>
<p>garbage</p>
</xi:include>
<xi:test xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="thisisnonexistent">
<p>garbage</p>
</xi:include>
</xi:test>
</root>
XML);

$xpath = new DOMXPath($doc);

$garbage = [];
foreach ($xpath->query('//p') as $entry)
$garbage[] = $entry;

@$doc->xinclude();

var_dump($garbage);
?>
--EXPECT--
array(3) {
[0]=>
object(DOMElement)#3 (1) {
["schemaTypeInfo"]=>
NULL
}
[1]=>
object(DOMElement)#4 (1) {
["schemaTypeInfo"]=>
NULL
}
[2]=>
object(DOMElement)#5 (1) {
["schemaTypeInfo"]=>
NULL
}
}
Loading