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)