Skip to content

Commit 55d759d

Browse files
committed
Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4: Fix DOMNameSpaceNode clone UAF after xinclude
2 parents bf22afd + d09e5e2 commit 55d759d

3 files changed

Lines changed: 56 additions & 6 deletions

File tree

‎NEWS‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ PHP NEWS
66
. Fixed bug GH-15375 (Nested "yield from" skips items after a valid() or
77
next() call on the inner generator). (iliaal)
88

9+
- DOM:
10+
. Fixed a use-after-free when cloning a DOMNameSpaceNode after
11+
DOMDocument::xinclude(). (iliaal)
12+
913
- Opcache:
1014
. Fixed opcache.protect_memory race under ZTS. (realFlowControl)
1115

‎ext/dom/php_dom.c‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ static HashTable dom_xpath_prop_handlers;
147147

148148
static zend_object *dom_objects_namespace_node_new(zend_class_entry *class_type);
149149
static void dom_object_namespace_node_free_storage(zend_object *object);
150-
static xmlNodePtr php_dom_create_fake_namespace_decl_node_ptr(xmlNodePtr nodep, xmlNsPtr original);
150+
static xmlNodePtr php_dom_create_fake_namespace_decl_node_ptr(xmlNodePtr nodep, xmlNsPtr original, xmlDocPtr fallback_doc);
151151

152152
typedef zend_result (*dom_read_t)(dom_object *obj, zval *retval);
153153
typedef zend_result (*dom_write_t)(dom_object *obj, zval *newval);
@@ -722,7 +722,8 @@ static zend_object *dom_object_namespace_node_clone_obj(zend_object *zobject)
722722
xmlNodePtr original_node = dom_object_get_node(&intern->dom);
723723
if (original_node != NULL) {
724724
ZEND_ASSERT(original_node->type == XML_NAMESPACE_DECL);
725-
xmlNodePtr cloned_node = php_dom_create_fake_namespace_decl_node_ptr(original_node->parent, original_node->ns);
725+
xmlNodePtr parent = intern->parent_intern ? dom_object_get_node(intern->parent_intern) : NULL;
726+
xmlNodePtr cloned_node = php_dom_create_fake_namespace_decl_node_ptr(parent, original_node->ns, original_node->doc);
726727
dom_update_refcount_after_clone(&intern->dom, original_node, &clone_intern->dom, cloned_node);
727728
}
728729

@@ -2255,15 +2256,16 @@ xmlNsPtr dom_get_nsdecl(xmlNode *node, xmlChar *localName) {
22552256
}
22562257
/* }}} end dom_get_nsdecl */
22572258

2258-
static xmlNodePtr php_dom_create_fake_namespace_decl_node_ptr(xmlNodePtr nodep, xmlNsPtr original)
2259+
static xmlNodePtr php_dom_create_fake_namespace_decl_node_ptr(xmlNodePtr nodep, xmlNsPtr original, xmlDocPtr fallback_doc)
22592260
{
22602261
xmlNodePtr attrp;
2262+
xmlDocPtr doc = nodep ? nodep->doc : fallback_doc;
22612263
xmlNsPtr curns = xmlNewNs(NULL, original->href, NULL);
22622264
if (original->prefix) {
22632265
curns->prefix = xmlStrdup(original->prefix);
2264-
attrp = xmlNewDocNode(nodep->doc, NULL, BAD_CAST original->prefix, original->href);
2266+
attrp = xmlNewDocNode(doc, NULL, BAD_CAST original->prefix, original->href);
22652267
} else {
2266-
attrp = xmlNewDocNode(nodep->doc, NULL, BAD_CAST "xmlns", original->href);
2268+
attrp = xmlNewDocNode(doc, NULL, BAD_CAST "xmlns", original->href);
22672269
}
22682270
attrp->type = XML_NAMESPACE_DECL;
22692271
attrp->parent = nodep;
@@ -2274,7 +2276,7 @@ static xmlNodePtr php_dom_create_fake_namespace_decl_node_ptr(xmlNodePtr nodep,
22742276
/* Note: Assumes the additional lifetime was already added in the caller. */
22752277
xmlNodePtr php_dom_create_fake_namespace_decl(xmlNodePtr nodep, xmlNsPtr original, zval *return_value, dom_object *parent_intern)
22762278
{
2277-
xmlNodePtr attrp = php_dom_create_fake_namespace_decl_node_ptr(nodep, original);
2279+
xmlNodePtr attrp = php_dom_create_fake_namespace_decl_node_ptr(nodep, original, NULL);
22782280
php_dom_create_object(attrp, return_value, parent_intern);
22792281
/* This object must exist, because we just created an object for it via php_dom_create_object(). */
22802282
php_dom_namespace_node_obj_from_obj(Z_OBJ_P(return_value))->parent_intern = parent_intern;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
--TEST--
2+
DOMNameSpaceNode clone after xinclude does not use a dangling parent
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
$included = __DIR__ . '/dom_namespacenode_clone_xinclude_included.xml';
8+
file_put_contents($included, '<?xml version="1.0"?><included/>');
9+
$href = 'file:///' . ltrim(str_replace('\\', '/', $included), '/');
10+
11+
$doc = new DOMDocument();
12+
$doc->loadXML('<?xml version="1.0"?>
13+
<root xmlns:xi="http://www.w3.org/2001/XInclude">
14+
<xi:include href="' . $href . '" xmlns:local="urn:test"/>
15+
</root>');
16+
17+
$xpath = new DOMXPath($doc);
18+
$xpath->registerNamespace('xi', 'http://www.w3.org/2001/XInclude');
19+
$xi = $xpath->query('//xi:include')->item(0);
20+
$ns = $xpath->query('namespace::local', $xi)->item(0);
21+
22+
$live = clone $ns;
23+
echo "live clone: ", $live->nodeName, "\n";
24+
echo "live parent: ", $live->parentNode->nodeName, "\n";
25+
26+
$doc->xinclude();
27+
28+
$clone = clone $ns;
29+
echo "after xinclude: ", $clone->nodeName, "\n";
30+
var_dump($clone->parentNode);
31+
var_dump($clone->parentElement);
32+
var_dump($clone->isConnected);
33+
?>
34+
--CLEAN--
35+
<?php
36+
@unlink(__DIR__ . '/dom_namespacenode_clone_xinclude_included.xml');
37+
?>
38+
--EXPECT--
39+
live clone: xmlns:local
40+
live parent: xi:include
41+
after xinclude: xmlns:local
42+
NULL
43+
NULL
44+
bool(false)

0 commit comments

Comments
 (0)