Skip to content

Commit 9beccce

Browse files
committed
Fix GH-17847: xinclude destroys live node
dom_xinclude_strip_fallback_references() now also takes into account xi:include nodes children. This now subsumes all work done normally by the old start/end node removal, so we can remove that code and start using XML_PARSE_NOXINCNODE. Closes GH-17878.
1 parent 200f16f commit 9beccce

File tree

3 files changed

+59
-58
lines changed

3 files changed

+59
-58
lines changed

NEWS

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ PHP NEWS
1616
. Fixed bug GH-17797 (zend_test_compile_string crash on invalid
1717
script path). (David Carlier)
1818

19+
- DOM:
20+
. Fixed bug GH-17847 (xinclude destroys live node). (nielsdos)
21+
1922
- FFI:
2023
. Fix FFI Parsing of Pointer Declaration Lists. (davnotdev)
2124

ext/dom/document.c

+7-58
Original file line numberDiff line numberDiff line change
@@ -1586,47 +1586,6 @@ PHP_METHOD(DOMDocument, saveXML)
15861586
}
15871587
/* }}} end dom_document_savexml */
15881588

1589-
static xmlNodePtr php_dom_free_xinclude_node(xmlNodePtr cur) /* {{{ */
1590-
{
1591-
xmlNodePtr xincnode;
1592-
1593-
xincnode = cur;
1594-
cur = cur->next;
1595-
xmlUnlinkNode(xincnode);
1596-
php_libxml_node_free_resource(xincnode);
1597-
1598-
return cur;
1599-
}
1600-
/* }}} */
1601-
1602-
static void php_dom_remove_xinclude_nodes(xmlNodePtr cur) /* {{{ */
1603-
{
1604-
while(cur) {
1605-
if (cur->type == XML_XINCLUDE_START) {
1606-
cur = php_dom_free_xinclude_node(cur);
1607-
1608-
/* XML_XINCLUDE_END node will be a sibling of XML_XINCLUDE_START */
1609-
while(cur && cur->type != XML_XINCLUDE_END) {
1610-
/* remove xinclude processing nodes from recursive xincludes */
1611-
if (cur->type == XML_ELEMENT_NODE) {
1612-
php_dom_remove_xinclude_nodes(cur->children);
1613-
}
1614-
cur = cur->next;
1615-
}
1616-
1617-
if (cur && cur->type == XML_XINCLUDE_END) {
1618-
cur = php_dom_free_xinclude_node(cur);
1619-
}
1620-
} else {
1621-
if (cur->type == XML_ELEMENT_NODE) {
1622-
php_dom_remove_xinclude_nodes(cur->children);
1623-
}
1624-
cur = cur->next;
1625-
}
1626-
}
1627-
}
1628-
/* }}} */
1629-
16301589
/* Backported from master branch xml_common.h */
16311590
static zend_always_inline xmlNodePtr php_dom_next_in_tree_order(const xmlNode *nodep, const xmlNode *basep)
16321591
{
@@ -1660,17 +1619,19 @@ static void dom_xinclude_strip_references(xmlNodePtr basep)
16601619
}
16611620
}
16621621

1663-
/* See GH-14702.
1664-
* We have to remove userland references to xinclude fallback nodes because libxml2 will make clones of these
1622+
/* See GH-14702 and GH-17847.
1623+
* We have to remove userland references to xinclude nodes because libxml2 will make clones of these
16651624
* and remove the original nodes. If the originals are removed while there are still userland references
16661625
* this will cause memory corruption. */
16671626
static void dom_xinclude_strip_fallback_references(const xmlNode *basep)
16681627
{
16691628
xmlNodePtr current = basep->children;
16701629

1630+
/* TODO: try to improve loop search performance */
16711631
while (current) {
1672-
if (current->type == XML_ELEMENT_NODE && current->ns != NULL && current->_private != NULL
1673-
&& xmlStrEqual(current->name, XINCLUDE_FALLBACK)
1632+
if (current->type == XML_ELEMENT_NODE
1633+
&& current->ns != NULL
1634+
&& xmlStrEqual(current->name, XINCLUDE_NODE)
16741635
&& (xmlStrEqual(current->ns->href, XINCLUDE_NS) || xmlStrEqual(current->ns->href, XINCLUDE_OLD_NS))) {
16751636
dom_xinclude_strip_references(current);
16761637
}
@@ -1684,7 +1645,6 @@ PHP_METHOD(DOMDocument, xinclude)
16841645
{
16851646
zval *id;
16861647
xmlDoc *docp;
1687-
xmlNodePtr root;
16881648
zend_long flags = 0;
16891649
int err;
16901650
dom_object *intern;
@@ -1703,22 +1663,11 @@ PHP_METHOD(DOMDocument, xinclude)
17031663

17041664
dom_xinclude_strip_fallback_references((const xmlNode *) docp);
17051665

1666+
flags |= XML_PARSE_NOXINCNODE;
17061667
PHP_LIBXML_SANITIZE_GLOBALS(xinclude);
17071668
err = xmlXIncludeProcessFlags(docp, (int)flags);
17081669
PHP_LIBXML_RESTORE_GLOBALS(xinclude);
17091670

1710-
/* XML_XINCLUDE_START and XML_XINCLUDE_END nodes need to be removed as these
1711-
are added via xmlXIncludeProcess to mark beginning and ending of xincluded document
1712-
but are not wanted in resulting document - must be done even if err as it could fail after
1713-
having processed some xincludes */
1714-
root = (xmlNodePtr) docp->children;
1715-
while(root && root->type != XML_ELEMENT_NODE && root->type != XML_XINCLUDE_START) {
1716-
root = root->next;
1717-
}
1718-
if (root) {
1719-
php_dom_remove_xinclude_nodes(root);
1720-
}
1721-
17221671
php_libxml_invalidate_node_list_cache(intern->document);
17231672

17241673
if (err) {

ext/dom/tests/gh17847.phpt

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
--TEST--
2+
GH-17847 (xinclude destroys live node)
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
$doc = new DOMDocument();
8+
$doc->loadXML(<<<XML
9+
<root>
10+
<xi:include href="thisisnonexistent" xmlns:xi="http://www.w3.org/2001/XInclude">
11+
<xi:fallback>fallback<p>garbage</p></xi:fallback>
12+
<p>garbage</p>
13+
</xi:include>
14+
<xi:test xmlns:xi="http://www.w3.org/2001/XInclude">
15+
<xi:include href="thisisnonexistent">
16+
<p>garbage</p>
17+
</xi:include>
18+
</xi:test>
19+
</root>
20+
XML);
21+
22+
$xpath = new DOMXPath($doc);
23+
24+
$garbage = [];
25+
foreach ($xpath->query('//p') as $entry)
26+
$garbage[] = $entry;
27+
28+
@$doc->xinclude();
29+
30+
var_dump($garbage);
31+
?>
32+
--EXPECT--
33+
array(3) {
34+
[0]=>
35+
object(DOMElement)#3 (1) {
36+
["schemaTypeInfo"]=>
37+
NULL
38+
}
39+
[1]=>
40+
object(DOMElement)#4 (1) {
41+
["schemaTypeInfo"]=>
42+
NULL
43+
}
44+
[2]=>
45+
object(DOMElement)#5 (1) {
46+
["schemaTypeInfo"]=>
47+
NULL
48+
}
49+
}

0 commit comments

Comments
 (0)