mirror of
https://github.com/php/php-src.git
synced 2024-09-21 09:57:23 +00:00
Fix GH-14702: DOMDocument::xinclude() crash
The xinclude code from libxml removes the fallback node, but the fallback node is still reference via $fallback. The solution is to detach the nodes that are going to be removed in advance. Closes GH-14704.
This commit is contained in:
parent
056bec72f4
commit
42908f9f68
3
NEWS
3
NEWS
@ -9,6 +9,9 @@ PHP NEWS
|
||||
. Fixed bug GH-14590 (Memory leak in FPM test gh13563-conf-bool-env.phpt.
|
||||
(nielsdos)
|
||||
|
||||
- Dom:
|
||||
. Fixed bug GH-14702 (DOMDocument::xinclude() crash). (nielsdos)
|
||||
|
||||
- Phar:
|
||||
. Fixed bug GH-14603 (null string from zip entry).
|
||||
(David Carlier)
|
||||
|
@ -1566,6 +1566,58 @@ static void php_dom_remove_xinclude_nodes(xmlNodePtr cur) /* {{{ */
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* Backported from master branch xml_common.h */
|
||||
static zend_always_inline xmlNodePtr php_dom_next_in_tree_order(const xmlNode *nodep, const xmlNode *basep)
|
||||
{
|
||||
if (nodep->type == XML_ELEMENT_NODE && nodep->children) {
|
||||
return nodep->children;
|
||||
}
|
||||
|
||||
if (nodep->next) {
|
||||
return nodep->next;
|
||||
} else {
|
||||
/* Go upwards, until we find a parent node with a next sibling, or until we hit the base. */
|
||||
do {
|
||||
nodep = nodep->parent;
|
||||
if (nodep == basep) {
|
||||
return NULL;
|
||||
}
|
||||
} while (nodep->next == NULL);
|
||||
return nodep->next;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* {{{ Substitutues xincludes in a DomDocument */
|
||||
PHP_METHOD(DOMDocument, xinclude)
|
||||
{
|
||||
@ -1588,6 +1640,8 @@ PHP_METHOD(DOMDocument, xinclude)
|
||||
|
||||
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
|
||||
|
||||
dom_xinclude_strip_fallback_references((const xmlNode *) docp);
|
||||
|
||||
PHP_LIBXML_SANITIZE_GLOBALS(xinclude);
|
||||
err = xmlXIncludeProcessFlags(docp, (int)flags);
|
||||
PHP_LIBXML_RESTORE_GLOBALS(xinclude);
|
||||
|
66
ext/dom/tests/gh14702.phpt
Normal file
66
ext/dom/tests/gh14702.phpt
Normal file
@ -0,0 +1,66 @@
|
||||
--TEST--
|
||||
GH-14702 (DOMDocument::xinclude() crash)
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
$doc = new DOMDocument();
|
||||
$doc->loadXML(<<<XML
|
||||
<?xml version="1.0"?>
|
||||
<root>
|
||||
<child/>
|
||||
<include href="foo" xmlns="http://www.w3.org/2001/XInclude">
|
||||
<fallback/>
|
||||
</include>
|
||||
<keep/>
|
||||
</root>
|
||||
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($child1, $child2, $fallback, $keep->nodeName);
|
||||
|
||||
$keep->textContent = 'still works';
|
||||
echo $doc->saveXML();
|
||||
?>
|
||||
--EXPECT--
|
||||
int(2)
|
||||
<?xml version="1.0"?>
|
||||
<root>
|
||||
<fallback-child1/><fallback-child2/>
|
||||
|
||||
<keep/>
|
||||
</root>
|
||||
object(DOMElement)#4 (1) {
|
||||
["schemaTypeInfo"]=>
|
||||
NULL
|
||||
}
|
||||
object(DOMElement)#5 (1) {
|
||||
["schemaTypeInfo"]=>
|
||||
NULL
|
||||
}
|
||||
object(DOMElement)#3 (1) {
|
||||
["schemaTypeInfo"]=>
|
||||
NULL
|
||||
}
|
||||
string(4) "keep"
|
||||
<?xml version="1.0"?>
|
||||
<root>
|
||||
<fallback-child1/><fallback-child2/>
|
||||
|
||||
<keep>still works</keep>
|
||||
</root>
|
Loading…
Reference in New Issue
Block a user