From 5d40592fe28c87e027794a091d08f0d4cf280885 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Wed, 30 Jul 2025 23:57:21 +0200 Subject: [PATCH] Fix stale nInternalPosition on rehashing Since GH-13188 we're no longer immediately updating iterator positions when deleting array elements. zend_hash_rehash() needs to adapt accordingly by adjusting nInternalPosition for IS_UNDEF elements. This is already the case for array iterators. Fixes GH-19280 Closes GH-19323 --- NEWS | 1 + Zend/tests/gh19280.phpt | 81 +++++++++++++++++++++++++++++++++++++++++ Zend/zend_hash.c | 4 +- 3 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 Zend/tests/gh19280.phpt diff --git a/NEWS b/NEWS index 84006fc58f2..93f804bb5d9 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,7 @@ PHP NEWS delegated Generator). (Arnaud) . Fixed bug GH-19326 (Calling Generator::throw() on a running generator with a non-Generator delegate crashes). (Arnaud) + . Fixed bug GH-19280 (Stale array iterator position on rehashing). (ilutov) - FTP: . Fix theoretical issues with hrtime() not being available. (nielsdos) diff --git a/Zend/tests/gh19280.phpt b/Zend/tests/gh19280.phpt new file mode 100644 index 00000000000..d73fc91b623 --- /dev/null +++ b/Zend/tests/gh19280.phpt @@ -0,0 +1,81 @@ +--TEST-- +GH-19280: Stale nInternalPosition on rehashing +--FILE-- += 0; $i--) { + $a[$i] = $i; + } + for ($i = 0; $i <= 47; $i++) { + next($a); + } + for ($i = 48; $i >= 2; $i--) { + unset($a[$i]); + } + var_dump(key($a)); + $a[64] = 64; + var_dump(key($a)); +} + +rehash_packed(); +rehash_packed_iterated(); +rehash_string(); +rehash_int(); + +?> +--EXPECT-- +int(62) +int(62) +int(62) +int(62) +string(32) "44f683a84163b3523afe57c2e008bc8c" +string(32) "44f683a84163b3523afe57c2e008bc8c" +int(1) +int(1) diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index a417290b82c..07dad65f1d2 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -1379,7 +1379,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_rehash(HashTable *ht) q->key = p->key; Z_NEXT(q->val) = HT_HASH(ht, nIndex); HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(j); - if (UNEXPECTED(ht->nInternalPointer == i)) { + if (UNEXPECTED(ht->nInternalPointer > j && ht->nInternalPointer <= i)) { ht->nInternalPointer = j; } q++; @@ -1398,7 +1398,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_rehash(HashTable *ht) q->key = p->key; Z_NEXT(q->val) = HT_HASH(ht, nIndex); HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(j); - if (UNEXPECTED(ht->nInternalPointer == i)) { + if (UNEXPECTED(ht->nInternalPointer > j && ht->nInternalPointer <= i)) { ht->nInternalPointer = j; } if (UNEXPECTED(i >= iter_pos)) {