]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
thresholds: Fix buffer overflow in threshold context
authorMats Klepsland <mats.klepsland@gmail.com>
Thu, 27 May 2021 10:02:55 +0000 (12:02 +0200)
committerVictor Julien <victor@inliniac.net>
Fri, 4 Jun 2021 08:37:35 +0000 (10:37 +0200)
th_entry is resized using ThresholdHashRealloc() every time a rule with
a threshold using by_rule tracking is added. The problem is that this is
done before the rules are reordered, so occasionally a rule with by_rule
tracking gets a higher signature number (after reordering) than the
number of th_entries allocated, causing Suricata to crash.

This commit fixes this by allocating th_entries after all the rules are
loaded and reordered.

Backtrace from core dump:

  Program terminated with signal SIGSEGV, Segmentation fault.

  #0  0x000000000051b381 in ThresholdHandlePacket (p=p@entry=0x7fb0080f3960, lookup_tsh=0x51, new_tsh=new_tsh@entry=0x7fb016c316e0, td=td@entry=0x14adedf0, sid=9800979, gid=1, pa=0x7fb0080f3b18)
      at detect-engine-threshold.c:415
  415>----                if (TIMEVAL_DIFF_SEC(p->ts, lookup_tsh->tv1) < td->seconds) {

Bug #4503.

src/detect-engine-build.c
src/detect-engine-threshold.c
src/detect-engine-threshold.h
src/detect-threshold.c
src/util-threshold-config.c

index 460ee4c4983e829291efa6a21d90dfbfef08ead4..1377fbc2db976f8b28a6fa505197f2bbc7fd05a5 100644 (file)
@@ -28,6 +28,7 @@
 #include "detect-engine-port.h"
 #include "detect-engine-prefilter.h"
 #include "detect-engine-proto.h"
+#include "detect-engine-threshold.h"
 
 #include "detect-dsize.h"
 #include "detect-tcp-flags.h"
@@ -1944,6 +1945,8 @@ int SigGroupBuild(DetectEngineCtx *de_ctx)
     SCProfilingRuleInitCounters(de_ctx);
 #endif
 
+    ThresholdHashAllocate(de_ctx);
+
     if (!DetectEngineMultiTenantEnabled()) {
         VarNameStoreActivateStaging();
     }
index 28e50d5be7863ab6702cf1977aa9ced630ba70a7..70c4e4f567643317ab301bbefd93c051dc334729 100644 (file)
@@ -651,27 +651,71 @@ void ThresholdHashInit(DetectEngineCtx *de_ctx)
 }
 
 /**
- * \brief Realloc threshold context hash tables
+ * \brief Allocate threshold context hash tables
  *
  * \param de_ctx Detection Context
  */
-void ThresholdHashRealloc(DetectEngineCtx *de_ctx)
+void ThresholdHashAllocate(DetectEngineCtx *de_ctx)
 {
-    /* Return if we are already big enough */
-    uint32_t num = de_ctx->signum + 1;
-    if (num <= de_ctx->ths_ctx.th_size)
-        return;
+    Signature *s = de_ctx->sig_list;
+    bool has_by_rule_tracking = false;
+    const DetectThresholdData *td = NULL;
+    const SigMatchData *smd;
+
+    /* Find the signature with the highest signature number that is using
+       thresholding with by_rule tracking. */
+    uint32_t highest_signum = 0;
+    while (s != NULL) {
+        if (s->sm_arrays[DETECT_SM_LIST_SUPPRESS] != NULL) {
+            smd = NULL;
+            do {
+                td = SigGetThresholdTypeIter(s, &smd, DETECT_SM_LIST_SUPPRESS);
+                if (td == NULL) {
+                    continue;
+                }
+                if (td->track != TRACK_RULE) {
+                    continue;
+                }
+                if (s->num >= highest_signum) {
+                    highest_signum = s->num;
+                    has_by_rule_tracking = true;
+                }
+            } while (smd != NULL);
+        }
 
-    void *ptmp = SCRealloc(de_ctx->ths_ctx.th_entry, num * sizeof(DetectThresholdEntry *));
-    if (ptmp == NULL) {
-        SCLogWarning(SC_ERR_MEM_ALLOC, "Error allocating memory for rule thresholds"
-                " (tried to allocate %"PRIu32" th_entrys for rule tracking)", num);
-    } else {
-        de_ctx->ths_ctx.th_entry = ptmp;
-        for (uint32_t i = de_ctx->ths_ctx.th_size; i < num; ++i) {
-            de_ctx->ths_ctx.th_entry[i] = NULL;
+        if (s->sm_arrays[DETECT_SM_LIST_THRESHOLD] != NULL) {
+            smd = NULL;
+            do {
+                td = SigGetThresholdTypeIter(s, &smd, DETECT_SM_LIST_THRESHOLD);
+                if (td == NULL) {
+                    continue;
+                }
+                if (td->track != TRACK_RULE) {
+                    continue;
+                }
+                if (s->num >= highest_signum) {
+                    highest_signum = s->num;
+                    has_by_rule_tracking = true;
+                }
+            } while (smd != NULL);
         }
-        de_ctx->ths_ctx.th_size = num;
+
+        s = s->next;
+    }
+
+    /* Skip allocating if by_rule tracking is not used */
+    if (has_by_rule_tracking == false) {
+        return;
+    }
+
+    de_ctx->ths_ctx.th_size = highest_signum + 1;
+    de_ctx->ths_ctx.th_entry = SCCalloc(de_ctx->ths_ctx.th_size, sizeof(DetectThresholdEntry *));
+    if (de_ctx->ths_ctx.th_entry == NULL) {
+        FatalError(SC_ERR_MEM_ALLOC,
+                "Error allocating memory for rule "
+                "thresholds (tried to allocate %" PRIu32 " th_entrys for "
+                "rule tracking)",
+                de_ctx->ths_ctx.th_size);
     }
 }
 
index 09af50153610af00ffaeaa69763d713896968372..c4bdd1edbfb828e8ae892e7e6b933bfde18d908d 100644 (file)
@@ -44,7 +44,7 @@ int PacketAlertThreshold(DetectEngineCtx *, DetectEngineThreadCtx *,
         const Signature *, PacketAlert *);
 
 void ThresholdHashInit(DetectEngineCtx *);
-void ThresholdHashRealloc(DetectEngineCtx *);
+void ThresholdHashAllocate(DetectEngineCtx *);
 void ThresholdContextDestroy(DetectEngineCtx *);
 
 int ThresholdHostTimeoutCheck(Host *, struct timeval *);
index 3abe8d263cef4e15c2593704697de14571bcd2a5..baf8aea550b1b2d25daffe169d2c00e79a1971af 100644 (file)
@@ -251,9 +251,6 @@ static int DetectThresholdSetup(DetectEngineCtx *de_ctx, Signature *s, const cha
     if (de == NULL)
         goto error;
 
-    if (de->track == TRACK_RULE)
-        ThresholdHashRealloc(de_ctx);
-
     sm = SigMatchAlloc();
     if (sm == NULL)
         goto error;
@@ -1604,7 +1601,6 @@ static int DetectThresholdTestSig13(void)
 
     SigGroupBuild(de_ctx);
     DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
-    ThresholdHashRealloc(de_ctx);
 
     /* should alert twice */
     SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
index 860682055e9c12db52af2a37296cc6ad8f41dc55..9280016996dbbca0253dd6fd2f4ea34ca5678b8b 100644 (file)
@@ -497,9 +497,6 @@ static int SetupThresholdRule(DetectEngineCtx *de_ctx, uint32_t id, uint32_t gid
                 sm->type = DETECT_THRESHOLD;
             sm->ctx = (void *)de;
 
-            if (parsed_track == TRACK_RULE) {
-                ThresholdHashRealloc(de_ctx);
-            }
             SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_THRESHOLD);
         }
 
@@ -540,9 +537,6 @@ static int SetupThresholdRule(DetectEngineCtx *de_ctx, uint32_t id, uint32_t gid
                     sm->type = DETECT_THRESHOLD;
                 sm->ctx = (void *)de;
 
-                if (parsed_track == TRACK_RULE) {
-                    ThresholdHashRealloc(de_ctx);
-                }
                 SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_THRESHOLD);
             }
         }
@@ -614,10 +608,6 @@ static int SetupThresholdRule(DetectEngineCtx *de_ctx, uint32_t id, uint32_t gid
                 sm->type = DETECT_THRESHOLD;
             sm->ctx = (void *)de;
 
-            if (parsed_track == TRACK_RULE) {
-                ThresholdHashRealloc(de_ctx);
-            }
-
             SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_THRESHOLD);
         }
     }