From 22bf64b901217cb3033acb3434370e89006c4029 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 7 Jul 2026 13:15:55 +0100 Subject: [PATCH] ext/dom: use-after-free via DOMNameSpaceNode after DOMDocument::xinclude(). Fix #22624 --- ext/dom/node.c | 19 ++++++++++++++++-- ext/dom/tests/gh22624.phpt | 41 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 ext/dom/tests/gh22624.phpt diff --git a/ext/dom/node.c b/ext/dom/node.c index 4cfc6c5af0d2..5a33287f8191 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-- + +--FILE-- +'); +$href = 'file:///' . ltrim(str_replace('\\', '/', $included), '/'); + +$doc = new DOMDocument(); +$doc->loadXML(' + + +'); + +$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-- + +--EXPECT-- +NULL +NULL +bool(false)