Skip to content

Commit 5d40592

Browse files
committed
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
1 parent 771bfaf commit 5d40592

File tree

3 files changed

+84
-2
lines changed

3 files changed

+84
-2
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ PHP NEWS
2121
delegated Generator). (Arnaud)
2222
. Fixed bug GH-19326 (Calling Generator::throw() on a running generator with
2323
a non-Generator delegate crashes). (Arnaud)
24+
. Fixed bug GH-19280 (Stale array iterator position on rehashing). (ilutov)
2425

2526
- FTP:
2627
. Fix theoretical issues with hrtime() not being available. (nielsdos)

Zend/tests/gh19280.phpt

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
--TEST--
2+
GH-19280: Stale nInternalPosition on rehashing
3+
--FILE--
4+
<?php
5+
6+
function rehash_packed() {
7+
$a = range(0, 63);
8+
for ($i = 0; $i <= 47; $i++) {
9+
next($a);
10+
}
11+
for ($i = 16; $i < 62; $i++) {
12+
unset($a[$i]);
13+
}
14+
var_dump(key($a));
15+
$a[64] = 64;
16+
var_dump(key($a));
17+
}
18+
19+
function rehash_packed_iterated() {
20+
$a = range(0, 63);
21+
for ($i = 0; $i <= 47; $i++) {
22+
next($a);
23+
}
24+
for ($i = 16; $i < 62; $i++) {
25+
unset($a[$i]);
26+
}
27+
var_dump(key($a));
28+
foreach ($a as &$_) {
29+
$a[64] = 64;
30+
break;
31+
}
32+
var_dump(key($a));
33+
}
34+
35+
function rehash_string() {
36+
$a = [];
37+
for ($i = 0; $i < 64; $i++) {
38+
$a[md5($i)] = $i;
39+
}
40+
for ($i = 0; $i <= 47; $i++) {
41+
next($a);
42+
}
43+
for ($i = 16; $i < 62; $i++) {
44+
unset($a[md5($i)]);
45+
}
46+
var_dump(key($a));
47+
$a[md5(64)] = 64;
48+
var_dump(key($a));
49+
}
50+
51+
function rehash_int() {
52+
$a = [];
53+
for ($i = 63; $i >= 0; $i--) {
54+
$a[$i] = $i;
55+
}
56+
for ($i = 0; $i <= 47; $i++) {
57+
next($a);
58+
}
59+
for ($i = 48; $i >= 2; $i--) {
60+
unset($a[$i]);
61+
}
62+
var_dump(key($a));
63+
$a[64] = 64;
64+
var_dump(key($a));
65+
}
66+
67+
rehash_packed();
68+
rehash_packed_iterated();
69+
rehash_string();
70+
rehash_int();
71+
72+
?>
73+
--EXPECT--
74+
int(62)
75+
int(62)
76+
int(62)
77+
int(62)
78+
string(32) "44f683a84163b3523afe57c2e008bc8c"
79+
string(32) "44f683a84163b3523afe57c2e008bc8c"
80+
int(1)
81+
int(1)

Zend/zend_hash.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1379,7 +1379,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_rehash(HashTable *ht)
13791379
q->key = p->key;
13801380
Z_NEXT(q->val) = HT_HASH(ht, nIndex);
13811381
HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(j);
1382-
if (UNEXPECTED(ht->nInternalPointer == i)) {
1382+
if (UNEXPECTED(ht->nInternalPointer > j && ht->nInternalPointer <= i)) {
13831383
ht->nInternalPointer = j;
13841384
}
13851385
q++;
@@ -1398,7 +1398,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_rehash(HashTable *ht)
13981398
q->key = p->key;
13991399
Z_NEXT(q->val) = HT_HASH(ht, nIndex);
14001400
HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(j);
1401-
if (UNEXPECTED(ht->nInternalPointer == i)) {
1401+
if (UNEXPECTED(ht->nInternalPointer > j && ht->nInternalPointer <= i)) {
14021402
ht->nInternalPointer = j;
14031403
}
14041404
if (UNEXPECTED(i >= iter_pos)) {

0 commit comments

Comments
 (0)