From 0a654fd81eb5018baac0cd63cbd0de580dd3c64e Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 6 Jan 2023 16:25:10 +0100 Subject: [PATCH 1/3] Reduce HT_MAX_SIZE to account for the max load factor of 0.5 zend_hash allocates a hash table twice as big as nTableSize (HT_HASH_SIZE(HT_SIZE_TO_MASK(nTableSize)) == nTableSize*2), so HT_MAX_SIZE must be half the max table size or less. --- Zend/zend_hash.c | 10 ++++++++++ Zend/zend_types.h | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index cf0f9e5b332c4..a13bb196924e6 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -166,6 +166,8 @@ static zend_always_inline void zend_hash_real_init_mixed_ex(HashTable *ht) void *data; uint32_t nSize = ht->nTableSize; + ZEND_ASSERT(HT_SIZE_TO_MASK(nSize)); + if (UNEXPECTED(GC_FLAGS(ht) & IS_ARRAY_PERSISTENT)) { data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), 1); } else if (EXPECTED(nSize == HT_MIN_SIZE)) { @@ -341,6 +343,8 @@ ZEND_API void ZEND_FASTCALL zend_hash_packed_to_hash(HashTable *ht) Bucket *old_buckets = ht->arData; uint32_t nSize = ht->nTableSize; + ZEND_ASSERT(HT_SIZE_TO_MASK(nSize)); + HT_ASSERT_RC1(ht); HT_FLAGS(ht) &= ~HASH_FLAG_PACKED; new_data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT); @@ -369,7 +373,11 @@ ZEND_API void ZEND_FASTCALL zend_hash_to_packed(HashTable *ht) ZEND_API void ZEND_FASTCALL zend_hash_extend(HashTable *ht, uint32_t nSize, bool packed) { HT_ASSERT_RC1(ht); + if (nSize == 0) return; + + ZEND_ASSERT(HT_SIZE_TO_MASK(nSize)); + if (UNEXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) { if (nSize > ht->nTableSize) { ht->nTableSize = zend_hash_check_size(nSize); @@ -1207,6 +1215,8 @@ static void ZEND_FASTCALL zend_hash_do_resize(HashTable *ht) uint32_t nSize = ht->nTableSize + ht->nTableSize; Bucket *old_buckets = ht->arData; + ZEND_ASSERT(HT_SIZE_TO_MASK(nSize)); + ht->nTableSize = nSize; new_data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT); ht->nTableMask = HT_SIZE_TO_MASK(ht->nTableSize); diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 3d42d481a6530..df133fe8f1d43 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -401,7 +401,7 @@ struct _zend_array { #define HT_MIN_SIZE 8 #if SIZEOF_SIZE_T == 4 -# define HT_MAX_SIZE 0x04000000 /* small enough to avoid overflow checks */ +# define HT_MAX_SIZE 0x02000000 /* small enough to avoid overflow checks */ # define HT_HASH_TO_BUCKET_EX(data, idx) \ ((Bucket*)((char*)(data) + (idx))) # define HT_IDX_TO_HASH(idx) \ @@ -409,7 +409,7 @@ struct _zend_array { # define HT_HASH_TO_IDX(idx) \ ((idx) / sizeof(Bucket)) #elif SIZEOF_SIZE_T == 8 -# define HT_MAX_SIZE 0x80000000 +# define HT_MAX_SIZE 0x40000000 # define HT_HASH_TO_BUCKET_EX(data, idx) \ ((data) + (idx)) # define HT_IDX_TO_HASH(idx) \ From 2465dfa1e2af1c49e33d78a8b33fc786d7f2ccb8 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Sat, 7 Jan 2023 13:32:23 +0100 Subject: [PATCH 2/3] Expand comment --- Zend/zend_types.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Zend/zend_types.h b/Zend/zend_types.h index df133fe8f1d43..c20576445e276 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -400,8 +400,15 @@ struct _zend_array { #define HT_MIN_MASK ((uint32_t) -2) #define HT_MIN_SIZE 8 +/* HT_MAX_SIZE is chosen to satisfy the following constraints: + * - HT_SIZE_TO_MASK(HT_MAX_SIZE) != 0 + * - HT_SIZE_EX(HT_MAX_SIZE, HT_SIZE_TO_MASK(HT_MAX_SIZE)) does not overflow or + * wrapparound, and is <= the addressable space size + * - HT_MAX_SIZE must be a power of two: + * (nTableSize Date: Sat, 7 Jan 2023 13:32:36 +0100 Subject: [PATCH 3/3] Avoid overflow when nTableMask is (uint32_t)INT32_MAX+1 --- Zend/zend_types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_types.h b/Zend/zend_types.h index c20576445e276..5bc47dd31934e 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -435,7 +435,7 @@ struct _zend_array { #define HT_SIZE_TO_MASK(nTableSize) \ ((uint32_t)(-((nTableSize) + (nTableSize)))) #define HT_HASH_SIZE(nTableMask) \ - (((size_t)(uint32_t)-(int32_t)(nTableMask)) * sizeof(uint32_t)) + (((size_t)-(uint32_t)(nTableMask)) * sizeof(uint32_t)) #define HT_DATA_SIZE(nTableSize) \ ((size_t)(nTableSize) * sizeof(Bucket)) #define HT_SIZE_EX(nTableSize, nTableMask) \