Commit 1a5a12e0f63 for php
commit 1a5a12e0f632e62f520f12eda36b694283ef24e0
Author: David Carlier <devnexen@gmail.com>
Date: Sat Nov 7 18:33:26 2026 +0000
Fix GH-25268: clone of Dom\Notation NULL dereference
dom_clone_node() cannot copy notation nodes, so cloning a notation attached
to a document left the clone without a document proxy and dereferenced it.
Throw an Invalid State Error for modern DOM nodes when the clone fails
instead of returning a half-initialized object.
Close GH-25276
diff --git a/NEWS b/NEWS
index 668b5898c62..2c23cc8aa95 100644
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,10 @@ PHP NEWS
- Curl:
. Fixed curl_reset() retaining CURLOPT_PRIVATE after reset. (lacatoire)
+- DOM:
+ . Fixed bug GH-25268 (NULL php_libxml_ref_obj dereference in
+ dom_objects_store_clone_obj when cloning Dom\Notation). (David Carlier)
+
- MBString:
. Fixed stack overflow in mb_check_encoding() and mb_convert_encoding() with
deeply nested arrays. (Eyüp Can Akman)
diff --git a/ext/dom/node.c b/ext/dom/node.c
index 49e400ef530..867d166edb4 100644
--- a/ext/dom/node.c
+++ b/ext/dom/node.c
@@ -1513,6 +1513,10 @@ PHP_METHOD(DOMNode, cloneNode)
if (clone_document && private_data != NULL) {
php_dom_private_data_destroy(private_data);
}
+ if (instanceof_function(intern->std.ce, dom_modern_node_class_entry)) {
+ php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
+ RETURN_THROWS();
+ }
RETURN_FALSE;
}
diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c
index 445eacdfa19..ed300185eec 100644
--- a/ext/dom/php_dom.c
+++ b/ext/dom/php_dom.c
@@ -663,9 +663,11 @@ static zend_object *dom_objects_store_clone_obj(zend_object *zobject) /* {{{ */
xmlNodePtr cloned_node = dom_clone_node(php_dom_ns_mapper_from_private(private_data), node, node->doc, true);
if (cloned_node != NULL) {
dom_update_refcount_after_clone(intern, node, clone, cloned_node);
- }
- if (private_data != NULL) {
- clone->document->private_data = php_dom_libxml_private_data_header(private_data);
+ if (private_data != NULL) {
+ clone->document->private_data = php_dom_libxml_private_data_header(private_data);
+ }
+ } else if (private_data != NULL && (node->type == XML_DOCUMENT_NODE || node->type == XML_HTML_DOCUMENT_NODE)) {
+ php_dom_private_data_destroy(private_data);
}
}
}
diff --git a/ext/dom/tests/modern/xml/gh25268_declarations.phpt b/ext/dom/tests/modern/xml/gh25268_declarations.phpt
new file mode 100644
index 00000000000..ce814f2f7c4
--- /dev/null
+++ b/ext/dom/tests/modern/xml/gh25268_declarations.phpt
@@ -0,0 +1,27 @@
+--TEST--
+GH-25268 (NULL php_libxml_ref_obj dereference in dom_objects_store_clone_obj when cloning DTD declarations)
+--EXTENSIONS--
+dom
+--FILE--
+<?php
+$dom = Dom\XMLDocument::createFromString('<!DOCTYPE root [<!ELEMENT root EMPTY><!ENTITY e "x">]><root/>');
+
+foreach ([$dom->doctype->entities->getNamedItem("e"), $dom->doctype->childNodes[0]] as $decl) {
+ $clone = clone $decl;
+ try {
+ var_dump($clone->nodeName);
+ } catch (DOMException $e) {
+ echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+ }
+ try {
+ var_dump($decl->cloneNode());
+ } catch (DOMException $e) {
+ echo $e::class, ': ', $e->getMessage(), PHP_EOL;
+ }
+}
+?>
+--EXPECT--
+DOMException: Invalid State Error
+DOMException: Invalid State Error
+DOMException: Invalid State Error
+DOMException: Invalid State Error