[php-src] master: Merge branch 'PHP-8.5'
Weilin Du <[email protected]>
| Newsgroups | gmane.comp.php.cvs.general |
|---|---|
| Message-ID | <[email protected]> |
Author: Weilin Du (LamentXU123)
Date: 2026-08-08T16:57:16+08:00
Commit: https://github.com/php/php-src/commit/1423149fb6b083e935d1ab0e782bdfa8044d58cf
Raw diff: https://github.com/php/php-src/commit/1423149fb6b083e935d1ab0e782bdfa8044d58cf.diff
Merge branch 'PHP-8.5'
* PHP-8.5:
Fix GH-23116 and GH-23117: stack overflow when normalizing a deeply nested document (#23127)
Changed paths:
A ext/dom/tests/gh23116.phpt
A ext/dom/tests/modern/spec/gh23117.phpt
M NEWS
M ext/dom/php_dom.c
Diff:
diff --git a/NEWS b/NEWS
index c4b6eacb6069..fef83b8412c5 100644
--- a/NEWS
+++ b/NEWS
@@ -20,6 +20,12 @@ PHP NEWS
. Update timelib to 2026.02. (Derick, timwolla)
. Added Time\Duration. (timwolla, Derick)
+- DOM:
+ . Fixed bug GH-23116 (Stack overflow when normalizing a deeply nested
+ DOMDocument). (Lazizbek Ergashev)
+ . Fixed bug GH-23117 (Stack overflow when normalizing a deeply nested
+ Dom\XMLDocument). (Lazizbek Ergashev)
+
- GMP:
. Added optional $definitely_prime output parameter to gmp_prevprime().
(Weilin Du)
diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c
index eccbe4ed2984..467271943275 100644
--- a/ext/dom/php_dom.c
+++ b/ext/dom/php_dom.c
@@ -1910,9 +1910,26 @@ static void dom_merge_adjacent_exclusive_text_nodes(xmlNodePtr node)
}
}
+static zend_always_inline bool dom_normalize_check_stack_limit(void)
+{
+#ifdef ZEND_CHECK_STACK_LIMIT
+ if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
+ if (!EG(exception)) {
+ zend_throw_error(NULL, "Maximum call stack size reached. Infinite recursion?");
+ }
+ return true;
+ }
+#endif
+ return false;
+}
+
/* {{{ void php_dom_normalize_legacy(xmlNodePtr nodep) */
void php_dom_normalize_legacy(xmlNodePtr nodep)
{
+ if (UNEXPECTED(dom_normalize_check_stack_limit())) {
+ return;
+ }
+
xmlNodePtr child = nodep->children;
while(child != NULL) {
switch (child->type) {
@@ -1945,6 +1962,10 @@ void php_dom_normalize_legacy(xmlNodePtr nodep)
/* https://dom.spec.whatwg.org/#dom-node-normalize */
void php_dom_normalize_modern(xmlNodePtr this)
{
+ if (UNEXPECTED(dom_normalize_check_stack_limit())) {
+ return;
+ }
+
/* for each descendant exclusive Text node node of this: */
xmlNodePtr node = this->children;
while (node != NULL) {
diff --git a/ext/dom/tests/gh23116.phpt b/ext/dom/tests/gh23116.phpt
new file mode 100644
index 000000000000..20cabc4c535a
--- /dev/null
+++ b/ext/dom/tests/gh23116.phpt
@@ -0,0 +1,50 @@
+--TEST--
+GH-23116 (Stack overflow when normalizing a deeply nested DOMDocument)
+--EXTENSIONS--
+dom
+--SKIPIF--
+<?php
+if (ini_get('zend.max_allowed_stack_size') === false) {
+ die('skip No stack limit support');
+}
+if (getenv('SKIP_ASAN')) {
+ die('skip ASAN needs different stack limit setting due to more stack space usage');
+}
+?>
+--INI--
+zend.max_allowed_stack_size=256K
+--FILE--
+<?php
+// Build bottom-up so the insertion cycle-check stays O(1); top-down is O(n^2).
+$doc = new DOMDocument();
+$root = $doc->createElement('root');
+for ($s = 0; $s < 2; $s++) {
+ $node = $doc->createElement('a');
+ for ($i = 0; $i < 25000; $i++) {
+ $parent = $doc->createElement('a');
+ $parent->appendChild($node);
+ $node = $parent;
+ }
+ $root->appendChild($node);
+}
+$doc->appendChild($root);
+
+try {
+ $doc->normalize();
+} catch (\Error $e) {
+ echo "normalize: ", $e::class, ": ", $e->getMessage(), "\n";
+ var_dump($e->getPrevious());
+}
+
+try {
+ $doc->normalizeDocument();
+} catch (\Error $e) {
+ echo "normalizeDocument: ", $e::class, ": ", $e->getMessage(), "\n";
+ var_dump($e->getPrevious());
+}
+?>
+--EXPECT--
+normalize: Error: Maximum call stack size reached. Infinite recursion?
+NULL
+normalizeDocument: Error: Maximum call stack size reached. Infinite recursion?
+NULL
diff --git a/ext/dom/tests/modern/spec/gh23117.phpt b/ext/dom/tests/modern/spec/gh23117.phpt
new file mode 100644
index 000000000000..d284677799cd
--- /dev/null
+++ b/ext/dom/tests/modern/spec/gh23117.phpt
@@ -0,0 +1,41 @@
+--TEST--
+GH-23117 (Stack overflow when normalizing a deeply nested Dom\XMLDocument)
+--EXTENSIONS--
+dom
+--SKIPIF--
+<?php
+if (ini_get('zend.max_allowed_stack_size') === false) {
+ die('skip No stack limit support');
+}
+if (getenv('SKIP_ASAN')) {
+ die('skip ASAN needs different stack limit setting due to more stack space usage');
+}
+?>
+--INI--
+zend.max_allowed_stack_size=256K
+--FILE--
+<?php
+// Build bottom-up so the insertion cycle-check stays O(1); top-down is O(n^2).
+$doc = Dom\XMLDocument::createEmpty();
+$root = $doc->createElement('root');
+for ($s = 0; $s < 2; $s++) {
+ $node = $doc->createElement('a');
+ for ($i = 0; $i < 25000; $i++) {
+ $parent = $doc->createElement('a');
+ $parent->appendChild($node);
+ $node = $parent;
+ }
+ $root->appendChild($node);
+}
+$doc->appendChild($root);
+
+try {
+ $doc->normalize();
+} catch (\Error $e) {
+ echo "normalize: ", $e::class, ": ", $e->getMessage(), "\n";
+ var_dump($e->getPrevious());
+}
+?>
+--EXPECT--
+normalize: Error: Maximum call stack size reached. Infinite recursion?
+NULL