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

From: Date: Sat, 08 Aug 2026 09:01:16 +0000
Subject: [php-src] master: Merge branch 'PHP-8.5'
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-140542@lists.php.net to get a copy of this message
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

« previous php.cvs (#140542) next »