diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2019-04-16 10:20:19 +0200 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2019-04-16 10:23:11 +0200 |
commit | e1b4cabbd6d5ce72bb73ec4988b91c1e62c71335 (patch) | |
tree | 8546918ae01532a86985d4b85098381b922a43b6 | |
parent | a2f3ec17770d3fc4f995356c27b75adcc70c2cc9 (diff) | |
download | php-git-e1b4cabbd6d5ce72bb73ec4988b91c1e62c71335.tar.gz |
Partial fix for bug #77903
In the hash position APIs, make sure we always advance to the next
non-undef element and not just when the position is 0 (similar to
what foreach does). This can happen when the position of an
ArrayIterator is one past its current end and a new element is
inserted not directly at that position because the array is packed.
There is still a bug here (as shown in the tests), but this is a
separate issue that also affects plain array iteration in foreach.
-rw-r--r-- | Zend/zend_hash.c | 60 | ||||
-rw-r--r-- | ext/spl/tests/bug77903.phpt | 52 |
2 files changed, 65 insertions, 47 deletions
diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 57dfd7a964..5568b23af9 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -377,10 +377,8 @@ ZEND_API uint32_t zend_array_count(HashTable *ht) } /* }}} */ -static zend_always_inline HashPosition _zend_hash_get_first_pos(const HashTable *ht) +static zend_always_inline HashPosition _zend_hash_get_valid_pos(const HashTable *ht, HashPosition pos) { - HashPosition pos = 0; - while (pos < ht->nNumUsed && Z_ISUNDEF(ht->arData[pos].val)) { pos++; } @@ -389,12 +387,7 @@ static zend_always_inline HashPosition _zend_hash_get_first_pos(const HashTable static zend_always_inline HashPosition _zend_hash_get_current_pos(const HashTable *ht) { - HashPosition pos = ht->nInternalPointer; - - if (pos == 0) { - pos = _zend_hash_get_first_pos(ht); - } - return pos; + return _zend_hash_get_valid_pos(ht, ht->nInternalPointer); } ZEND_API HashPosition ZEND_FASTCALL zend_hash_get_current_pos(const HashTable *ht) @@ -2189,7 +2182,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_reset_ex(HashTable *ht, H { IS_CONSISTENT(ht); HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1); - *pos = _zend_hash_get_first_pos(ht); + *pos = _zend_hash_get_valid_pos(ht, 0); } @@ -2217,19 +2210,13 @@ ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_end_ex(HashTable *ht, Has ZEND_API int ZEND_FASTCALL zend_hash_move_forward_ex(HashTable *ht, HashPosition *pos) { - uint32_t idx = *pos; + uint32_t idx; IS_CONSISTENT(ht); HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1); + idx = _zend_hash_get_valid_pos(ht, *pos); if (idx < ht->nNumUsed) { - if (idx == 0) { - idx = _zend_hash_get_first_pos(ht); - if (idx >= ht->nNumUsed) { - *pos = idx; - return SUCCESS; - } - } while (1) { idx++; if (idx >= ht->nNumUsed) { @@ -2272,17 +2259,12 @@ ZEND_API int ZEND_FASTCALL zend_hash_move_backwards_ex(HashTable *ht, HashPositi /* This function should be made binary safe */ ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_ex(const HashTable *ht, zend_string **str_index, zend_ulong *num_index, HashPosition *pos) { - uint32_t idx = *pos; + uint32_t idx; Bucket *p; IS_CONSISTENT(ht); + idx = _zend_hash_get_valid_pos(ht, *pos); if (idx < ht->nNumUsed) { - if (idx == 0) { - idx = _zend_hash_get_first_pos(ht); - if (idx >= ht->nNumUsed) { - return HASH_KEY_NON_EXISTENT; - } - } p = ht->arData + idx; if (p->key) { *str_index = p->key; @@ -2297,20 +2279,14 @@ ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_ex(const HashTable *ht, zen ZEND_API void ZEND_FASTCALL zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, HashPosition *pos) { - uint32_t idx = *pos; + uint32_t idx; Bucket *p; IS_CONSISTENT(ht); + idx = _zend_hash_get_valid_pos(ht, *pos); if (idx >= ht->nNumUsed) { ZVAL_NULL(key); } else { - if (idx == 0) { - idx = _zend_hash_get_first_pos(ht); - if (idx >= ht->nNumUsed) { - ZVAL_NULL(key); - return; - } - } p = ht->arData + idx; if (p->key) { ZVAL_STR_COPY(key, p->key); @@ -2322,17 +2298,12 @@ ZEND_API void ZEND_FASTCALL zend_hash_get_current_key_zval_ex(const HashTable *h ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_type_ex(HashTable *ht, HashPosition *pos) { - uint32_t idx = *pos; + uint32_t idx; Bucket *p; IS_CONSISTENT(ht); + idx = _zend_hash_get_valid_pos(ht, *pos); if (idx < ht->nNumUsed) { - if (idx == 0) { - idx = _zend_hash_get_first_pos(ht); - if (idx >= ht->nNumUsed) { - return HASH_KEY_NON_EXISTENT; - } - } p = ht->arData + idx; if (p->key) { return HASH_KEY_IS_STRING; @@ -2346,17 +2317,12 @@ ZEND_API int ZEND_FASTCALL zend_hash_get_current_key_type_ex(HashTable *ht, Hash ZEND_API zval* ZEND_FASTCALL zend_hash_get_current_data_ex(HashTable *ht, HashPosition *pos) { - uint32_t idx = *pos; + uint32_t idx; Bucket *p; IS_CONSISTENT(ht); + idx = _zend_hash_get_valid_pos(ht, *pos); if (idx < ht->nNumUsed) { - if (idx == 0) { - idx = _zend_hash_get_first_pos(ht); - if (idx >= ht->nNumUsed) { - return NULL; - } - } p = ht->arData + idx; return &p->val; } else { diff --git a/ext/spl/tests/bug77903.phpt b/ext/spl/tests/bug77903.phpt new file mode 100644 index 0000000000..eb1c579b8f --- /dev/null +++ b/ext/spl/tests/bug77903.phpt @@ -0,0 +1,52 @@ +--TEST-- +Bug #77903: ArrayIterator stops iterating after offsetSet call +--FILE-- +<?php +$a = new ArrayIterator(); +$a->rewind(); +var_dump($a->valid()); // false +var_dump($a->current()); // null +$a->offsetSet(1,1); +var_dump($a->valid()); // true +var_dump($a->current()); // 1 +$a->next(); +var_dump($a->valid()); // false +var_dump($a->current()); // null +$a->offsetSet(4,4); +var_dump($a->valid()); // true +var_dump($a->current()); // 4 +$a->next(); +var_dump($a->valid()); // false +var_dump($a->current()); // null +$a->next(); +var_dump($a->valid()); // false +var_dump($a->current()); // null +$a->offsetSet(2,2); +var_dump($a->valid()); // should: true; got: false +var_dump($a->current()); // should: 2; got: null +$a->next(); +var_dump($a->valid()); // false +var_dump($a->current()); // null +$a->next(); +var_dump($a->valid()); // false +var_dump($a->current()); // null +?> +--EXPECT-- +bool(false) +NULL +bool(true) +int(1) +bool(false) +NULL +bool(true) +int(4) +bool(false) +NULL +bool(false) +NULL +bool(false) +NULL +bool(false) +NULL +bool(false) +NULL |