Reduce HT_MAX_SIZE to account for the max load factor of 0.5 (#10242)

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.

Fixes GH-10240
This commit is contained in:
Arnaud Le Blanc 2023-01-13 12:05:51 +01:00 committed by GitHub
parent 4fb149390a
commit 0f7625c47c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 20 additions and 3 deletions

View File

@ -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);

View File

@ -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<HT_MAX_SIZE ? nTableSize+nTableSize : nTableSize) <= HT_MAX_SIZE
*/
#if SIZEOF_SIZE_T == 4
# define HT_MAX_SIZE 0x04000000 /* small enough to avoid overflow checks */
# define HT_MAX_SIZE 0x02000000
# define HT_HASH_TO_BUCKET_EX(data, idx) \
((Bucket*)((char*)(data) + (idx)))
# define HT_IDX_TO_HASH(idx) \
@ -409,7 +416,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) \
@ -428,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) \