[php-src] PHP-8.5: Merge branch 'PHP-8.4' into PHP-8.5

From: Date: Sat, 08 Aug 2026 09:01:15 +0000
Subject: [php-src] PHP-8.5: Merge branch 'PHP-8.4' into PHP-8.5
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-140541@lists.php.net to get a copy of this message
Author: Weilin Du (LamentXU123) Date: 2026-08-08T16:52:48+08:00 Commit: https://github.com/php/php-src/commit/3a1b95e43ae6cf1b9c9ccb141e763d26a47762de Raw diff: https://github.com/php/php-src/commit/3a1b95e43ae6cf1b9c9ccb141e763d26a47762de.diff Merge branch 'PHP-8.4' into PHP-8.5 * PHP-8.4: 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 01849a378fff..19cb73aba23e 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,10 @@ PHP NEWS . Fixed leak on double DatePeriod::__construct() call. (ilutov) - 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) . Fixed bug GH-22825 (DOMElement::setAttribute() fails silently when the DTD declares a default value for the attribute). (iliaal) diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 4ce0a2cb147c..6716315b82b9 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -1903,9 +1903,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) { @@ -1938,6 +1955,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 (#140541) next »