Skip to content

Commit 0a1f51d

Browse files
committed
Merge branch 'PHP-8.2' into PHP-8.3
* PHP-8.2: Fix GH-14702: DOMDocument::xinclude() crash
2 parents 4137190 + 42908f9 commit 0a1f51d

File tree

3 files changed

+123
-0
lines changed

3 files changed

+123
-0
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ PHP NEWS
99
. Fixed bug GH-14590 (Memory leak in FPM test gh13563-conf-bool-env.phpt.
1010
(nielsdos)
1111

12+
- Dom:
13+
. Fixed bug GH-14702 (DOMDocument::xinclude() crash). (nielsdos)
14+
1215
- Phar:
1316
. Fixed bug GH-14603 (null string from zip entry).
1417
(David Carlier)

ext/dom/document.c

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1625,6 +1625,58 @@ static void php_dom_remove_xinclude_nodes(xmlNodePtr cur) /* {{{ */
16251625
}
16261626
/* }}} */
16271627

1628+
/* Backported from master branch xml_common.h */
1629+
static zend_always_inline xmlNodePtr php_dom_next_in_tree_order(const xmlNode *nodep, const xmlNode *basep)
1630+
{
1631+
if (nodep->type == XML_ELEMENT_NODE && nodep->children) {
1632+
return nodep->children;
1633+
}
1634+
1635+
if (nodep->next) {
1636+
return nodep->next;
1637+
} else {
1638+
/* Go upwards, until we find a parent node with a next sibling, or until we hit the base. */
1639+
do {
1640+
nodep = nodep->parent;
1641+
if (nodep == basep) {
1642+
return NULL;
1643+
}
1644+
} while (nodep->next == NULL);
1645+
return nodep->next;
1646+
}
1647+
}
1648+
1649+
static void dom_xinclude_strip_references(xmlNodePtr basep)
1650+
{
1651+
php_libxml_node_free_resource(basep);
1652+
1653+
xmlNodePtr current = basep->children;
1654+
1655+
while (current) {
1656+
php_libxml_node_free_resource(current);
1657+
current = php_dom_next_in_tree_order(current, basep);
1658+
}
1659+
}
1660+
1661+
/* See GH-14702.
1662+
* We have to remove userland references to xinclude fallback nodes because libxml2 will make clones of these
1663+
* and remove the original nodes. If the originals are removed while there are still userland references
1664+
* this will cause memory corruption. */
1665+
static void dom_xinclude_strip_fallback_references(const xmlNode *basep)
1666+
{
1667+
xmlNodePtr current = basep->children;
1668+
1669+
while (current) {
1670+
if (current->type == XML_ELEMENT_NODE && current->ns != NULL && current->_private != NULL
1671+
&& xmlStrEqual(current->name, XINCLUDE_FALLBACK)
1672+
&& (xmlStrEqual(current->ns->href, XINCLUDE_NS) || xmlStrEqual(current->ns->href, XINCLUDE_OLD_NS))) {
1673+
dom_xinclude_strip_references(current);
1674+
}
1675+
1676+
current = php_dom_next_in_tree_order(current, basep);
1677+
}
1678+
}
1679+
16281680
/* {{{ Substitutues xincludes in a DomDocument */
16291681
PHP_METHOD(DOMDocument, xinclude)
16301682
{
@@ -1647,6 +1699,8 @@ PHP_METHOD(DOMDocument, xinclude)
16471699

16481700
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
16491701

1702+
dom_xinclude_strip_fallback_references((const xmlNode *) docp);
1703+
16501704
PHP_LIBXML_SANITIZE_GLOBALS(xinclude);
16511705
err = xmlXIncludeProcessFlags(docp, (int)flags);
16521706
PHP_LIBXML_RESTORE_GLOBALS(xinclude);

ext/dom/tests/gh14702.phpt

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
--TEST--
2+
GH-14702 (DOMDocument::xinclude() crash)
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
$doc = new DOMDocument();
8+
$doc->loadXML(<<<XML
9+
<?xml version="1.0"?>
10+
<root>
11+
<child/>
12+
<include href="foo" xmlns="http://www.w3.org/2001/XInclude">
13+
<fallback/>
14+
</include>
15+
<keep/>
16+
</root>
17+
XML);
18+
$xi = $doc->createElementNS('http://www.w3.org/2001/XInclude', 'xi:include');
19+
$xi->setAttribute('href', 'nonexistent');
20+
21+
$fallback = $doc->createElementNS('http://www.w3.org/2001/XInclude', 'xi:fallback');
22+
$xi->appendChild($fallback);
23+
$child1 = $fallback->appendChild($doc->createElement('fallback-child1'));
24+
$child2 = $fallback->appendChild($doc->createElement('fallback-child2'));
25+
26+
$xpath = new DOMXPath($doc);
27+
$toReplace = $xpath->query('//child')->item(0);
28+
$toReplace->parentNode->replaceChild($xi, $toReplace);
29+
30+
$keep = $doc->documentElement->lastElementChild;
31+
32+
var_dump(@$doc->xinclude());
33+
echo $doc->saveXML();
34+
35+
var_dump($child1, $child2, $fallback, $keep->nodeName);
36+
37+
$keep->textContent = 'still works';
38+
echo $doc->saveXML();
39+
?>
40+
--EXPECT--
41+
int(2)
42+
<?xml version="1.0"?>
43+
<root>
44+
<fallback-child1/><fallback-child2/>
45+
46+
<keep/>
47+
</root>
48+
object(DOMElement)#4 (1) {
49+
["schemaTypeInfo"]=>
50+
NULL
51+
}
52+
object(DOMElement)#5 (1) {
53+
["schemaTypeInfo"]=>
54+
NULL
55+
}
56+
object(DOMElement)#3 (1) {
57+
["schemaTypeInfo"]=>
58+
NULL
59+
}
60+
string(4) "keep"
61+
<?xml version="1.0"?>
62+
<root>
63+
<fallback-child1/><fallback-child2/>
64+
65+
<keep>still works</keep>
66+
</root>

0 commit comments

Comments
 (0)