]> granicus.if.org Git - php/commitdiff
Limit HashTable size to avoid integer overflow checks
authorDmitry Stogov <dmitry@zend.com>
Thu, 12 Feb 2015 23:12:42 +0000 (02:12 +0300)
committerDmitry Stogov <dmitry@zend.com>
Thu, 12 Feb 2015 23:12:42 +0000 (02:12 +0300)
Zend/zend_hash.c
Zend/zend_string.c
Zend/zend_types.h

index 195b5e48f0e1ae32812b85c0931da85821e2f1cc..e93e2e6752662fdbdd5309de731fbd0260c6f4fd 100644 (file)
@@ -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));
 
index ccd95e51f4029073557df1d157436aad4b7965e8..634e2c810487ce94d54383c5586fa8230ac964af 100644 (file)
@@ -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);
 
index d05a6adcb979375eef7c2af1997016bd3a0eacf3..4c238f652bf14ac71a41287c93dadc1fe69d7453 100644 (file)
@@ -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 {