diff --git a/NEWS b/NEWS
index 36c49a8635a..08ad143e4e9 100644
--- a/NEWS
+++ b/NEWS
@@ -56,6 +56,7 @@ PHP NEWS
. Fixed bug #79701 (getElementById does not correctly work with duplicate
definitions). (nielsdos)
. Implemented "New ext-dom features in PHP 8.4" RFC. (nielsdos)
+ . Fixed GH-14698 (segfault on DOM node dereference). (David Carlier)
- Fileinfo:
. Update to libmagic 5.45. (nielsdos)
diff --git a/ext/dom/tests/gh14698.phpt b/ext/dom/tests/gh14698.phpt
new file mode 100644
index 00000000000..b4d61b5daa6
--- /dev/null
+++ b/ext/dom/tests/gh14698.phpt
@@ -0,0 +1,22 @@
+--TEST--
+GH-14698 crash on DOM node dereference
+--EXTENSIONS--
+dom
+--CREDITS--
+YuanchengJiang
+--FILE--
+loadHTML('xx');
+ $html = simplexml_import_dom($dom);
+ foreach ($html->body->span as $obj) {
+ }
+ $script1_dataflow = $html;
+ $array = ['foo'];
+ foreach ($array as $key => &$value) {
+ unset($script1_dataflow[$key]);
+ }
+ echo "DONE";
+?>
+--EXPECTF--
+DONE
diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c
index 8a6c5dec88a..26e0c289183 100644
--- a/ext/libxml/libxml.c
+++ b/ext/libxml/libxml.c
@@ -329,9 +329,13 @@ PHP_LIBXML_API void php_libxml_node_free_list(xmlNodePtr 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. */
php_libxml_node_ptr *ptr = curnode->_private;
- php_libxml_node_object *obj = ptr->_private;
- if (!obj->document || obj->document->class_type < PHP_LIBXML_CLASS_MODERN) {
- xmlReconciliateNs(curnode->doc, curnode);
+
+ /* Checking in case it runs out of reference */
+ if (ptr->_private) {
+ php_libxml_node_object *obj = ptr->_private;
+ if (!obj->document || obj->document->class_type < PHP_LIBXML_CLASS_MODERN) {
+ xmlReconciliateNs(curnode->doc, curnode);
+ }
}
}
/* Skip freeing */