prevent memory exhaustion from hash attacks
authorYves Orton <demerphq@gmail.com>
Sat, 24 Nov 2012 10:16:48 +0000 (11:16 +0100)
committerYves Orton <demerphq@gmail.com>
Sat, 24 Nov 2012 10:18:27 +0000 (11:18 +0100)
We do not want to resize the hash every time the bucket length is
too long. Nor do we want to pay the price of checking how long
the bucket length is when there is nothing we can do about it anyway.

hv.c

diff --git a/hv.c b/hv.c
index c34d4d3..1511a37 100644 (file)
--- a/hv.c
+++ b/hv.c
@@ -35,7 +35,7 @@ holds the key and hash value.
 #define PERL_HASH_INTERNAL_ACCESS
 #include "perl.h"
 
-#define HV_MAX_LENGTH_BEFORE_SPLIT 14
+#define DO_HSPLIT(xhv) ((xhv)->xhv_keys > (xhv)->xhv_max) /* HvTOTALKEYS(hv) > HvMAX(hv) */
 
 static const char S_strtab_error[]
     = "Cannot modify shared string table in hv_%s";
@@ -794,29 +794,9 @@ Perl_hv_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
     if (masked_flags & HVhek_ENABLEHVKFLAGS)
        HvHASKFLAGS_on(hv);
 
-    {
-       const HE *counter = HeNEXT(entry);
-
-       xhv->xhv_keys++; /* HvTOTALKEYS(hv)++ */
-       if (!counter) {                         /* initial entry? */
-       } else if (xhv->xhv_keys > xhv->xhv_max) {
-               /* Use only the old HvUSEDKEYS(hv) > HvMAX(hv) condition to limit
-                  bucket splits on a rehashed hash, as we're not going to
-                  split it again, and if someone is lucky (evil) enough to
-                  get all the keys in one list they could exhaust our memory
-                  as we repeatedly double the number of buckets on every
-                  entry. Linear search feels a less worse thing to do.  */
-           hsplit(hv);
-        } else {
-           U32 n_links = 1;
-
-           while ((counter = HeNEXT(counter)))
-               n_links++;
-
-           if (n_links > HV_MAX_LENGTH_BEFORE_SPLIT) {
-               hsplit(hv);
-           }
-       }
+    xhv->xhv_keys++; /* HvTOTALKEYS(hv)++ */
+    if ( DO_HSPLIT(xhv) ) {
+        hsplit(hv);
     }
 
     if (return_svp) {
@@ -2718,8 +2698,8 @@ S_share_hek_flags(pTHX_ const char *str, I32 len, register U32 hash, int flags)
 
        xhv->xhv_keys++; /* HvTOTALKEYS(hv)++ */
        if (!next) {                    /* initial entry? */
-       } else if (xhv->xhv_keys > xhv->xhv_max /* HvUSEDKEYS(hv) > HvMAX(hv) */) {
-               hsplit(PL_strtab);
+       } else if ( DO_HSPLIT(xhv) ) {
+            hsplit(PL_strtab);
        }
     }