From: Dmitry Stogov Date: Thu, 12 Feb 2015 23:12:42 +0000 (+0300) Subject: Limit HashTable size to avoid integer overflow checks X-Git-Tag: PRE_PHP7_EREG_MYSQL_REMOVALS~169 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=12abac8bb78c494597d740e7f9afd202f63180a3;p=php Limit HashTable size to avoid integer overflow checks --- diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 195b5e48f0..e93e2e6752 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -82,42 +82,26 @@ static void _zend_is_inconsistent(const HashTable *ht, const char *file, int lin static void zend_hash_do_resize(HashTable *ht); -#define CHECK_INIT(ht, packed) do { \ - if (UNEXPECTED(!((ht)->u.flags & HASH_FLAG_INITIALIZED))) { \ - if (packed) { \ - (ht)->u.flags |= HASH_FLAG_INITIALIZED | HASH_FLAG_PACKED; \ - (ht)->arData = (Bucket *) safe_pemalloc((ht)->nTableSize, sizeof(Bucket), 0, (ht)->u.flags & HASH_FLAG_PERSISTENT); \ - } else { \ - (ht)->u.flags |= HASH_FLAG_INITIALIZED; \ - (ht)->nTableMask = (ht)->nTableSize - 1; \ - (ht)->arData = (Bucket *) safe_pemalloc((ht)->nTableSize, sizeof(Bucket) + sizeof(uint32_t), 0, (ht)->u.flags & HASH_FLAG_PERSISTENT); \ - (ht)->arHash = (uint32_t*)((ht)->arData + (ht)->nTableSize); \ - memset((ht)->arHash, INVALID_IDX, (ht)->nTableSize * sizeof(uint32_t)); \ - } \ - } \ -} while (0) - -static const uint32_t uninitialized_bucket = {INVALID_IDX}; - -ZEND_API void _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC) +static uint32_t zend_always_inline zend_hash_check_size(uint32_t nSize) { #if defined(ZEND_WIN32) unsigned long index; #endif + /* Use big enough power of 2 */ - /* size should be between 8 and 0x80000000 */ - nSize = (nSize <= 8 ? 8 : (nSize >= 0x80000000 ? 0x80000000 : nSize)); + /* size should be between HT_MIN_SIZE and HT_MAX_SIZE */ + nSize = (nSize <= HT_MIN_SIZE ? HT_MIN_SIZE : (nSize >= HT_MAX_SIZE ? HT_MAX_SIZE : nSize)); #if defined(ZEND_WIN32) if (BitScanReverse(&index, nSize - 1)) { - ht->nTableSize = 0x2 << ((31 - index) ^ 0x1f); + return 0x2 << ((31 - index) ^ 0x1f); } else { /* nSize is ensured to be in the valid range, fall back to it rather than using an undefined bis scan result. */ - ht->nTableSize = nSize; + return nSize; } #elif defined(__GNUC__) - ht->nTableSize = 0x2 << (__builtin_clz(nSize - 1) ^ 0x1f); + return 0x2 << (__builtin_clz(nSize - 1) ^ 0x1f); #else nSize -= 1; nSize |= (nSize >> 1); @@ -125,9 +109,34 @@ ZEND_API void _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestru nSize |= (nSize >> 4); nSize |= (nSize >> 8); nSize |= (nSize >> 16); - ht->nTableSize = nSize + 1; + return nSize + 1; #endif +} + +static void zend_always_inline zend_hash_check_init(HashTable *ht, int packed) +{ + if (UNEXPECTED(!((ht)->u.flags & HASH_FLAG_INITIALIZED))) { + if (packed) { + (ht)->u.flags |= HASH_FLAG_INITIALIZED | HASH_FLAG_PACKED; + (ht)->arData = (Bucket *) pemalloc((ht)->nTableSize * sizeof(Bucket), (ht)->u.flags & HASH_FLAG_PERSISTENT); + } else { + (ht)->u.flags |= HASH_FLAG_INITIALIZED; + (ht)->nTableMask = (ht)->nTableSize - 1; + (ht)->arData = (Bucket *) pemalloc((ht)->nTableSize * (sizeof(Bucket) + sizeof(uint32_t)), (ht)->u.flags & HASH_FLAG_PERSISTENT); + (ht)->arHash = (uint32_t*)((ht)->arData + (ht)->nTableSize); + memset((ht)->arHash, INVALID_IDX, (ht)->nTableSize * sizeof(uint32_t)); + } + } +} + +#define CHECK_INIT(ht, packed) \ + zend_hash_check_init(ht, packed) + +static const uint32_t uninitialized_bucket = {INVALID_IDX}; +ZEND_API void _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC) +{ + ht->nTableSize = zend_hash_check_size(nSize); ht->nTableMask = 0; ht->nNumUsed = 0; ht->nNumOfElements = 0; @@ -141,9 +150,12 @@ ZEND_API void _zend_hash_init(HashTable *ht, uint32_t nSize, dtor_func_t pDestru static void zend_hash_packed_grow(HashTable *ht) { + if (ht->nTableSize >= HT_MAX_SIZE) { + zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", ht->nTableSize * 2, sizeof(Bucket), sizeof(Bucket)); + } HANDLE_BLOCK_INTERRUPTIONS(); - ht->arData = (Bucket *) safe_perealloc(ht->arData, (ht->nTableSize << 1), sizeof(Bucket), 0, ht->u.flags & HASH_FLAG_PERSISTENT); - ht->nTableSize = (ht->nTableSize << 1); + ht->nTableSize += ht->nTableSize; + ht->arData = (Bucket *) perealloc(ht->arData, ht->nTableSize * sizeof(Bucket), ht->u.flags & HASH_FLAG_PERSISTENT); HANDLE_UNBLOCK_INTERRUPTIONS(); } @@ -159,7 +171,7 @@ ZEND_API void zend_hash_packed_to_hash(HashTable *ht) HANDLE_BLOCK_INTERRUPTIONS(); ht->u.flags &= ~HASH_FLAG_PACKED; ht->nTableMask = ht->nTableSize - 1; - ht->arData = (Bucket *) safe_perealloc(ht->arData, ht->nTableSize, sizeof(Bucket) + sizeof(uint32_t), 0, ht->u.flags & HASH_FLAG_PERSISTENT); + ht->arData = (Bucket *) perealloc(ht->arData, ht->nTableSize * (sizeof(Bucket) + sizeof(uint32_t)), ht->u.flags & HASH_FLAG_PERSISTENT); ht->arHash = (uint32_t*)(ht->arData + ht->nTableSize); zend_hash_rehash(ht); HANDLE_UNBLOCK_INTERRUPTIONS(); @@ -705,14 +717,16 @@ static void zend_hash_do_resize(HashTable *ht) HANDLE_BLOCK_INTERRUPTIONS(); zend_hash_rehash(ht); HANDLE_UNBLOCK_INTERRUPTIONS(); - } else if ((ht->nTableSize << 1) > 0) { /* Let's double the table size */ + } else if (ht->nTableSize < HT_MAX_SIZE) { /* Let's double the table size */ HANDLE_BLOCK_INTERRUPTIONS(); - ht->arData = (Bucket *) safe_perealloc(ht->arData, (ht->nTableSize << 1), sizeof(Bucket) + sizeof(uint32_t), 0, ht->u.flags & HASH_FLAG_PERSISTENT); - ht->arHash = (uint32_t*)(ht->arData + (ht->nTableSize << 1)); - ht->nTableSize = (ht->nTableSize << 1); + ht->nTableSize += ht->nTableSize; + ht->arData = (Bucket *) perealloc(ht->arData, ht->nTableSize * (sizeof(Bucket) + sizeof(uint32_t)), ht->u.flags & HASH_FLAG_PERSISTENT); + ht->arHash = (uint32_t*)(ht->arData + ht->nTableSize); ht->nTableMask = ht->nTableSize - 1; zend_hash_rehash(ht); HANDLE_UNBLOCK_INTERRUPTIONS(); + } else { + zend_error_noreturn(E_ERROR, "Possible integer overflow in memory allocation (%zu * %zu + %zu)", ht->nTableSize * 2, sizeof(Bucket) + sizeof(uint32_t), sizeof(Bucket)); } } @@ -1412,7 +1426,7 @@ ZEND_API void zend_array_dup(HashTable *target, HashTable *source) target->nNumUsed = source->nNumUsed; target->nNumOfElements = source->nNumOfElements; target->nNextFreeElement = source->nNextFreeElement; - target->arData = (Bucket *) safe_pemalloc(target->nTableSize, sizeof(Bucket), 0, 0); + target->arData = (Bucket *) pemalloc(target->nTableSize * sizeof(Bucket), 0); target->arHash = (uint32_t*)&uninitialized_bucket; target->nInternalPointer = source->nInternalPointer; @@ -1455,7 +1469,7 @@ ZEND_API void zend_array_dup(HashTable *target, HashTable *source) } } else { target->nNextFreeElement = source->nNextFreeElement; - target->arData = (Bucket *) safe_pemalloc(target->nTableSize, sizeof(Bucket) + sizeof(uint32_t), 0, 0); + target->arData = (Bucket *) pemalloc(target->nTableSize * (sizeof(Bucket) + sizeof(uint32_t)), 0); target->arHash = (uint32_t*)(target->arData + target->nTableSize); memset(target->arHash, INVALID_IDX, target->nTableSize * sizeof(uint32_t)); diff --git a/Zend/zend_string.c b/Zend/zend_string.c index ccd95e51f4..634e2c8104 100644 --- a/Zend/zend_string.c +++ b/Zend/zend_string.c @@ -106,7 +106,7 @@ static zend_string *zend_new_interned_string_int(zend_string *str) GC_FLAGS(str) |= IS_STR_INTERNED; if (CG(interned_strings).nNumUsed >= CG(interned_strings).nTableSize) { - if ((CG(interned_strings).nTableSize << 1) > 0) { /* Let's double the table size */ + if (CG(interned_strings).nTableSize < HT_MAX_SIZE) { /* Let's double the table size */ Bucket *d = (Bucket *) perealloc_recoverable(CG(interned_strings).arData, (CG(interned_strings).nTableSize << 1) * sizeof(Bucket), 1); uint32_t *h = (uint32_t *) perealloc_recoverable(CG(interned_strings).arHash, (CG(interned_strings).nTableSize << 1) * sizeof(uint32_t), 1); diff --git a/Zend/zend_types.h b/Zend/zend_types.h index d05a6adcb9..4c238f652b 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -181,6 +181,16 @@ typedef struct _HashTable { dtor_func_t pDestructor; } HashTable; +#define HT_MIN_SIZE 8 + +#if SIZEOF_SIZE_T == 4 +# define HT_MAX_SIZE 0x04000000 /* small enough to avoid overflow checks */ +#elif SIZEOF_SIZE_T == 8 +# define HT_MAX_SIZE 0x80000000 +#else +# error "Unknown SIZEOF_SIZE_T" +#endif + typedef uint32_t HashPosition; typedef struct _HashTableIterator {