summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2019-04-16 11:47:41 +0200
committerNikita Popov <nikita.ppv@gmail.com>2019-04-16 12:14:45 +0200
commitf9a755d0d27eebd02fdeb9b1750aaca70b15f409 (patch)
tree614988fa36e579302da47a2480e1a650dfb909b8
parent5ae49c43dc618ad373b88480a1662e480bd125fb (diff)
downloadphp-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.c10
-rw-r--r--Zend/zend_hash.h3
-rw-r--r--ext/standard/array.c1
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);