Zend: Fix iterator relocation at the current element during rehash - #23607
Conversation
| zend_hash_iterators_update(ht, iter_pos, j); | ||
| iter_pos = zend_hash_iterators_lower_pos(ht, iter_pos + 1); | ||
| } while (iter_pos < i); | ||
| } while (iter_pos <= i); |
There was a problem hiding this comment.
nit: while at it, you can also fix zend_array_dup_elements()
There was a problem hiding this comment.
Right. and I think this is the similar fix to the existing one so we don't need to add more NEWS entry.
When one iterator points at a hole and another at the next live bucket, the relocation loop must include the iterator at that bucket's original position. Otherwise it is moved to the following bucket's destination, causing a nested by-reference foreach to skip an element. Add regression coverage for packed-to-hash conversion and compaction of a full mixed table.
cb88dd2 to
2e62b97
Compare
|
I am testing my new agent workflow to help me branching written PR to remote and obviously it glitched so this is unintentionally closed and reopened. Sorry for the noise. |
| zend_hash_iterators_update(target, iter_pos, target_idx); | ||
| iter_pos = zend_hash_iterators_lower_pos(target, iter_pos + 1); | ||
| } while (iter_pos < idx); | ||
| } while (iter_pos <= idx); |
There was a problem hiding this comment.
Two more relocation gaps in this function, both pre-existing and both already handled in zend_hash_rehash():
- No one-past-the-end migration. Rehash does it at
:1434, and this loop cannot reach that position under either bound, sincelower_pos()returnsnNumUsedas both its sentinel and a real cursor there. A by-ref foreach on the last element then loses elements appended after a COW dup. :2411and:2424still usesource->nInternalPointer == idx, the form 5d40592 replaced in rehash with the(j, i]range at:1389. A cursor parked on a hole is never remapped, sokey()/next()skips live elements. Testtarget->rather thansource->, at both sites.
Follow-up is fine, neither is yours.
| if ($innerValue === 11) { | ||
| // The outer cursor is at a hole, the inner at the next value. | ||
| unset($values['a'], $values['b']); | ||
| $copy = $values; |
There was a problem hiding this comment.
$copy is write-only and is the only reason this write separates through zend_array_dup_elements() instead of zend_hash_rehash(). Delete it and the test still passes, but it becomes a duplicate of rehash_multiple_iterators.phpt case 2 and :2431 loses its only pin.
echo 'copy: ', implode(' ', array_keys($copy)), "\n"; with copy: c d e f g h in --EXPECT-- makes the line undeletable. Optional, nothing is wrong today.
|
Fix looks right. The bound now agrees with the
|
iliaal
left a comment
There was a problem hiding this comment.
LGTM, thanks for making the changes!
Found when I am implementing #23585
Return
But I expect: