diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c
index 5c903d2c9a228..eecbca4ed8908 100644
--- a/ext/libxml/libxml.c
+++ b/ext/libxml/libxml.c
@@ -331,13 +331,26 @@ PHP_LIBXML_API void php_libxml_node_free_list(xmlNodePtr node)
if (curnode->type == XML_ELEMENT_NODE) {
/* This ensures that namespace references in this subtree are defined within this subtree,
* otherwise a use-after-free would be possible when the original namespace holder gets freed. */
-#if 0
- xmlDOMWrapCtxt dummy_ctxt = {0};
- xmlDOMWrapReconcileNamespaces(&dummy_ctxt, curnode, /* options */ 0);
-#else
- /* See php_dom.c */
- xmlReconciliateNs(curnode->doc, curnode);
-#endif
+ if (LIBXML_VERSION < 21300 && UNEXPECTED(curnode->doc == NULL)) {
+ /* xmlReconciliateNs() in these versions just uses the document for xmlNewReconciledNs(),
+ * which can create an oldNs xml namespace declaration via xmlSearchNs() -> xmlTreeEnsureXMLDecl(). */
+ xmlDoc dummy;
+ memset(&dummy, 0, sizeof(dummy));
+ dummy.type = XML_DOCUMENT_NODE;
+ curnode->doc = &dummy;
+ xmlReconciliateNs(curnode->doc, curnode);
+ curnode->doc = NULL;
+
+ /* Append oldNs to current node's nsDef, which can be at most one node. */
+ if (dummy.oldNs) {
+ ZEND_ASSERT(dummy.oldNs->next == NULL);
+ xmlNsPtr old = curnode->nsDef;
+ curnode->nsDef = dummy.oldNs;
+ dummy.oldNs->next = old;
+ }
+ } else {
+ xmlReconciliateNs(curnode->doc, curnode);
+ }
}
/* Skip freeing */
curnode = next;
diff --git a/ext/xmlreader/tests/gh19098.phpt b/ext/xmlreader/tests/gh19098.phpt
new file mode 100644
index 0000000000000..13a6eda328f25
--- /dev/null
+++ b/ext/xmlreader/tests/gh19098.phpt
@@ -0,0 +1,44 @@
+--TEST--
+GH-19098 (libxml<2.13 segmentation fault caused by php_libxml_node_free)
+--EXTENSIONS--
+xmlreader
+dom
+--FILE--
+
+
+
+
+');
+
+$success = $xml_reader->next("sparql");
+
+$success = $xml_reader->read();
+$success = $xml_reader->next("results");
+
+while ($xml_reader->read()) {
+ if ($xml_reader->next("result")) {
+ $result_as_dom_node = $xml_reader->expand();
+ $child = $result_as_dom_node->firstChild;
+ unset($result_as_dom_node);
+ var_dump($child->namespaceURI);
+ foreach ($child->attributes as $attr) {
+ var_dump($attr->namespaceURI);
+ }
+ $doc = new DOMDocument;
+ $doc->adoptNode($child);
+ echo $doc->saveXML($child), "\n";
+ unset($child);
+ break;
+ }
+}
+
+?>
+--EXPECT--
+string(38) "http://www.w3.org/2005/sparql-results#"
+string(36) "http://www.w3.org/XML/1998/namespace"
+string(10) "urn:custom"
+NULL
+