]> granicus.if.org Git - php/commitdiff
zend_hash_do_resize: amortizing the cost of compaction
authorK <kaja47@k47.cz>
Thu, 26 Mar 2015 18:05:38 +0000 (19:05 +0100)
committerK <kaja47@k47.cz>
Thu, 26 Mar 2015 18:05:38 +0000 (19:05 +0100)
New implementation of hashtables introduced a compaction step which is
triggered when a hashtable is full but it contains at least one deleted
bucket. Therefore there is a possibility that a cleverly crafted code can
trigger this compaction step (which takes time proportional to the size of
hashtabe) by executing constatnt number of operations. When the hashtable
is full, deletion and subsequent addition or single element triggers a
table compaction and these two steps can be repeated ad infinitum. This
might be avenue for a DOS attack.

This patch allows compaction to be performed only if the hashtable contains
at least 1/32 deleted elements, otherwise the hashtable is doubled in size.
Linear amount of work caused by compaction is amortized over multiple
malicious additions and deletions.

Zend/zend_hash.c

index 1fd3ccf3d96dc0560d5326f8a441db052d7c45b7..09b8fa488ebf43770f33d2ec3d5238ce877504ae 100644 (file)
@@ -777,7 +777,7 @@ static void ZEND_FASTCALL zend_hash_do_resize(HashTable *ht)
        IS_CONSISTENT(ht);
        HT_ASSERT(GC_REFCOUNT(ht) == 1);
 
-       if (ht->nNumUsed > ht->nNumOfElements) {
+       if (ht->nNumUsed > ht->nNumOfElements + (ht->nNumOfElements >> 5)) { /* additional term is there to amortize the cost of compaction */
                HANDLE_BLOCK_INTERRUPTIONS();
                zend_hash_rehash(ht);
                HANDLE_UNBLOCK_INTERRUPTIONS();