[php-src] master: ext/dom: use-after-free via DOMNameSpaceNode after DOMDocument::xinclude().

David Carlier <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: David Carlier (devnexen)
Date: 2026-08-16T15:30:23+01:00

Commit: https://github.com/php/php-src/commit/e5623ea1e93030b6628b4374f455bc5a62eeebbb
Raw diff: https://github.com/php/php-src/commit/e5623ea1e93030b6628b4374f455bc5a62eeebbb.diff

ext/dom: use-after-free via DOMNameSpaceNode after DOMDocument::xinclude().

Fix GH-22624

Close GH-22627

Changed paths:
  A  ext/dom/tests/gh22624.phpt
  M  NEWS
  M  ext/dom/node.c


Diff:

diff --git a/NEWS b/NEWS
index 1bba6a7b6c22..9977ea3b3409 100644
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,10 @@ PHP                                                                        NEWS
     class constants via OBJ->prop = $val). (Khaled Alam)
   . Reverted GH-22833, which attempted to fix bug GH-18985. (ilutov)
 
+- DOM:
+  . Fixed bug GH-22624 (use-after-free via DOMNameSpaceNode after
+    DOMDocument::xinclude()). (David Carlier)
+
 - PDO_PGSQL:
   . Fixed several lazy fetch (PDO::ATTR_PREFETCH => 0) defects: an infinite
     loop when cleaning up a fetch left in a COPY, a use-after-free when a
diff --git a/ext/dom/node.c b/ext/dom/node.c
index cc063df66960..362000792f1a 100644
--- a/ext/dom/node.c
+++ b/ext/dom/node.c
@@ -245,11 +245,21 @@ zend_result dom_node_node_type_read(dom_object *obj, zval *retval)
 
 /* }}} */
 
+static xmlNodePtr dom_node_get_parent(dom_object *obj, xmlNodePtr nodep)
+{
+	if (nodep->type == XML_NAMESPACE_DECL) {
+		dom_object_namespace_node *ns = php_dom_namespace_node_obj_from_obj(&obj->std);
+		return ns->parent_intern ? dom_object_get_node(ns->parent_intern) : NULL;
+	}
+	return nodep->parent;
+}
+
+
 static zend_result dom_node_parent_get(dom_object *obj, zval *retval, bool only_element)
 {
 	DOM_PROP_NODE(xmlNodePtr, nodep, obj);
 
-	xmlNodePtr nodeparent = nodep->parent;
+	xmlNodePtr nodeparent = dom_node_get_parent(obj, nodep);
 	if (!nodeparent || (only_element && nodeparent->type != XML_ELEMENT_NODE)) {
 		ZVAL_NULL(retval);
 		return SUCCESS;
@@ -457,7 +467,12 @@ URL: https://dom.spec.whatwg.org/#dom-node-isconnected
 zend_result dom_node_is_connected_read(dom_object *obj, zval *retval)
 {
 	DOM_PROP_NODE(xmlNodePtr, nodep, obj);
-	ZVAL_BOOL(retval, php_dom_is_node_connected(nodep));
+	if (nodep->type == XML_NAMESPACE_DECL) {
+		xmlNodePtr parent = dom_node_get_parent(obj, nodep);
+		ZVAL_BOOL(retval, parent && php_dom_is_node_connected(parent));
+	} else {
+		ZVAL_BOOL(retval, php_dom_is_node_connected(nodep));
+	}
 	return SUCCESS;
 }
 /* }}} */
diff --git a/ext/dom/tests/gh22624.phpt b/ext/dom/tests/gh22624.phpt
new file mode 100644
index 000000000000..cab2ada76cfc
--- /dev/null
+++ b/ext/dom/tests/gh22624.phpt
@@ -0,0 +1,41 @@
+--TEST--
+GH-22624 (Use-after-free via DOMNameSpaceNode after DOMDocument::xinclude())
+--CREDITS--
+ExPatch-LLC
+--EXTENSIONS--
+dom
+--SKIPIF--
+<?php
+if (!function_exists('libxml_set_external_entity_loader')) die('skip xinclude not available');
+?>
+--FILE--
+<?php
+$included = __DIR__ . '/gh22624_included.xml';
+file_put_contents($included, '<?xml version="1.0"?><included/>');
+$href = 'file:///' . ltrim(str_replace('\\', '/', $included), '/');
+
+$doc = new DOMDocument();
+$doc->loadXML('<?xml version="1.0"?>
+<root xmlns:xi="http://www.w3.org/2001/XInclude">
+  <xi:include href="' . $href . '" xmlns:local="urn:test"/>
+</root>');
+
+$xpath = new DOMXPath($doc);
+$xpath->registerNamespace('xi', 'http://www.w3.org/2001/XInclude');
+$xi = $xpath->query('//xi:include')->item(0);
+$ns = $xpath->query('namespace::local', $xi)->item(0); // DOMNameSpaceNode
+
+$doc->xinclude(); // frees the xi:include element
+
+var_dump($ns->parentNode);
+var_dump($ns->parentElement);
+var_dump($ns->isConnected);
+?>
+--CLEAN--
+<?php
+@unlink(__DIR__ . '/gh22624_included.xml');
+?>
+--EXPECT--
+NULL
+NULL
+bool(false)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.