Skip to content

Commit 8ce16d5

Browse files
committed
Merge branch 'PHP-8.4'
* PHP-8.4: Fix GH-17847: xinclude destroys live node
2 parents 7b8a61a + be3d128 commit 8ce16d5

File tree

2 files changed

+48
-57
lines changed

2 files changed

+48
-57
lines changed

ext/dom/document.c

+7-57
Original file line numberDiff line numberDiff line change
@@ -1668,47 +1668,6 @@ PHP_METHOD(Dom_XMLDocument, saveXml)
16681668
}
16691669
/* }}} end dom_document_savexml */
16701670

1671-
static xmlNodePtr php_dom_free_xinclude_node(xmlNodePtr cur) /* {{{ */
1672-
{
1673-
xmlNodePtr xincnode;
1674-
1675-
xincnode = cur;
1676-
cur = cur->next;
1677-
xmlUnlinkNode(xincnode);
1678-
php_libxml_node_free_resource(xincnode);
1679-
1680-
return cur;
1681-
}
1682-
/* }}} */
1683-
1684-
static void php_dom_remove_xinclude_nodes(xmlNodePtr cur) /* {{{ */
1685-
{
1686-
while(cur) {
1687-
if (cur->type == XML_XINCLUDE_START) {
1688-
cur = php_dom_free_xinclude_node(cur);
1689-
1690-
/* XML_XINCLUDE_END node will be a sibling of XML_XINCLUDE_START */
1691-
while(cur && cur->type != XML_XINCLUDE_END) {
1692-
/* remove xinclude processing nodes from recursive xincludes */
1693-
if (cur->type == XML_ELEMENT_NODE) {
1694-
php_dom_remove_xinclude_nodes(cur->children);
1695-
}
1696-
cur = cur->next;
1697-
}
1698-
1699-
if (cur && cur->type == XML_XINCLUDE_END) {
1700-
cur = php_dom_free_xinclude_node(cur);
1701-
}
1702-
} else {
1703-
if (cur->type == XML_ELEMENT_NODE) {
1704-
php_dom_remove_xinclude_nodes(cur->children);
1705-
}
1706-
cur = cur->next;
1707-
}
1708-
}
1709-
}
1710-
/* }}} */
1711-
17121671
static void dom_xinclude_strip_references(xmlNodePtr basep)
17131672
{
17141673
php_libxml_node_free_resource(basep);
@@ -1721,17 +1680,19 @@ static void dom_xinclude_strip_references(xmlNodePtr basep)
17211680
}
17221681
}
17231682

1724-
/* See GH-14702.
1725-
* We have to remove userland references to xinclude fallback nodes because libxml2 will make clones of these
1683+
/* See GH-14702 and GH-17847.
1684+
* We have to remove userland references to xinclude nodes because libxml2 will make clones of these
17261685
* and remove the original nodes. If the originals are removed while there are still userland references
17271686
* this will cause memory corruption. */
17281687
static void dom_xinclude_strip_fallback_references(const xmlNode *basep)
17291688
{
17301689
xmlNodePtr current = basep->children;
17311690

1691+
/* TODO: try to improve loop search performance */
17321692
while (current) {
1733-
if (current->type == XML_ELEMENT_NODE && current->ns != NULL && current->_private != NULL
1734-
&& xmlStrEqual(current->name, XINCLUDE_FALLBACK)
1693+
if (current->type == XML_ELEMENT_NODE
1694+
&& current->ns != NULL
1695+
&& xmlStrEqual(current->name, XINCLUDE_NODE)
17351696
&& (xmlStrEqual(current->ns->href, XINCLUDE_NS) || xmlStrEqual(current->ns->href, XINCLUDE_OLD_NS))) {
17361697
dom_xinclude_strip_references(current);
17371698
}
@@ -1744,22 +1705,11 @@ static int dom_perform_xinclude(xmlDocPtr docp, dom_object *intern, zend_long fl
17441705
{
17451706
dom_xinclude_strip_fallback_references((const xmlNode *) docp);
17461707

1708+
flags |= XML_PARSE_NOXINCNODE;
17471709
PHP_LIBXML_SANITIZE_GLOBALS(xinclude);
17481710
int err = xmlXIncludeProcessFlags(docp, (int)flags);
17491711
PHP_LIBXML_RESTORE_GLOBALS(xinclude);
17501712

1751-
/* XML_XINCLUDE_START and XML_XINCLUDE_END nodes need to be removed as these
1752-
are added via xmlXIncludeProcess to mark beginning and ending of xincluded document
1753-
but are not wanted in resulting document - must be done even if err as it could fail after
1754-
having processed some xincludes */
1755-
xmlNodePtr root = docp->children;
1756-
while (root && root->type != XML_ELEMENT_NODE && root->type != XML_XINCLUDE_START) {
1757-
root = root->next;
1758-
}
1759-
if (root) {
1760-
php_dom_remove_xinclude_nodes(root);
1761-
}
1762-
17631713
php_libxml_invalidate_node_list_cache(intern->document);
17641714

17651715
return err;

ext/dom/tests/gh17847.phpt

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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+
foreach ($garbage as $node) {
31+
try {
32+
var_dump($node->localName);
33+
} catch (DOMException $e) {
34+
echo $e->getMessage(), "\n";
35+
}
36+
}
37+
?>
38+
--EXPECT--
39+
Invalid State Error
40+
Invalid State Error
41+
Invalid State Error

0 commit comments

Comments
 (0)