]> granicus.if.org Git - php/commitdiff
Fix HT flags copying wrt iterator count
authorNikita Popov <nikita.ppv@gmail.com>
Tue, 16 Apr 2019 09:47:41 +0000 (11:47 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Tue, 16 Apr 2019 10:14:45 +0000 (12:14 +0200)
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.

Zend/zend_hash.c
Zend/zend_hash.h
ext/standard/array.c

index e26ebd75947fd8890fb0afdb4bfa6e4a8031669d..8c0bce5b411ff6d47fdbf04e17a423000edb0094 100644 (file)
@@ -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 =
index 66c8ce378540639756519a8d71c1164ed609f138..2c295adca6b6920ac6a87e9f1be262aa408d52db 100644 (file)
@@ -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) \
index 042f97726ade3b4f3449e7383f43e9cb409e78a5..3c8723fca754a7d7206d74635f73b5a7f5e75d99 100644 (file)
@@ -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);