summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2015-02-13 02:12:42 +0300
committerDmitry Stogov <dmitry@zend.com>2015-02-13 02:12:42 +0300
commit12abac8bb78c494597d740e7f9afd202f63180a3 (patch)
tree811a7a1a5efd2e1e90cc02b14ee89ee1a048dec0
parent2fb85f1058fd7cc7778afa5d3fef24948cbe2ac7 (diff)
downloadphp-git-12abac8bb78c494597d740e7f9afd202f63180a3.tar.gz
Limit HashTable size to avoid integer overflow checks
-rw-r--r--Zend/zend_hash.c80
-rw-r--r--Zend/zend_string.c2
-rw-r--r--Zend/zend_types.h10
3 files changed, 58 insertions, 34 deletions
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 {