diff --git a/NEWS b/NEWS index 86b6bd3e2e0c..d95ec7b47c41 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,8 @@ PHP NEWS next() call on the inner generator). (iliaal) . Fixed bug GH-23301 (Nested "yield from" yields a value twice when the middle generator delegates again). (Lazizbek Ergashev) + . Fixed bug GH-23332 (Saturation of the HashTable iterator counter leads to + UAF). (luizmenos, iliaal) - DOM: . Fixed a use-after-free when cloning a DOMNameSpaceNode after diff --git a/Zend/tests/gh23332.phpt b/Zend/tests/gh23332.phpt new file mode 100644 index 000000000000..9d58a2620217 --- /dev/null +++ b/Zend/tests/gh23332.phpt @@ -0,0 +1,45 @@ +--TEST-- +GH-23332 (Saturation of the HashTable iterator counter leads to UAF) +--FILE-- + 1, 'second' => 2, 'third' => 3]; + +// Saturate $a's iterator counter (it caps at 255). +$gens = []; +for ($i = 0; $i < 255; $i++) { + $g = gen($a); + $g->current(); + $gens[] = $g; +} +unset($g); + +$array = $a; + +$pass = 0; +$retained = null; +foreach ($array as $key => &$value) { + echo "pass ", ++$pass, ": $key => $value\n"; + if ($pass === 1) { + $retained = $array; + $array = ['replacement' => 4242]; + continue; + } + for ($i = 0; $i < 254; $i++) { + unset($gens[$i]); + } + $retained = null; + unset($gens[254]); +} + +echo "done\n"; +?> +--EXPECT-- +pass 1: first => 1 +pass 2: replacement => 4242 +done diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 82d0318428fa..552ed0919753 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -632,7 +632,7 @@ ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterator_pos_ex(uint32_t idx, zval ZEND_ASSERT(idx != (uint32_t)-1); if (UNEXPECTED(iter->ht != ht) && !zend_hash_iterator_find_copy_pos(idx, ht)) { if (EXPECTED(iter->ht) && EXPECTED(iter->ht != HT_POISONED_PTR) - && EXPECTED(!HT_ITERATORS_OVERFLOW(ht))) { + && EXPECTED(!HT_ITERATORS_OVERFLOW(iter->ht))) { HT_DEC_ITERATORS_COUNT(iter->ht); }