[PHP-CVS] [php-src] master: Merge branch 'PHP-8.5'

[email protected] (Ilia Alshanetsky)
Newsgroups php.cvs
Message-ID <[email protected]>
Author: Ilia Alshanetsky (iliaal)
Date: 2026-08-17T06:53:52-04:00

Commit: https://github.com/php/php-src/commit/d72a9106c5a2355478b95a178d3095909e660e99
Raw diff: https://github.com/php/php-src/commit/d72a9106c5a2355478b95a178d3095909e660e99.diff

Merge branch 'PHP-8.5'

* PHP-8.5:
  Fix DOMXPath crash wrapping a foreign node after a nodeset callback

Changed paths:
  A  ext/dom/tests/xpath_php_function_foreign_doc.phpt
  M  NEWS
  M  ext/dom/xpath.c


Diff:

diff --git a/NEWS b/NEWS
index 8e7950150646..a0c4359f4a09 100644
--- a/NEWS
+++ b/NEWS
@@ -19,6 +19,8 @@ PHP                                                                        NEWS
     DOMDocument::xinclude()). (David Carlier)
   . Fixed a use-after-free when cloning a DOMNameSpaceNode after
     DOMDocument::xinclude(). (iliaal)
+  . Fixed a crash in DOMXPath when a php:function callback receives a nodeset
+    and a later callback returns a node from another document. (iliaal)
 
 - PDO_PGSQL:
   . Fixed several lazy fetch (PDO::ATTR_PREFETCH => 0) defects: an infinite
diff --git a/ext/dom/tests/xpath_php_function_foreign_doc.phpt b/ext/dom/tests/xpath_php_function_foreign_doc.phpt
new file mode 100644
index 000000000000..4e4b2b2c2822
--- /dev/null
+++ b/ext/dom/tests/xpath_php_function_foreign_doc.phpt
@@ -0,0 +1,40 @@
+--TEST--
+DOMXPath: php:function nodeset args plus a foreign-document return must not treat arrays as DOM objects
+--EXTENSIONS--
+dom
+--FILE--
+<?php
+$doc1 = new DOMDocument();
+$doc1->loadXML('<root><a>1</a></root>');
+$doc2 = new DOMDocument();
+$doc2->loadXML('<root><b>2</b></root>');
+
+$xp = new DOMXPath($doc1);
+$xp->registerNamespace('php', 'http://php.net/xpath');
+$xp->registerPhpFunctions();
+
+function uses_nodeset($nodes) {
+    return true;
+}
+
+function foreign() {
+    global $doc2;
+    return $doc2->documentElement;
+}
+
+$xp->query('//a[php:function("uses_nodeset", //a)]');
+$res = $xp->query('php:function("foreign")');
+echo "count: ";
+var_dump($res->length);
+$n = $res->item(0);
+echo "name: ";
+var_dump($n->nodeName);
+echo "owner is doc2: ";
+var_dump($n->ownerDocument === $doc2);
+echo "done\n";
+?>
+--EXPECT--
+count: int(1)
+name: string(4) "root"
+owner is doc2: bool(true)
+done
diff --git a/ext/dom/xpath.c b/ext/dom/xpath.c
index dc9e1b852fae..a35d343b50d9 100644
--- a/ext/dom/xpath.c
+++ b/ext/dom/xpath.c
@@ -33,6 +33,25 @@
 
 #ifdef LIBXML_XPATH_ENABLED
 
+static dom_object *dom_xpath_intern_from_entry(zval *entry, xmlDocPtr doc)
+{
+	if (Z_TYPE_P(entry) == IS_OBJECT) {
+		dom_object *obj = Z_DOMOBJ_P(entry);
+		if (obj->document && obj->document->ptr == doc) {
+			return obj;
+		}
+	} else if (Z_TYPE_P(entry) == IS_ARRAY) {
+		zval *inner;
+		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(entry), inner) {
+			dom_object *obj = dom_xpath_intern_from_entry(inner, doc);
+			if (obj) {
+				return obj;
+			}
+		} ZEND_HASH_FOREACH_END();
+	}
+	return NULL;
+}
+
 static dom_object *dom_xpath_intern_for_doc(dom_xpath_object *xpath_obj, xmlDocPtr doc)
 {
 	if (xpath_obj->dom.document && xpath_obj->dom.document->ptr == doc) {
@@ -42,8 +61,8 @@ static dom_object *dom_xpath_intern_for_doc(dom_xpath_object *xpath_obj, xmlDocP
 	if (node_list) {
 		zval *entry;
 		ZEND_HASH_PACKED_FOREACH_VAL(node_list, entry) {
-			dom_object *obj = Z_DOMOBJ_P(entry);
-			if (obj->document && obj->document->ptr == doc) {
+			dom_object *obj = dom_xpath_intern_from_entry(entry, doc);
+			if (obj) {
 				return obj;
 			}
 		} ZEND_HASH_FOREACH_END();
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.