Skip to content

Commit 0f7625c

Browse files
authored
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
1 parent 4fb1493 commit 0f7625c

File tree

2 files changed

+20
-3
lines changed

2 files changed

+20
-3
lines changed

Zend/zend_hash.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ static zend_always_inline void zend_hash_real_init_mixed_ex(HashTable *ht)
166166
void *data;
167167
uint32_t nSize = ht->nTableSize;
168168

169+
ZEND_ASSERT(HT_SIZE_TO_MASK(nSize));
170+
169171
if (UNEXPECTED(GC_FLAGS(ht) & IS_ARRAY_PERSISTENT)) {
170172
data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), 1);
171173
} else if (EXPECTED(nSize == HT_MIN_SIZE)) {
@@ -341,6 +343,8 @@ ZEND_API void ZEND_FASTCALL zend_hash_packed_to_hash(HashTable *ht)
341343
Bucket *old_buckets = ht->arData;
342344
uint32_t nSize = ht->nTableSize;
343345

346+
ZEND_ASSERT(HT_SIZE_TO_MASK(nSize));
347+
344348
HT_ASSERT_RC1(ht);
345349
HT_FLAGS(ht) &= ~HASH_FLAG_PACKED;
346350
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)
369373
ZEND_API void ZEND_FASTCALL zend_hash_extend(HashTable *ht, uint32_t nSize, bool packed)
370374
{
371375
HT_ASSERT_RC1(ht);
376+
372377
if (nSize == 0) return;
378+
379+
ZEND_ASSERT(HT_SIZE_TO_MASK(nSize));
380+
373381
if (UNEXPECTED(HT_FLAGS(ht) & HASH_FLAG_UNINITIALIZED)) {
374382
if (nSize > ht->nTableSize) {
375383
ht->nTableSize = zend_hash_check_size(nSize);
@@ -1207,6 +1215,8 @@ static void ZEND_FASTCALL zend_hash_do_resize(HashTable *ht)
12071215
uint32_t nSize = ht->nTableSize + ht->nTableSize;
12081216
Bucket *old_buckets = ht->arData;
12091217

1218+
ZEND_ASSERT(HT_SIZE_TO_MASK(nSize));
1219+
12101220
ht->nTableSize = nSize;
12111221
new_data = pemalloc(HT_SIZE_EX(nSize, HT_SIZE_TO_MASK(nSize)), GC_FLAGS(ht) & IS_ARRAY_PERSISTENT);
12121222
ht->nTableMask = HT_SIZE_TO_MASK(ht->nTableSize);

Zend/zend_types.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -400,16 +400,23 @@ struct _zend_array {
400400
#define HT_MIN_MASK ((uint32_t) -2)
401401
#define HT_MIN_SIZE 8
402402

403+
/* HT_MAX_SIZE is chosen to satisfy the following constraints:
404+
* - HT_SIZE_TO_MASK(HT_MAX_SIZE) != 0
405+
* - HT_SIZE_EX(HT_MAX_SIZE, HT_SIZE_TO_MASK(HT_MAX_SIZE)) does not overflow or
406+
* wrapparound, and is <= the addressable space size
407+
* - HT_MAX_SIZE must be a power of two:
408+
* (nTableSize<HT_MAX_SIZE ? nTableSize+nTableSize : nTableSize) <= HT_MAX_SIZE
409+
*/
403410
#if SIZEOF_SIZE_T == 4
404-
# define HT_MAX_SIZE 0x04000000 /* small enough to avoid overflow checks */
411+
# define HT_MAX_SIZE 0x02000000
405412
# define HT_HASH_TO_BUCKET_EX(data, idx) \
406413
((Bucket*)((char*)(data) + (idx)))
407414
# define HT_IDX_TO_HASH(idx) \
408415
((idx) * sizeof(Bucket))
409416
# define HT_HASH_TO_IDX(idx) \
410417
((idx) / sizeof(Bucket))
411418
#elif SIZEOF_SIZE_T == 8
412-
# define HT_MAX_SIZE 0x80000000
419+
# define HT_MAX_SIZE 0x40000000
413420
# define HT_HASH_TO_BUCKET_EX(data, idx) \
414421
((data) + (idx))
415422
# define HT_IDX_TO_HASH(idx) \
@@ -428,7 +435,7 @@ struct _zend_array {
428435
#define HT_SIZE_TO_MASK(nTableSize) \
429436
((uint32_t)(-((nTableSize) + (nTableSize))))
430437
#define HT_HASH_SIZE(nTableMask) \
431-
(((size_t)(uint32_t)-(int32_t)(nTableMask)) * sizeof(uint32_t))
438+
(((size_t)-(uint32_t)(nTableMask)) * sizeof(uint32_t))
432439
#define HT_DATA_SIZE(nTableSize) \
433440
((size_t)(nTableSize) * sizeof(Bucket))
434441
#define HT_SIZE_EX(nTableSize, nTableMask) \

0 commit comments

Comments
 (0)