diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2019-04-16 11:47:41 +0200 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2019-04-16 12:14:45 +0200 |
commit | f9a755d0d27eebd02fdeb9b1750aaca70b15f409 (patch) | |
tree | 614988fa36e579302da47a2480e1a650dfb909b8 | |
parent | 5ae49c43dc618ad373b88480a1662e480bd125fb (diff) | |
download | php-git-f9a755d0d27eebd02fdeb9b1750aaca70b15f409.tar.gz |
Fix HT flags copying wrt iterator count
HT_FLAGS() includes the full flag word, including the iterator
count. When we're fully reassigning it, we need to make sure that
we either really do want to copy the iterator count (as in some
cases in array.c) or we need to mask only the actual flag byte.
Add an assert to hash_iterators_del() to make sure the iterator
count is non-zero (which is how I ran into this) and make sure that
the iterator count is correctly preserved during array splicing.
-rw-r--r-- | Zend/zend_hash.c | 10 | ||||
-rw-r--r-- | Zend/zend_hash.h | 3 | ||||
-rw-r--r-- | ext/standard/array.c | 1 |
3 files changed, 10 insertions, 4 deletions
diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index e26ebd7594..8c0bce5b41 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -481,6 +481,7 @@ ZEND_API void ZEND_FASTCALL zend_hash_iterator_del(uint32_t idx) if (EXPECTED(iter->ht) && EXPECTED(iter->ht != HT_POISONED_PTR) && EXPECTED(!HT_ITERATORS_OVERFLOW(iter->ht))) { + ZEND_ASSERT(HT_ITERATORS_COUNT(iter->ht) != 0); HT_DEC_ITERATORS_COUNT(iter->ht); } iter->ht = NULL; @@ -1928,7 +1929,8 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source) target->pDestructor = ZVAL_PTR_DTOR; if (source->nNumOfElements == 0) { - HT_FLAGS(target) = (HT_FLAGS(source) & ~(HASH_FLAG_INITIALIZED|HASH_FLAG_PACKED)) | HASH_FLAG_STATIC_KEYS; + uint32_t mask = HASH_FLAG_MASK & ~(HASH_FLAG_INITIALIZED|HASH_FLAG_PACKED); + HT_FLAGS(target) = (HT_FLAGS(source) & mask) | HASH_FLAG_STATIC_KEYS; target->nTableMask = HT_MIN_MASK; target->nNumUsed = 0; target->nNumOfElements = 0; @@ -1936,7 +1938,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source) target->nInternalPointer = 0; HT_SET_DATA_ADDR(target, &uninitialized_bucket); } else if (GC_FLAGS(source) & IS_ARRAY_IMMUTABLE) { - HT_FLAGS(target) = HT_FLAGS(source); + HT_FLAGS(target) = HT_FLAGS(source) & HASH_FLAG_MASK; target->nTableMask = source->nTableMask; target->nNumUsed = source->nNumUsed; target->nNumOfElements = source->nNumOfElements; @@ -1945,7 +1947,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source) target->nInternalPointer = source->nInternalPointer; memcpy(HT_GET_DATA_ADDR(target), HT_GET_DATA_ADDR(source), HT_USED_SIZE(source)); } else if (HT_FLAGS(source) & HASH_FLAG_PACKED) { - HT_FLAGS(target) = HT_FLAGS(source); + HT_FLAGS(target) = HT_FLAGS(source) & HASH_FLAG_MASK; target->nTableMask = HT_MIN_MASK; target->nNumUsed = source->nNumUsed; target->nNumOfElements = source->nNumOfElements; @@ -1963,7 +1965,7 @@ ZEND_API HashTable* ZEND_FASTCALL zend_array_dup(HashTable *source) zend_array_dup_packed_elements(source, target, 1); } } else { - HT_FLAGS(target) = HT_FLAGS(source); + HT_FLAGS(target) = HT_FLAGS(source) & HASH_FLAG_MASK; target->nTableMask = source->nTableMask; target->nNextFreeElement = source->nNextFreeElement; target->nInternalPointer = diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 66c8ce3785..2c295adca6 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -40,6 +40,9 @@ #define HASH_FLAG_HAS_EMPTY_IND (1<<5) #define HASH_FLAG_ALLOW_COW_VIOLATION (1<<6) +/* Only the low byte are real flags */ +#define HASH_FLAG_MASK 0xff + #define HT_FLAGS(ht) (ht)->u.flags #define HT_IS_PACKED(ht) \ diff --git a/ext/standard/array.c b/ext/standard/array.c index 042f97726a..3c8723fca7 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -3138,6 +3138,7 @@ static void php_splice(HashTable *in_hash, zend_long offset, zend_long length, H } /* replace HashTable data */ + HT_SET_ITERATORS_COUNT(&out_hash, HT_ITERATORS_COUNT(in_hash)); HT_SET_ITERATORS_COUNT(in_hash, 0); in_hash->pDestructor = NULL; zend_hash_destroy(in_hash); |