[php-src] master: ext/dom: Fix Dom\DtdNamedNodeMap index overflow in dimension access (#22630)

Weilin Du via GitHub <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Weilin Du (LamentXU123)
Committer: GitHub (web-flow)
Pusher: LamentXU123
Date: 2026-07-08T20:10:26+08:00

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

ext/dom: Fix Dom\DtdNamedNodeMap index overflow in dimension access (#22630)

Dom\DtdNamedNodeMap dimension access could overflow the index when reading DTD
entities or notations. This commit fix it.

Changed paths:
  A  ext/dom/tests/modern/xml/DtdNamedNodeMap_dimension_index_overflow_64.phpt
  M  NEWS
  M  ext/dom/dom_iterators.c
  M  ext/dom/php_dom.h


Diff:

diff --git a/NEWS b/NEWS
index 8971e7d317a3..cd396479e3e2 100644
--- a/NEWS
+++ b/NEWS
@@ -20,6 +20,9 @@ PHP                                                                        NEWS
 - DOM:
   . Fixed bug GH-22570 (Stack overflow when serializing a deeply nested
     Dom\XMLDocument). (iliaal)
+  . Fixed Dom\DtdNamedNodeMap integer dimension access so negative indexes
+    return NULL and indexes outside the int range throw ValueError instead of
+    returning the first entity or notation. (Weilin Du)
 
 - Exif:
   . Fixed bug GH-11020 (exif_read_data() emits a spurious "Illegal IFD size"
diff --git a/ext/dom/dom_iterators.c b/ext/dom/dom_iterators.c
index f97a9ec825d5..7566dd8b61a8 100644
--- a/ext/dom/dom_iterators.c
+++ b/ext/dom/dom_iterators.c
@@ -79,14 +79,22 @@ void dom_free_notation(xmlEntityPtr entity) /* {{{ */
 }
 /* }}} */
 
-xmlNodePtr php_dom_libxml_hash_iter(xmlHashTable *ht, int index)
+xmlNodePtr php_dom_libxml_hash_iter(xmlHashTable *ht, zend_long index)
 {
 	int htsize;
 
+	if (index < 0) {
+		return NULL;
+	}
+	if (UNEXPECTED(ZEND_LONG_INT_OVFL(index))) {
+		zend_value_error("must be between 0 and %d", INT_MAX);
+		return NULL;
+	}
+
 	if ((htsize = xmlHashSize(ht)) > 0 && index < htsize) {
 		nodeIterator iter;
 		iter.cur = 0;
-		iter.index = index;
+		iter.index = (int) index;
 		iter.node = NULL;
 		xmlHashScan(ht, itemHashScanner, &iter);
 		return iter.node;
diff --git a/ext/dom/php_dom.h b/ext/dom/php_dom.h
index 1b19aeee3baa..6f4b5075b659 100644
--- a/ext/dom/php_dom.h
+++ b/ext/dom/php_dom.h
@@ -127,7 +127,7 @@ bool dom_node_is_read_only(const xmlNode *node);
 bool dom_node_children_valid(const xmlNode *node);
 xmlNodePtr create_notation(xmlDtdPtr parent_dtd, const xmlChar *name, const xmlChar *ExternalID, const xmlChar *SystemID);
 void dom_free_notation(xmlEntityPtr entity);
-xmlNode *php_dom_libxml_hash_iter(xmlHashTable *ht, int index);
+xmlNode *php_dom_libxml_hash_iter(xmlHashTable *ht, zend_long index);
 zend_object_iterator *php_dom_get_iterator(zend_class_entry *ce, zval *object, int by_ref);
 void dom_set_doc_classmap(php_libxml_ref_obj *document, zend_class_entry *basece, zend_class_entry *ce);
 xmlNodePtr php_dom_create_fake_namespace_decl(xmlNodePtr nodep, xmlNsPtr original, zval *return_value, dom_object *parent_intern);
diff --git a/ext/dom/tests/modern/xml/DtdNamedNodeMap_dimension_index_overflow_64.phpt b/ext/dom/tests/modern/xml/DtdNamedNodeMap_dimension_index_overflow_64.phpt
new file mode 100644
index 000000000000..cc2d7a06a2ca
--- /dev/null
+++ b/ext/dom/tests/modern/xml/DtdNamedNodeMap_dimension_index_overflow_64.phpt
@@ -0,0 +1,41 @@
+--TEST--
+Dom\DtdNamedNodeMap dimension access handles invalid integer indexes
+--EXTENSIONS--
+dom
+--SKIPIF--
+<?php
+if (PHP_INT_SIZE == 4) {
+    die("skip this test is for 64bit platform only");
+}
+?>
+--FILE--
+<?php
+$doc = Dom\XMLDocument::createFromString(<<<XML
+<!DOCTYPE root [
+<!ENTITY e "entity">
+<!NOTATION n SYSTEM "notation">
+]>
+<root/>
+XML);
+
+$overflow = 4294967296;
+
+function dump_access(Closure $callback): void {
+    try {
+        var_dump($callback()?->nodeName);
+    } catch (ValueError $e) {
+        echo $e->getMessage(), PHP_EOL;
+    }
+}
+
+dump_access(fn() => $doc->doctype->entities[-1]);
+dump_access(fn() => $doc->doctype->entities[$overflow]);
+
+dump_access(fn() => $doc->doctype->notations[-1]);
+dump_access(fn() => $doc->doctype->notations[$overflow]);
+?>
+--EXPECT--
+NULL
+must be between 0 and 2147483647
+NULL
+must be between 0 and 2147483647
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.