Commit ff480bce5c4 for php.net
commit ff480bce5c44e3bfe62bec6a689d62d5cdc20793
Author: Weilin Du <weilindu@php.net>
Date: Wed Jul 8 20:10:26 2026 +0800
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.
diff --git a/NEWS b/NEWS
index 8971e7d317a..cd396479e3e 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 f97a9ec825d..7566dd8b61a 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 1b19aeee3ba..6f4b5075b65 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 00000000000..cc2d7a06a2c
--- /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