]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
detect: support multi buffer matching
authorVictor Julien <vjulien@oisf.net>
Tue, 21 Mar 2023 05:37:31 +0000 (06:37 +0100)
committerVictor Julien <vjulien@oisf.net>
Mon, 1 May 2023 19:56:29 +0000 (21:56 +0200)
Multi buffer matching is implemented as a way for a rule to match
on multiple buffers within the same transaction.

Before this patch a rule like:

    dns.query; content:"example"; dns.query; content:".com";

would be equivalent to:

    dns.query; content:"example"; content:".com";

If a DNS query would request more than one name, e.g.:

    DNS: [example.net][something.com]

Eeach would be inspected to have both patterns present. Otherwise,
it would not be a match. So the rule above would not match, as neither
example.net and somthing.com satisfy both conditions at the same time.

This patch changes this behavior. Instead of the above, each time the
sticky buffer is specified, it creates a separate detection unit. Each
buffer is a "multi buffer" sticky buffer will now be evaluated against
each "instance" of the sticky buffer.

To continue with the above example:

    DNS: [example.net] <- matches 'dns.query; content:"example";'
    DNS: [something.com] <- matches 'dns.query; content:".com"'

So this would now be a match.

To make sure both patterns match in a single query string, the expression
'dns.query; content:"example"; content:".com";' still works for this.

This patch doesn't yet enable the behavior for the keywords. That is
done in a follow up patch.

To be able to implement this the internal storage of parsed rules
is changed. Until this patch and array of lists was used, where the
index was the buffer id (e.g. http_uri, dns_query). Therefore there
was only one list of matches per buffer id. As a side effect this
array was always very sparsely populated as many buffers could not
be mixed.

This patch changes the internal representation. The new array is densely
packed:

    dns.query; content:"1"; dns.query; bsize:1; content:"2";

    [type: dns_query][list: content:"1";]
    [type: dns_query][list: bsize:1; content:"2";]

The new scheme allows for multiple instances of the same buffer.
These lists are then translated into multiple inspection engines
during the final setup of the rule.

Ticket: #5784.

28 files changed:
src/detect-bsize.c
src/detect-bsize.h
src/detect-byte-extract.c
src/detect-bytemath.c
src/detect-content.c
src/detect-engine-analyzer.c
src/detect-engine-build.c
src/detect-engine-mpm.c
src/detect-engine-state.c
src/detect-engine.c
src/detect-engine.h
src/detect-fast-pattern.c
src/detect-flowbits.c
src/detect-http-host.c
src/detect-http-method.c
src/detect-http2.c
src/detect-parse.c
src/detect-parse.h
src/detect-quic-cyu-hash.c
src/detect-sip-method.c
src/detect-ssh-hassh-server.c
src/detect-ssh-hassh.c
src/detect-tls-cert-fingerprint.c
src/detect-tls-cert-serial.c
src/detect-tls-ja3-hash.c
src/detect-tls-ja3s-hash.c
src/detect-urilen.c
src/detect.h

index 4c89abc3c6a8fcb166b0ab8218f93e92c28b839a..3b3efe87b7ff841e1a5fdf06d93fe9f830a45b6e 100644 (file)
@@ -45,11 +45,11 @@ static int SigParseGetMaxBsize(DetectU64Data *bsz);
 static void DetectBsizeRegisterTests (void);
 #endif
 
-bool DetectBsizeValidateContentCallback(Signature *s, int list)
+bool DetectBsizeValidateContentCallback(Signature *s, const SignatureInitDataBuffer *b)
 {
     int bsize = -1;
     DetectU64Data *bsz;
-    for (const SigMatch *sm = s->init_data->smlists[list]; sm != NULL; sm = sm->next) {
+    for (const SigMatch *sm = b->head; sm != NULL; sm = sm->next) {
         if (sm->type == DETECT_BSIZE) {
             bsz = (DetectU64Data *)sm->ctx;
             bsize = SigParseGetMaxBsize(bsz);
@@ -64,7 +64,7 @@ bool DetectBsizeValidateContentCallback(Signature *s, int list)
     uint64_t needed;
     if (bsize >= 0) {
         int len, offset;
-        SigParseRequiredContentSize(s, bsize, s->init_data->smlists[list], &len, &offset);
+        SigParseRequiredContentSize(s, bsize, b->head, &len, &offset);
         SCLogDebug("bsize: %d; len: %d; offset: %d [%s]", bsize, len, offset, s->sig_str);
         needed = len;
         if (len > bsize) {
index 1535c6fdb2715f52bddeed93f1a40d7ea649c759..d14bbab056bbcca9c49c1930c05631958a198928 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2017 Open Information Security Foundation
+/* Copyright (C) 2017-2023 Open Information Security Foundation
  *
  * You can copy, redistribute or modify this Program under the terms of
  * the GNU General Public License version 2 as published by the Free
@@ -26,6 +26,6 @@
 
 void DetectBsizeRegister(void);
 int DetectBsizeMatch(const SigMatchCtx *ctx, const uint64_t buffer_size, bool eof);
-bool DetectBsizeValidateContentCallback(Signature *s, int list);
+bool DetectBsizeValidateContentCallback(Signature *s, const SignatureInitDataBuffer *);
 
 #endif /* __DETECT_URILEN_H__ */
index 3f95eb39f47f00dbbf91e3cc19be4bc3d6af37fe..80324fcce63090756bed3c933fe5dee3d5b6e599 100644 (file)
@@ -665,8 +665,20 @@ static void DetectByteExtractFree(DetectEngineCtx *de_ctx, void *ptr)
  */
 SigMatch *DetectByteExtractRetrieveSMVar(const char *arg, const Signature *s)
 {
-    const int nlists = s->init_data->smlists_array_size;
-    for (int list = 0; list < nlists; list++) {
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        SigMatch *sm = s->init_data->buffers[x].head;
+        while (sm != NULL) {
+            if (sm->type == DETECT_BYTE_EXTRACT) {
+                const DetectByteExtractData *bed = (const DetectByteExtractData *)sm->ctx;
+                if (strcmp(bed->name, arg) == 0) {
+                    return sm;
+                }
+            }
+            sm = sm->next;
+        }
+    }
+
+    for (int list = 0; list < DETECT_SM_LIST_MAX; list++) {
         SigMatch *sm = s->init_data->smlists[list];
         while (sm != NULL) {
             if (sm->type == DETECT_BYTE_EXTRACT) {
index 58b157289f93f4dbbb98849f97f65b3b12372fad..abcc1761d45834800413e201842971c12a4c857b 100644 (file)
@@ -410,8 +410,21 @@ static void DetectByteMathFree(DetectEngineCtx *de_ctx, void *ptr)
  */
 SigMatch *DetectByteMathRetrieveSMVar(const char *arg, const Signature *s)
 {
-    const int nlists = s->init_data->smlists_array_size;
-    for (int list = 0; list < nlists; list++) {
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        SigMatch *sm = s->init_data->buffers[x].head;
+        while (sm != NULL) {
+            if (sm->type == DETECT_BYTEMATH) {
+                const DetectByteMathData *bmd = (const DetectByteMathData *)sm->ctx;
+                if (strcmp(bmd->result, arg) == 0) {
+                    SCLogDebug("Retrieved SM for \"%s\"", arg);
+                    return sm;
+                }
+            }
+            sm = sm->next;
+        }
+    }
+
+    for (int list = 0; list < DETECT_SM_LIST_MAX; list++) {
         SigMatch *sm = s->init_data->smlists[list];
         while (sm != NULL) {
             if (sm->type == DETECT_BYTEMATH) {
index 8dfa544fbf5672be123d291a13593c4b4168df31..536366e940b4c004638314301d7d300f98bcd082 100644 (file)
@@ -710,9 +710,9 @@ static void PropagateLimits(Signature *s, SigMatch *sm_head)
 
 void DetectContentPropagateLimits(Signature *s)
 {
-    for (uint32_t list = 0; list < s->init_data->smlists_array_size; list++) {
-        SigMatch *sm = s->init_data->smlists[list];
-        PropagateLimits(s, sm);
+    PropagateLimits(s, s->init_data->smlists[DETECT_SM_LIST_PMATCH]);
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        PropagateLimits(s, s->init_data->buffers[x].head);
     }
 }
 
index beb736f471f9889475017eb5a721b0e3a485dc4f..0ccc408ef7d5c98a1467aae58d6a339765d18748 100644 (file)
@@ -1321,7 +1321,7 @@ void EngineAnalysisRules(const DetectEngineCtx *de_ctx,
         rule_ipv6_only += 1;
     }
 
-    for (list_id = 0; list_id < (int)s->init_data->smlists_array_size; list_id++) {
+    for (list_id = 0; list_id < DETECT_SM_LIST_MAX; list_id++) {
         SigMatch *sm = NULL;
         for (sm = s->init_data->smlists[list_id]; sm != NULL; sm = sm->next) {
             int16_t item_slot = analyzer_item_map[list_id];
@@ -1385,11 +1385,10 @@ void EngineAnalysisRules(const DetectEngineCtx *de_ctx,
                     rule_flags = 1;
                 }
             }
-        } /* for (sm = s->sm_lists[list_id]; sm != NULL; sm = sm->next) */
+        } /* for (sm = s->init_data->smlists[list_id]; sm != NULL; sm = sm->next) */
 
     } /* for ( ; list_id < DETECT_SM_LIST_MAX; list_id++) */
 
-
     if (file_store && !RequiresFeature("output::file-store")) {
         rule_warning += 1;
         warn_file_store_not_present = 1;
index 87e83008fc9d07b9cbbe825ed3e3690610e9b888..f12fef1cbce6c5b7f9e13dc3eecca897be97a491 100644 (file)
@@ -185,6 +185,11 @@ int SignatureIsFilesizeInspecting(const Signature *s)
     return 0;
 }
 
+static bool SignatureInspectsBuffers(const Signature *s)
+{
+    return (s->init_data->buffer_index > 0);
+}
+
 /** \brief Test is a initialized signature is IP only
  *  \param de_ctx detection engine ctx
  *  \param s the signature
@@ -211,13 +216,7 @@ int SignatureIsIPOnly(DetectEngineCtx *de_ctx, const Signature *s)
         return 0;
 
     /* for now assume that all registered buffer types are incompatible */
-    const int nlists = s->init_data->smlists_array_size;
-    for (int i = 0; i < nlists; i++) {
-        if (s->init_data->smlists[i] == NULL)
-            continue;
-        if (!(DetectEngineBufferTypeGetNameById(de_ctx, i)))
-            continue;
-
+    if (SignatureInspectsBuffers(s)) {
         SCReturnInt(0);
     }
 
@@ -275,13 +274,7 @@ static int SignatureIsPDOnly(const DetectEngineCtx *de_ctx, const Signature *s)
         return 0;
 
     /* for now assume that all registered buffer types are incompatible */
-    const int nlists = s->init_data->smlists_array_size;
-    for (int i = 0; i < nlists; i++) {
-        if (s->init_data->smlists[i] == NULL)
-            continue;
-        if (!(DetectEngineBufferTypeGetNameById(de_ctx, i)))
-            continue;
-
+    if (SignatureInspectsBuffers(s)) {
         SCReturnInt(0);
     }
 
@@ -362,13 +355,7 @@ static int SignatureIsDEOnly(DetectEngineCtx *de_ctx, const Signature *s)
     }
 
     /* for now assume that all registered buffer types are incompatible */
-    const int nlists = s->init_data->smlists_array_size;
-    for (int i = 0; i < nlists; i++) {
-        if (s->init_data->smlists[i] == NULL)
-            continue;
-        if (!(DetectEngineBufferTypeGetNameById(de_ctx, i)))
-            continue;
-
+    if (SignatureInspectsBuffers(s)) {
         SCReturnInt(0);
     }
 
@@ -461,16 +448,11 @@ static bool SignatureNeedsDCERPCMask(const Signature *s)
         SCLogDebug("g_dce_stub_data_buffer_id %d", g_dce_stub_data_buffer_id);
     }
 
-    if (g_dce_generic_list_id >= 0 &&
-            s->init_data->smlists[g_dce_generic_list_id] != NULL)
-    {
-        return true;
-    }
-    if (g_dce_stub_data_buffer_id >= 0 &&
-            s->init_data->smlists[g_dce_stub_data_buffer_id] != NULL)
-    {
+    if (DetectBufferIsPresent(s, g_dce_generic_list_id) ||
+            DetectBufferIsPresent(s, g_dce_stub_data_buffer_id)) {
         return true;
     }
+
     return false;
 }
 
@@ -1430,8 +1412,10 @@ int SigAddressPrepareStage1(DetectEngineCtx *de_ctx)
         {
             int prefilter_list = DETECT_TBLSIZE;
 
+            // TODO buffers?
+
             /* get the keyword supporting prefilter with the lowest type */
-            for (int i = 0; i < (int)s->init_data->smlists_array_size; i++) {
+            for (int i = 0; i < DETECT_SM_LIST_MAX; i++) {
                 for (SigMatch *sm = s->init_data->smlists[i]; sm != NULL; sm = sm->next) {
                     if (sigmatch_table[sm->type].SupportsPrefilter != NULL) {
                         if (sigmatch_table[sm->type].SupportsPrefilter(s)) {
@@ -1443,7 +1427,7 @@ int SigAddressPrepareStage1(DetectEngineCtx *de_ctx)
 
             /* apply that keyword as prefilter */
             if (prefilter_list != DETECT_TBLSIZE) {
-                for (int i = 0; i < (int)s->init_data->smlists_array_size; i++) {
+                for (int i = 0; i < DETECT_SM_LIST_MAX; i++) {
                     for (SigMatch *sm = s->init_data->smlists[i]; sm != NULL; sm = sm->next) {
                         if (sm->type == prefilter_list) {
                             s->init_data->prefilter_sm = sm;
@@ -1457,10 +1441,13 @@ int SigAddressPrepareStage1(DetectEngineCtx *de_ctx)
         }
 
         /* run buffer type callbacks if any */
-        for (int x = 0; x < (int)s->init_data->smlists_array_size; x++) {
+        for (int x = 0; x < DETECT_SM_LIST_MAX; x++) {
             if (s->init_data->smlists[x])
                 DetectEngineBufferRunSetupCallback(de_ctx, x, s);
         }
+        for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+            DetectEngineBufferRunSetupCallback(de_ctx, s->init_data->buffers[x].id, s);
+        }
 
         de_ctx->sig_cnt++;
     }
@@ -1902,7 +1889,7 @@ static int SigMatchPrepare(DetectEngineCtx *de_ctx)
         }
         /* free lists. Ctx' are xferred to sm_arrays so won't get freed */
         uint32_t i;
-        for (i = 0; i < s->init_data->smlists_array_size; i++) {
+        for (i = 0; i < DETECT_SM_LIST_MAX; i++) {
             SigMatch *sm = s->init_data->smlists[i];
             while (sm != NULL) {
                 SigMatch *nsm = sm->next;
@@ -1910,8 +1897,6 @@ static int SigMatchPrepare(DetectEngineCtx *de_ctx)
                 sm = nsm;
             }
         }
-        SCFree(s->init_data->smlists);
-        SCFree(s->init_data->smlists_tail);
         for (i = 0; i < (uint32_t)s->init_data->transforms.cnt; i++) {
             if (s->init_data->transforms.transforms[i].options) {
                 int transform = s->init_data->transforms.transforms[i].transform;
@@ -1920,6 +1905,15 @@ static int SigMatchPrepare(DetectEngineCtx *de_ctx)
                 s->init_data->transforms.transforms[i].options = NULL;
             }
         }
+        for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+            SigMatch *sm = s->init_data->buffers[x].head;
+            while (sm != NULL) {
+                SigMatch *nsm = sm->next;
+                SigMatchFree(de_ctx, sm);
+                sm = nsm;
+            }
+        }
+        SCFree(s->init_data->buffers);
         SCFree(s->init_data);
         s->init_data = NULL;
     }
index 79c780eda0b92f5c7d4893be571b674e6d085155..d6b8dd92f651b5df0460994733a80a243d802d27 100644 (file)
@@ -1033,10 +1033,10 @@ static void SetMpm(Signature *s, SigMatch *mpm_sm, const int mpm_sm_list)
     return;
 }
 
-static SigMatch *GetMpmForList(const Signature *s, const int list, SigMatch *mpm_sm,
-    uint16_t max_len, bool skip_negated_content)
+static SigMatch *GetMpmForList(const Signature *s, SigMatch *list, SigMatch *mpm_sm,
+        uint16_t max_len, bool skip_negated_content)
 {
-    for (SigMatch *sm = s->init_data->smlists[list]; sm != NULL; sm = sm->next) {
+    for (SigMatch *sm = list; sm != NULL; sm = sm->next) {
         if (sm->type != DETECT_CONTENT)
             continue;
 
@@ -1045,9 +1045,10 @@ static SigMatch *GetMpmForList(const Signature *s, const int list, SigMatch *mpm
          * non-negated content present in the sig */
         if ((cd->flags & DETECT_CONTENT_NEGATED) && skip_negated_content)
             continue;
-        if (cd->content_len != max_len)
+        if (cd->content_len != max_len) {
+            SCLogDebug("content_len %u != max_len %u", cd->content_len, max_len);
             continue;
-
+        }
         if (mpm_sm == NULL) {
             mpm_sm = sm;
         } else {
@@ -1074,7 +1075,7 @@ void RetrieveFPForSig(const DetectEngineCtx *de_ctx, Signature *s)
     if (s->init_data->mpm_sm != NULL)
         return;
 
-    const int nlists = s->init_data->smlists_array_size;
+    const int nlists = s->init_data->max_content_list_id + 1;
     int pos_sm_list[nlists];
     int neg_sm_list[nlists];
     memset(pos_sm_list, 0, nlists * sizeof(int));
@@ -1084,18 +1085,42 @@ void RetrieveFPForSig(const DetectEngineCtx *de_ctx, Signature *s)
 
     /* inspect rule to see if we have the fast_pattern reg to
      * force using a sig, otherwise keep stats about the patterns */
-    for (int list_id = DETECT_SM_LIST_PMATCH; list_id < nlists; list_id++) {
-        if (s->init_data->smlists[list_id] == NULL)
-            continue;
+    if (s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL) {
+        if (FastPatternSupportEnabledForSigMatchList(de_ctx, DETECT_SM_LIST_PMATCH)) {
+            for (SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_PMATCH]; sm != NULL;
+                    sm = sm->next) {
+                if (sm->type != DETECT_CONTENT)
+                    continue;
 
-        if (list_id == DETECT_SM_LIST_POSTMATCH || list_id == DETECT_SM_LIST_TMATCH ||
-                list_id == DETECT_SM_LIST_SUPPRESS || list_id == DETECT_SM_LIST_THRESHOLD)
-            continue;
+                const DetectContentData *cd = (DetectContentData *)sm->ctx;
+                /* fast_pattern set in rule, so using this pattern */
+                if ((cd->flags & DETECT_CONTENT_FAST_PATTERN)) {
+                    SetMpm(s, sm, DETECT_SM_LIST_PMATCH);
+                    return;
+                }
 
-        if (!FastPatternSupportEnabledForSigMatchList(de_ctx, list_id))
+                if (cd->flags & DETECT_CONTENT_NEGATED) {
+                    neg_sm_list[DETECT_SM_LIST_PMATCH] = 1;
+                    neg_sm_list_cnt++;
+                } else {
+                    pos_sm_list[DETECT_SM_LIST_PMATCH] = 1;
+                    pos_sm_list_cnt++;
+                }
+            }
+        }
+    }
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        const int list_id = s->init_data->buffers[x].id;
+
+        SCLogDebug("%u: list_id %d: %s", s->id, list_id,
+                DetectEngineBufferTypeGetNameById(de_ctx, list_id));
+
+        if (!FastPatternSupportEnabledForSigMatchList(de_ctx, list_id)) {
+            SCLogDebug("skip");
             continue;
+        }
 
-        for (SigMatch *sm = s->init_data->smlists[list_id]; sm != NULL; sm = sm->next) {
+        for (SigMatch *sm = s->init_data->buffers[x].head; sm != NULL; sm = sm->next) {
             if (sm->type != DETECT_CONTENT)
                 continue;
 
@@ -1112,10 +1137,14 @@ void RetrieveFPForSig(const DetectEngineCtx *de_ctx, Signature *s)
             } else {
                 pos_sm_list[list_id] = 1;
                 pos_sm_list_cnt++;
+                SCLogDebug("pos added for %d", list_id);
             }
         }
+        SCLogDebug("ok");
     }
 
+    SCLogDebug("neg_sm_list_cnt %d pos_sm_list_cnt %d", neg_sm_list_cnt, pos_sm_list_cnt);
+
     /* prefer normal not-negated over negated */
     int *curr_sm_list = NULL;
     int skip_negated_content = 1;
@@ -1146,43 +1175,86 @@ void RetrieveFPForSig(const DetectEngineCtx *de_ctx, Signature *s)
             if (curr_sm_list[tmp->list_id] == 0)
                 continue;
             final_sm_list[count_final_sm_list++] = tmp->list_id;
+            SCLogDebug("tmp->list_id %d", tmp->list_id);
         }
         if (count_final_sm_list != 0)
             break;
     }
 
     BUG_ON(count_final_sm_list == 0);
+    SCLogDebug("count_final_sm_list %d skip_negated_content %d", count_final_sm_list,
+            skip_negated_content);
 
     uint16_t max_len = 0;
     for (int i = 0; i < count_final_sm_list; i++) {
-        if (final_sm_list[i] >= (int)s->init_data->smlists_array_size)
-            continue;
+        SCLogDebug("i %d final_sm_list[i] %d", i, final_sm_list[i]);
 
-        for (SigMatch *sm = s->init_data->smlists[final_sm_list[i]]; sm != NULL; sm = sm->next) {
-            if (sm->type != DETECT_CONTENT)
-                continue;
+        if (final_sm_list[i] == DETECT_SM_LIST_PMATCH) {
+            for (SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_PMATCH]; sm != NULL;
+                    sm = sm->next) {
+                if (sm->type != DETECT_CONTENT)
+                    continue;
 
-            const DetectContentData *cd = (DetectContentData *)sm->ctx;
-            /* skip_negated_content is only set if there's absolutely no
-             * non-negated content present in the sig */
-            if ((cd->flags & DETECT_CONTENT_NEGATED) && skip_negated_content)
-                continue;
-            if (max_len < cd->content_len)
-                max_len = cd->content_len;
+                const DetectContentData *cd = (DetectContentData *)sm->ctx;
+                /* skip_negated_content is only set if there's absolutely no
+                 * non-negated content present in the sig */
+                if ((cd->flags & DETECT_CONTENT_NEGATED) && skip_negated_content)
+                    continue;
+                max_len = MAX(max_len, cd->content_len);
+            }
+        } else {
+            for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+                const int list_id = s->init_data->buffers[x].id;
+                if (final_sm_list[i] == list_id) {
+                    SCLogDebug("%u: list_id %d: %s", s->id, list_id,
+                            DetectEngineBufferTypeGetNameById(de_ctx, list_id));
+
+                    for (SigMatch *sm = s->init_data->buffers[x].head; sm != NULL; sm = sm->next) {
+                        if (sm->type != DETECT_CONTENT)
+                            continue;
+
+                        const DetectContentData *cd = (DetectContentData *)sm->ctx;
+                        /* skip_negated_content is only set if there's absolutely no
+                         * non-negated content present in the sig */
+                        if ((cd->flags & DETECT_CONTENT_NEGATED) && skip_negated_content)
+                            continue;
+                        max_len = MAX(max_len, cd->content_len);
+                    }
+                }
+            }
         }
     }
 
     SigMatch *mpm_sm = NULL;
     int mpm_sm_list = -1;
     for (int i = 0; i < count_final_sm_list; i++) {
-        if (final_sm_list[i] >= (int)s->init_data->smlists_array_size)
-            continue;
-
-        /* GetMpmForList may keep `mpm_sm` the same, so track if it changed */
-        SigMatch *prev_mpm_sm = mpm_sm;
-        mpm_sm = GetMpmForList(s, final_sm_list[i], mpm_sm, max_len, skip_negated_content);
-        if (mpm_sm != prev_mpm_sm) {
-            mpm_sm_list = final_sm_list[i];
+        SCLogDebug("i %d", i);
+        if (final_sm_list[i] == DETECT_SM_LIST_PMATCH) {
+            /* GetMpmForList may keep `mpm_sm` the same, so track if it changed */
+            SigMatch *prev_mpm_sm = mpm_sm;
+            mpm_sm = GetMpmForList(s, s->init_data->smlists[DETECT_SM_LIST_PMATCH], mpm_sm, max_len,
+                    skip_negated_content);
+            if (mpm_sm != prev_mpm_sm) {
+                mpm_sm_list = final_sm_list[i];
+            }
+        } else {
+            SCLogDebug(
+                    "%u: %s", s->id, DetectEngineBufferTypeGetNameById(de_ctx, final_sm_list[i]));
+            for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+                const int list_id = s->init_data->buffers[x].id;
+                if (final_sm_list[i] == list_id) {
+                    SCLogDebug("%u: list_id %d: %s", s->id, list_id,
+                            DetectEngineBufferTypeGetNameById(de_ctx, list_id));
+                    /* GetMpmForList may keep `mpm_sm` the same, so track if it changed */
+                    SigMatch *prev_mpm_sm = mpm_sm;
+                    mpm_sm = GetMpmForList(s, s->init_data->buffers[x].head, mpm_sm, max_len,
+                            skip_negated_content);
+                    SCLogDebug("mpm_sm %p from %p", mpm_sm, s->init_data->buffers[x].head);
+                    if (mpm_sm != prev_mpm_sm) {
+                        mpm_sm_list = list_id;
+                    }
+                }
+            }
         }
     }
 
@@ -2444,6 +2516,7 @@ void EngineAnalysisAddAllRulePatterns(DetectEngineCtx *de_ctx, const Signature *
 
     const DetectEngineAppInspectionEngine *app = s->app_inspect;
     for (; app != NULL; app = app->next) {
+        DEBUG_VALIDATE_BUG_ON(app->smd == NULL);
         SigMatchData *smd = app->smd;
         do {
             switch (smd->type) {
index db7cfc0ace0c0a9240de71ed3012afd5596eb236..aabb02f74d7a3aefa622a00d7671449b7359a253 100644 (file)
@@ -622,7 +622,7 @@ static int DeStateSigTest02(void)
     FAIL_IF_NULL(tx_de_state);
     FAIL_IF(tx_de_state->dir_state[0].cnt != 1);
     /* http_header(mpm): 5, uri: 3, method: 6, cookie: 7 */
-    uint32_t expected_flags = (BIT_U32(5) | BIT_U32(3) | BIT_U32(6) |BIT_U32(7));
+    uint32_t expected_flags = (BIT_U32(5) | BIT_U32(3) | BIT_U32(6) | BIT_U32(4));
     FAIL_IF(tx_de_state->dir_state[0].head->store[0].flags != expected_flags);
 
     r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf4, httplen4);
index 23341087fdbe7dc8dc7e3498362fd7e5495b9c1c..db1bd3b842d0316edb15e833bea7f3e3ac218937 100644 (file)
@@ -233,6 +233,7 @@ void DetectAppLayerInspectEngineRegister2(const char *name,
     if (sm_list == -1) {
         FatalError("failed to register inspect engine %s", name);
     }
+    SCLogDebug("name %s id %d", name, sm_list);
 
     if ((alproto >= ALPROTO_FAILED) ||
         (!(dir == SIG_FLAG_TOSERVER || dir == SIG_FLAG_TOCLIENT)) ||
@@ -574,222 +575,237 @@ static void AppendStreamInspectEngine(
     SCLogDebug("sid %u: engine %p/%u added", s->id, new_engine, new_engine->id);
 }
 
-/**
- *  \note for the file inspect engine, the id DE_STATE_ID_FILE_INSPECT
- *        is assigned.
- */
-int DetectEngineAppInspectionEngine2Signature(DetectEngineCtx *de_ctx, Signature *s)
+static void AppendFrameInspectEngine(DetectEngineCtx *de_ctx,
+        const DetectEngineFrameInspectionEngine *u, Signature *s, SigMatchData *smd,
+        const int mpm_list)
 {
-    const int nlists = s->init_data->smlists_array_size;
-    SigMatchData *ptrs[nlists];
-    memset(&ptrs, 0, (nlists * sizeof(SigMatchData *)));
+    bool prepend = false;
 
-    const int mpm_list = s->init_data->mpm_sm ? s->init_data->mpm_sm_list : -1;
+    if (u->alproto == ALPROTO_UNKNOWN) {
+        /* special case, inspect engine applies to all protocols */
+    } else if (s->alproto != ALPROTO_UNKNOWN && !AppProtoEquals(s->alproto, u->alproto))
+        return;
 
-    const int files_id = DetectBufferTypeGetByName("files");
+    if (s->flags & SIG_FLAG_TOSERVER && !(s->flags & SIG_FLAG_TOCLIENT)) {
+        if (u->dir == 1)
+            return;
+    } else if (s->flags & SIG_FLAG_TOCLIENT && !(s->flags & SIG_FLAG_TOSERVER)) {
+        if (u->dir == 0)
+            return;
+    }
 
-    /* convert lists to SigMatchData arrays */
-    int i = 0;
-    for (i = DETECT_SM_LIST_DYNAMIC_START; i < nlists; i++) {
-        if (s->init_data->smlists[i] == NULL)
-            continue;
+    DetectEngineFrameInspectionEngine *new_engine =
+            SCCalloc(1, sizeof(DetectEngineFrameInspectionEngine));
+    if (unlikely(new_engine == NULL)) {
+        exit(EXIT_FAILURE);
+    }
+    if (mpm_list == u->sm_list) {
+        SCLogDebug("%s is mpm", DetectEngineBufferTypeGetNameById(de_ctx, u->sm_list));
+        prepend = true;
+        new_engine->mpm = true;
+    }
 
-        ptrs[i] = SigMatchList2DataArray(s->init_data->smlists[i]);
-        SCLogDebug("ptrs[%d] is set", i);
-    }
-
-    /* set up inspect engines */
-    const DetectEngineFrameInspectionEngine *u = de_ctx->frame_inspect_engines;
-    while (u != NULL) {
-        SCLogDebug("u %p sm_list %u nlists %u ptrs[] %p", u, u->sm_list, nlists,
-                u->sm_list < nlists ? ptrs[u->sm_list] : NULL);
-        if (u->sm_list < nlists && ptrs[u->sm_list] != NULL) {
-            bool prepend = false;
-
-            if (u->alproto == ALPROTO_UNKNOWN) {
-                /* special case, inspect engine applies to all protocols */
-            } else if (s->alproto != ALPROTO_UNKNOWN && !AppProtoEquals(s->alproto, u->alproto))
-                goto next_engine;
-
-            if (s->flags & SIG_FLAG_TOSERVER && !(s->flags & SIG_FLAG_TOCLIENT)) {
-                if (u->dir == 1)
-                    goto next_engine;
-            } else if (s->flags & SIG_FLAG_TOCLIENT && !(s->flags & SIG_FLAG_TOSERVER)) {
-                if (u->dir == 0)
-                    goto next_engine;
-            }
-            DetectEngineFrameInspectionEngine *new_engine =
-                    SCCalloc(1, sizeof(DetectEngineFrameInspectionEngine));
-            if (unlikely(new_engine == NULL)) {
-                exit(EXIT_FAILURE);
-            }
-            if (mpm_list == u->sm_list) {
-                SCLogDebug("%s is mpm", DetectEngineBufferTypeGetNameById(de_ctx, u->sm_list));
-                prepend = true;
-                new_engine->mpm = true;
-            }
+    new_engine->type = u->type;
+    new_engine->sm_list = u->sm_list;
+    new_engine->sm_list_base = u->sm_list_base;
+    new_engine->smd = smd;
+    new_engine->v1 = u->v1;
+    SCLogDebug("sm_list %d new_engine->v1 %p/%p", new_engine->sm_list, new_engine->v1.Callback,
+            new_engine->v1.transforms);
 
-            new_engine->type = u->type;
-            new_engine->sm_list = u->sm_list;
-            new_engine->sm_list_base = u->sm_list_base;
-            new_engine->smd = ptrs[new_engine->sm_list];
-            new_engine->v1 = u->v1;
-            SCLogDebug("sm_list %d new_engine->v1 %p/%p", new_engine->sm_list,
-                    new_engine->v1.Callback, new_engine->v1.transforms);
-
-            if (s->frame_inspect == NULL) {
-                s->frame_inspect = new_engine;
-            } else if (prepend) {
-                new_engine->next = s->frame_inspect;
-                s->frame_inspect = new_engine;
-            } else {
-                DetectEngineFrameInspectionEngine *a = s->frame_inspect;
-                while (a->next != NULL) {
-                    a = a->next;
-                }
-                new_engine->next = a->next;
-                a->next = new_engine;
-            }
+    if (s->frame_inspect == NULL) {
+        s->frame_inspect = new_engine;
+    } else if (prepend) {
+        new_engine->next = s->frame_inspect;
+        s->frame_inspect = new_engine;
+    } else {
+        DetectEngineFrameInspectionEngine *a = s->frame_inspect;
+        while (a->next != NULL) {
+            a = a->next;
         }
-    next_engine:
-        u = u->next;
+        new_engine->next = a->next;
+        a->next = new_engine;
     }
+}
 
-    /* set up pkt inspect engines */
-    const DetectEnginePktInspectionEngine *e = de_ctx->pkt_inspect_engines;
-    while (e != NULL) {
-        SCLogDebug("e %p sm_list %u nlists %u ptrs[] %p", e, e->sm_list, nlists, e->sm_list < nlists ? ptrs[e->sm_list] : NULL);
-        if (e->sm_list < nlists && ptrs[e->sm_list] != NULL) {
-            bool prepend = false;
+static void AppendPacketInspectEngine(DetectEngineCtx *de_ctx,
+        const DetectEnginePktInspectionEngine *e, Signature *s, SigMatchData *smd,
+        const int mpm_list)
+{
+    bool prepend = false;
 
-            DetectEnginePktInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEnginePktInspectionEngine));
-            if (unlikely(new_engine == NULL)) {
-                exit(EXIT_FAILURE);
-            }
-            if (mpm_list == e->sm_list) {
-                SCLogDebug("%s is mpm", DetectEngineBufferTypeGetNameById(de_ctx, e->sm_list));
-                prepend = true;
-                new_engine->mpm = true;
-            }
+    DetectEnginePktInspectionEngine *new_engine =
+            SCCalloc(1, sizeof(DetectEnginePktInspectionEngine));
+    if (unlikely(new_engine == NULL)) {
+        exit(EXIT_FAILURE);
+    }
+    if (mpm_list == e->sm_list) {
+        SCLogDebug("%s is mpm", DetectEngineBufferTypeGetNameById(de_ctx, e->sm_list));
+        prepend = true;
+        new_engine->mpm = true;
+    }
 
-            new_engine->sm_list = e->sm_list;
-            new_engine->sm_list_base = e->sm_list_base;
-            new_engine->smd = ptrs[new_engine->sm_list];
-            new_engine->v1 = e->v1;
-            SCLogDebug("sm_list %d new_engine->v1 %p/%p/%p",
-                    new_engine->sm_list, new_engine->v1.Callback,
-                    new_engine->v1.GetData, new_engine->v1.transforms);
-
-            if (s->pkt_inspect == NULL) {
-                s->pkt_inspect = new_engine;
-            } else if (prepend) {
-                new_engine->next = s->pkt_inspect;
-                s->pkt_inspect = new_engine;
-            } else {
-                DetectEnginePktInspectionEngine *a = s->pkt_inspect;
-                while (a->next != NULL) {
-                    a = a->next;
-                }
-                new_engine->next = a->next;
-                a->next = new_engine;
-            }
+    new_engine->sm_list = e->sm_list;
+    new_engine->sm_list_base = e->sm_list_base;
+    new_engine->smd = smd;
+    new_engine->v1 = e->v1;
+    SCLogDebug("sm_list %d new_engine->v1 %p/%p/%p", new_engine->sm_list, new_engine->v1.Callback,
+            new_engine->v1.GetData, new_engine->v1.transforms);
+
+    if (s->pkt_inspect == NULL) {
+        s->pkt_inspect = new_engine;
+    } else if (prepend) {
+        new_engine->next = s->pkt_inspect;
+        s->pkt_inspect = new_engine;
+    } else {
+        DetectEnginePktInspectionEngine *a = s->pkt_inspect;
+        while (a->next != NULL) {
+            a = a->next;
         }
-        e = e->next;
+        new_engine->next = a->next;
+        a->next = new_engine;
     }
+}
 
-    bool head_is_mpm = false;
-    uint8_t last_id = DE_STATE_FLAG_BASE;
-    const DetectEngineAppInspectionEngine *t = de_ctx->app_inspect_engines;
-    while (t != NULL) {
-        bool prepend = false;
+static void AppendAppInspectEngine(DetectEngineCtx *de_ctx,
+        const DetectEngineAppInspectionEngine *t, Signature *s, SigMatchData *smd,
+        const int mpm_list, const int files_id, uint8_t *last_id, bool *head_is_mpm)
+{
+    if (t->alproto == ALPROTO_UNKNOWN) {
+        /* special case, inspect engine applies to all protocols */
+    } else if (s->alproto != ALPROTO_UNKNOWN && !AppProtoEquals(s->alproto, t->alproto))
+        return;
 
-        if (t->sm_list >= nlists)
-            goto next;
+    if (s->flags & SIG_FLAG_TOSERVER && !(s->flags & SIG_FLAG_TOCLIENT)) {
+        if (t->dir == 1)
+            return;
+    } else if (s->flags & SIG_FLAG_TOCLIENT && !(s->flags & SIG_FLAG_TOSERVER)) {
+        if (t->dir == 0)
+            return;
+    }
+    SCLogDebug("app engine: t %p t->id %u => alproto:%s files:%s", t, t->id,
+            AppProtoToString(t->alproto), BOOL2STR(t->sm_list == files_id));
 
-        if (ptrs[t->sm_list] == NULL)
-            goto next;
+    DetectEngineAppInspectionEngine *new_engine =
+            SCCalloc(1, sizeof(DetectEngineAppInspectionEngine));
+    if (unlikely(new_engine == NULL)) {
+        exit(EXIT_FAILURE);
+    }
+    bool prepend = false;
+    if (mpm_list == t->sm_list) {
+        SCLogDebug("%s is mpm", DetectEngineBufferTypeGetNameById(de_ctx, t->sm_list));
+        prepend = true;
+        *head_is_mpm = true;
+        new_engine->mpm = true;
+    }
 
-        SCLogDebug("ptrs[%d] is set", t->sm_list);
+    new_engine->alproto = t->alproto;
+    new_engine->dir = t->dir;
+    new_engine->sm_list = t->sm_list;
+    new_engine->sm_list_base = t->sm_list_base;
+    new_engine->smd = smd;
+    new_engine->progress = t->progress;
+    new_engine->v2 = t->v2;
+    SCLogDebug("sm_list %d new_engine->v2 %p/%p/%p", new_engine->sm_list, new_engine->v2.Callback,
+            new_engine->v2.GetData, new_engine->v2.transforms);
 
-        if (t->alproto == ALPROTO_UNKNOWN) {
-            /* special case, inspect engine applies to all protocols */
-        } else if (s->alproto != ALPROTO_UNKNOWN && !AppProtoEquals(s->alproto, t->alproto))
-            goto next;
+    if (s->app_inspect == NULL) {
+        s->app_inspect = new_engine;
+        if (new_engine->sm_list == files_id) {
+            new_engine->id = DE_STATE_ID_FILE_INSPECT;
+            SCLogDebug("sid %u: engine %p/%u is FILE ENGINE", s->id, new_engine, new_engine->id);
+        } else {
+            new_engine->id = DE_STATE_FLAG_BASE; /* id is used as flag in stateful detect */
+            SCLogDebug("sid %u: engine %p/%u %s", s->id, new_engine, new_engine->id,
+                    DetectEngineBufferTypeGetNameById(de_ctx, t->sm_list));
+        }
 
-        if (s->flags & SIG_FLAG_TOSERVER && !(s->flags & SIG_FLAG_TOCLIENT)) {
-            if (t->dir == 1)
-                goto next;
-        } else if (s->flags & SIG_FLAG_TOCLIENT && !(s->flags & SIG_FLAG_TOSERVER)) {
-            if (t->dir == 0)
-                goto next;
+        /* prepend engine if forced or if our engine has a lower progress. */
+    } else if (prepend || (!(*head_is_mpm) && s->app_inspect->progress > new_engine->progress)) {
+        new_engine->next = s->app_inspect;
+        s->app_inspect = new_engine;
+        if (new_engine->sm_list == files_id) {
+            new_engine->id = DE_STATE_ID_FILE_INSPECT;
+            SCLogDebug("sid %u: engine %p/%u is FILE ENGINE", s->id, new_engine, new_engine->id);
+        } else {
+            new_engine->id = ++(*last_id);
+            SCLogDebug("sid %u: engine %p/%u %s", s->id, new_engine, new_engine->id,
+                    DetectEngineBufferTypeGetNameById(de_ctx, t->sm_list));
         }
-        DetectEngineAppInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEngineAppInspectionEngine));
-        if (unlikely(new_engine == NULL)) {
-            exit(EXIT_FAILURE);
+
+    } else {
+        DetectEngineAppInspectionEngine *a = s->app_inspect;
+        while (a->next != NULL) {
+            if (a->next && a->next->progress > new_engine->progress) {
+                break;
+            }
+            a = a->next;
         }
-        if (mpm_list == t->sm_list) {
-            SCLogDebug("%s is mpm", DetectEngineBufferTypeGetNameById(de_ctx, t->sm_list));
-            prepend = true;
-            head_is_mpm = true;
-            new_engine->mpm = true;
+
+        new_engine->next = a->next;
+        a->next = new_engine;
+        if (new_engine->sm_list == files_id) {
+            new_engine->id = DE_STATE_ID_FILE_INSPECT;
+            SCLogDebug("sid %u: engine %p/%u is FILE ENGINE", s->id, new_engine, new_engine->id);
+        } else {
+            new_engine->id = ++(*last_id);
+            SCLogDebug("sid %u: engine %p/%u %s", s->id, new_engine, new_engine->id,
+                    DetectEngineBufferTypeGetNameById(de_ctx, t->sm_list));
         }
+    }
 
-        new_engine->alproto = t->alproto;
-        new_engine->dir = t->dir;
-        new_engine->sm_list = t->sm_list;
-        new_engine->sm_list_base = t->sm_list_base;
-        new_engine->smd = ptrs[new_engine->sm_list];
-        new_engine->progress = t->progress;
-        new_engine->v2 = t->v2;
-        SCLogDebug("sm_list %d new_engine->v2 %p/%p/%p",
-                new_engine->sm_list, new_engine->v2.Callback,
-                new_engine->v2.GetData, new_engine->v2.transforms);
-
-        if (s->app_inspect == NULL) {
-            s->app_inspect = new_engine;
-            if (new_engine->sm_list == files_id) {
-                SCLogDebug("sid %u: engine %p/%u is FILE ENGINE", s->id, new_engine, new_engine->id);
-                new_engine->id = DE_STATE_ID_FILE_INSPECT;
-            } else {
-                new_engine->id = DE_STATE_FLAG_BASE; /* id is used as flag in stateful detect */
-            }
+    SCLogDebug("sid %u: engine %p/%u added", s->id, new_engine, new_engine->id);
 
-        /* prepend engine if forced or if our engine has a lower progress. */
-        } else if (prepend || (!head_is_mpm && s->app_inspect->progress > new_engine->progress)) {
-            new_engine->next = s->app_inspect;
-            s->app_inspect = new_engine;
-            if (new_engine->sm_list == files_id) {
-                SCLogDebug("sid %u: engine %p/%u is FILE ENGINE", s->id, new_engine, new_engine->id);
-                new_engine->id = DE_STATE_ID_FILE_INSPECT;
-            } else {
-                new_engine->id = ++last_id;
-            }
+    s->init_data->init_flags |= SIG_FLAG_INIT_STATE_MATCH;
+}
 
-        } else {
-            DetectEngineAppInspectionEngine *a = s->app_inspect;
-            while (a->next != NULL) {
-                if (a->next && a->next->progress > new_engine->progress) {
-                    break;
-                }
+/**
+ *  \note for the file inspect engine, the id DE_STATE_ID_FILE_INSPECT
+ *        is assigned.
+ */
+int DetectEngineAppInspectionEngine2Signature(DetectEngineCtx *de_ctx, Signature *s)
+{
+    const int mpm_list = s->init_data->mpm_sm ? s->init_data->mpm_sm_list : -1;
+    const int files_id = DetectBufferTypeGetByName("files");
+    bool head_is_mpm = false;
+    uint8_t last_id = DE_STATE_FLAG_BASE;
 
-                a = a->next;
-            }
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        SigMatchData *smd = SigMatchList2DataArray(s->init_data->buffers[x].head);
+        SCLogDebug("smd %p, id %u", smd, s->init_data->buffers[x].id);
 
-            new_engine->next = a->next;
-            a->next = new_engine;
-            if (new_engine->sm_list == files_id) {
-                SCLogDebug("sid %u: engine %p/%u is FILE ENGINE", s->id, new_engine, new_engine->id);
-                new_engine->id = DE_STATE_ID_FILE_INSPECT;
-            } else {
-                new_engine->id = ++last_id;
+        const DetectBufferType *b =
+                DetectEngineBufferTypeGetById(de_ctx, s->init_data->buffers[x].id);
+        if (b == NULL)
+            FatalError("unknown buffer");
+
+        if (b->frame) {
+            for (const DetectEngineFrameInspectionEngine *u = de_ctx->frame_inspect_engines;
+                    u != NULL; u = u->next) {
+                if (u->sm_list == s->init_data->buffers[x].id) {
+                    AppendFrameInspectEngine(de_ctx, u, s, smd, mpm_list);
+                }
+            }
+        } else if (b->packet) {
+            /* set up pkt inspect engines */
+            for (const DetectEnginePktInspectionEngine *e = de_ctx->pkt_inspect_engines; e != NULL;
+                    e = e->next) {
+                SCLogDebug("e %p sm_list %u", e, e->sm_list);
+                if (e->sm_list == s->init_data->buffers[x].id) {
+                    AppendPacketInspectEngine(de_ctx, e, s, smd, mpm_list);
+                }
+            }
+        } else {
+            SCLogDebug("app %s id %u parent %u rule %u xforms %u", b->name, b->id, b->parent_id,
+                    s->init_data->buffers[x].id, b->transforms.cnt);
+            for (const DetectEngineAppInspectionEngine *t = de_ctx->app_inspect_engines; t != NULL;
+                    t = t->next) {
+                if (t->sm_list == s->init_data->buffers[x].id) {
+                    AppendAppInspectEngine(
+                            de_ctx, t, s, smd, mpm_list, files_id, &last_id, &head_is_mpm);
+                }
             }
         }
-
-        SCLogDebug("sid %u: engine %p/%u added", s->id, new_engine, new_engine->id);
-
-        s->init_data->init_flags |= SIG_FLAG_INIT_STATE_MATCH;
-next:
-        t = t->next;
     }
 
     if ((s->init_data->init_flags & SIG_FLAG_INIT_STATE_MATCH) &&
@@ -836,64 +852,91 @@ next:
  */
 void DetectEngineAppInspectionEngineSignatureFree(DetectEngineCtx *de_ctx, Signature *s)
 {
-    int nlists = 0;
+    int engines = 0;
 
     DetectEngineAppInspectionEngine *ie = s->app_inspect;
     while (ie) {
-        nlists = MAX(ie->sm_list + 1, nlists);
         ie = ie->next;
+        engines++;
     }
     DetectEnginePktInspectionEngine *e = s->pkt_inspect;
     while (e) {
-        nlists = MAX(e->sm_list + 1, nlists);
         e = e->next;
+        engines++;
     }
     DetectEngineFrameInspectionEngine *u = s->frame_inspect;
     while (u) {
-        nlists = MAX(u->sm_list + 1, nlists);
         u = u->next;
+        engines++;
     }
-    if (nlists == 0) {
+    if (engines == 0) {
         BUG_ON(s->pkt_inspect);
         BUG_ON(s->frame_inspect);
         return;
     }
 
-    SigMatchData *ptrs[nlists];
-    memset(&ptrs, 0, (nlists * sizeof(SigMatchData *)));
+    SigMatchData *bufs[engines];
+    memset(&bufs, 0, (engines * sizeof(SigMatchData *)));
+    int arrays = 0;
 
     /* free engines and put smd in the array */
     ie = s->app_inspect;
     while (ie) {
         DetectEngineAppInspectionEngine *next = ie->next;
-        BUG_ON(ptrs[ie->sm_list] != NULL && ptrs[ie->sm_list] != ie->smd);
-        ptrs[ie->sm_list] = ie->smd;
+
+        bool skip = false;
+        for (int i = 0; i < arrays; i++) {
+            if (bufs[i] == ie->smd) {
+                skip = true;
+                break;
+            }
+        }
+        if (!skip) {
+            bufs[arrays++] = ie->smd;
+        }
         SCFree(ie);
         ie = next;
     }
     e = s->pkt_inspect;
     while (e) {
         DetectEnginePktInspectionEngine *next = e->next;
-        ptrs[e->sm_list] = e->smd;
+
+        bool skip = false;
+        for (int i = 0; i < arrays; i++) {
+            if (bufs[i] == e->smd) {
+                skip = true;
+                break;
+            }
+        }
+        if (!skip) {
+            bufs[arrays++] = e->smd;
+        }
         SCFree(e);
         e = next;
     }
     u = s->frame_inspect;
     while (u) {
         DetectEngineFrameInspectionEngine *next = u->next;
-        ptrs[u->sm_list] = u->smd;
+
+        bool skip = false;
+        for (int i = 0; i < arrays; i++) {
+            if (bufs[i] == u->smd) {
+                skip = true;
+                break;
+            }
+        }
+        if (!skip) {
+            bufs[arrays++] = u->smd;
+        }
         SCFree(u);
         u = next;
     }
 
-    /* free the smds */
-    for (int i = 0; i < nlists; i++)
-    {
-        if (ptrs[i] == NULL)
+    for (int i = 0; i < engines; i++) {
+        if (bufs[i] == NULL)
             continue;
-
-        SigMatchData *smd = ptrs[i];
-        while(1) {
+        SigMatchData *smd = bufs[i];
+        while (1) {
             if (sigmatch_table[smd->type].Free != NULL) {
                 sigmatch_table[smd->type].Free(de_ctx, smd->ctx);
             }
@@ -901,7 +944,7 @@ void DetectEngineAppInspectionEngineSignatureFree(DetectEngineCtx *de_ctx, Signa
                 break;
             smd++;
         }
-        SCFree(ptrs[i]);
+        SCFree(bufs[i]);
     }
 }
 
@@ -1036,6 +1079,16 @@ int DetectBufferTypeRegister(const char *name)
     }
 }
 
+void DetectBufferTypeSupportsMultiInstance(const char *name)
+{
+    BUG_ON(g_buffer_type_reg_closed);
+    DetectBufferTypeRegister(name);
+    DetectBufferType *exists = DetectBufferTypeLookupByName(name);
+    BUG_ON(!exists);
+    exists->multi_instance = true;
+    SCLogDebug("%p %s -- %d supports multi instance", exists, name, exists->id);
+}
+
 void DetectBufferTypeSupportsFrames(const char *name)
 {
     BUG_ON(g_buffer_type_reg_closed);
@@ -1234,6 +1287,15 @@ void DetectEngineBufferTypeSupportsTransformations(DetectEngineCtx *de_ctx, cons
     SCLogDebug("%p %s -- %d supports transformations", exists, name, exists->id);
 }
 
+bool DetectEngineBufferTypeSupportsMultiInstanceGetById(const DetectEngineCtx *de_ctx, const int id)
+{
+    const DetectBufferType *map = DetectEngineBufferTypeGetById(de_ctx, id);
+    if (map == NULL)
+        return false;
+    SCLogDebug("map %p id %d multi_instance? %s", map, id, BOOL2STR(map->multi_instance));
+    return map->multi_instance;
+}
+
 bool DetectEngineBufferTypeSupportsPacketGetById(const DetectEngineCtx *de_ctx, const int id)
 {
     const DetectBufferType *map = DetectEngineBufferTypeGetById(de_ctx, id);
@@ -1292,27 +1354,31 @@ bool DetectEngineBufferRunValidateCallback(
 
 SigMatch *DetectBufferGetFirstSigMatch(const Signature *s, const uint32_t buf_id)
 {
-    const uint32_t nlists = s->init_data->smlists_array_size;
-    if (buf_id < nlists) {
-        return s->init_data->smlists[buf_id];
+    for (uint32_t i = 0; i < s->init_data->buffer_index; i++) {
+        if (buf_id == s->init_data->buffers[i].id) {
+            return s->init_data->buffers[i].head;
+        }
     }
     return NULL;
 }
 
 SigMatch *DetectBufferGetLastSigMatch(const Signature *s, const uint32_t buf_id)
 {
-    const uint32_t nlists = s->init_data->smlists_array_size;
-    if (buf_id < nlists) {
-        return s->init_data->smlists_tail[buf_id];
+    SigMatch *last = NULL;
+    for (uint32_t i = 0; i < s->init_data->buffer_index; i++) {
+        if (buf_id == s->init_data->buffers[i].id) {
+            last = s->init_data->buffers[i].tail;
+        }
     }
-    return NULL;
+    return last;
 }
 
 bool DetectBufferIsPresent(const Signature *s, const uint32_t buf_id)
 {
-    const uint32_t nlists = s->init_data->smlists_array_size;
-    if (buf_id < nlists) {
-        return s->init_data->smlists_tail[buf_id] != NULL;
+    for (uint32_t i = 0; i < s->init_data->buffer_index; i++) {
+        if (buf_id == s->init_data->buffers[i].id) {
+            return true;
+        }
     }
     return false;
 }
@@ -1321,6 +1387,11 @@ int DetectBufferSetActiveList(DetectEngineCtx *de_ctx, Signature *s, const int l
 {
     BUG_ON(s->init_data == NULL);
 
+    if (s->init_data->list == DETECT_SM_LIST_BASE64_DATA) {
+        SCLogError("Rule buffer cannot be reset after base64_data.");
+        return -1;
+    }
+
     if (s->init_data->list && s->init_data->transforms.cnt) {
         SCLogError("no matches following transform(s)");
         return -1;
@@ -1328,6 +1399,53 @@ int DetectBufferSetActiveList(DetectEngineCtx *de_ctx, Signature *s, const int l
     s->init_data->list = list;
     s->init_data->list_set = true;
 
+    // check if last has matches -> if no, error
+    if (s->init_data->curbuf && s->init_data->curbuf->head == NULL) {
+        SCLogError("previous sticky buffer has no matches");
+        return -1;
+    }
+
+    for (uint32_t x = 0; x < s->init_data->buffers_size; x++) {
+        SignatureInitDataBuffer *b = &s->init_data->buffers[x];
+        for (SigMatch *sm = b->head; sm != NULL; sm = sm->next) {
+            SCLogDebug(
+                    "buf:%p: id:%u: '%s' pos %u", b, b->id, sigmatch_table[sm->type].name, sm->idx);
+        }
+        if ((uint32_t)list == b->id) {
+            SCLogDebug("found buffer %p for list %d", b, list);
+            if (s->init_data->buffers[x].sm_init) {
+                s->init_data->buffers[x].sm_init = false;
+                SCLogDebug("sm_init was true for %p list %d", b, list);
+                s->init_data->curbuf = b;
+                return 0;
+
+            } else if (DetectEngineBufferTypeSupportsMultiInstanceGetById(de_ctx, list)) {
+                // fall through
+            } else {
+                SCLogWarning("duplicate instance for %s in '%s'",
+                        DetectEngineBufferTypeGetNameById(de_ctx, list), s->sig_str);
+                s->init_data->curbuf = b;
+                return 0;
+            }
+        }
+    }
+
+    if (list < DETECT_SM_LIST_MAX)
+        return 0;
+
+    if (SignatureInitDataBufferCheckExpand(s) < 0) {
+        SCLogError("failed to expand rule buffer array");
+        return -1;
+    }
+
+    /* initialize new buffer */
+    s->init_data->curbuf = &s->init_data->buffers[s->init_data->buffer_index++];
+    s->init_data->curbuf->id = list;
+    s->init_data->curbuf->head = NULL;
+    s->init_data->curbuf->tail = NULL;
+    SCLogDebug("new: idx %u list %d set up curbuf %p", s->init_data->buffer_index - 1, list,
+            s->init_data->curbuf);
+
     return 0;
 }
 
@@ -1356,6 +1474,21 @@ int DetectBufferGetActiveList(DetectEngineCtx *de_ctx, Signature *s)
         s->init_data->list_set = false;
         // reset transforms now that we've set up the list
         s->init_data->transforms.cnt = 0;
+
+        if (s->init_data->curbuf && s->init_data->curbuf->head != NULL) {
+            if (SignatureInitDataBufferCheckExpand(s) < 0) {
+                SCLogError("failed to expand rule buffer array");
+                return -1;
+            }
+            s->init_data->curbuf = &s->init_data->buffers[s->init_data->buffer_index++];
+        }
+        if (s->init_data->curbuf == NULL) {
+            SCLogError("failed to setup buffer");
+            DEBUG_VALIDATE_BUG_ON(1);
+            SCReturnInt(-1);
+        }
+        s->init_data->curbuf->id = new_list;
+        SCLogDebug("new list after applying transforms: %u", new_list);
     }
 
     SCReturnInt(0);
index 848aa4abb5d8e8c34303f7aeb5458a37ccabd046..e593fb8cfd60147b28299900a7745d050900d9d2 100644 (file)
@@ -51,6 +51,7 @@ void DetectBufferTypeSupportsMpm(const char *name);
 void DetectBufferTypeSupportsPacket(const char *name);
 void DetectBufferTypeSupportsFrames(const char *name);
 void DetectBufferTypeSupportsTransformations(const char *name);
+void DetectBufferTypeSupportsMultiInstance(const char *name);
 int DetectBufferTypeMaxId(void);
 void DetectBufferTypeCloseRegistration(void);
 void DetectBufferTypeSetDescriptionByName(const char *name, const char *desc);
@@ -69,6 +70,8 @@ const char *DetectEngineBufferTypeGetNameById(const DetectEngineCtx *de_ctx, con
 const DetectBufferType *DetectEngineBufferTypeGetById(const DetectEngineCtx *de_ctx, const int id);
 bool DetectEngineBufferTypeSupportsMpmGetById(const DetectEngineCtx *de_ctx, const int id);
 bool DetectEngineBufferTypeSupportsPacketGetById(const DetectEngineCtx *de_ctx, const int id);
+bool DetectEngineBufferTypeSupportsMultiInstanceGetById(
+        const DetectEngineCtx *de_ctx, const int id);
 const char *DetectEngineBufferTypeGetDescriptionById(const DetectEngineCtx *de_ctx, const int id);
 const DetectBufferType *DetectEngineBufferTypeGetById(const DetectEngineCtx *de_ctx, const int id);
 int DetectEngineBufferTypeGetByIdTransforms(
@@ -192,7 +195,6 @@ int WARN_UNUSED DetectBufferSetActiveList(DetectEngineCtx *de_ctx, Signature *s,
 int DetectBufferGetActiveList(DetectEngineCtx *de_ctx, Signature *s);
 SigMatch *DetectBufferGetFirstSigMatch(const Signature *s, const uint32_t buf_id);
 SigMatch *DetectBufferGetLastSigMatch(const Signature *s, const uint32_t buf_id);
-bool DetectBufferIsPresent(const Signature *s, const uint32_t buf_id);
 
 DetectEngineThreadCtx *DetectEngineThreadCtxInitForReload(
         ThreadVars *tv, DetectEngineCtx *new_de_ctx, int mt);
index 4f98f507e2ea4c0aabab1107dba2327f507b4d6f..34f4cf4c69968ac224479094c2b5ed9fac6c4c6a 100644 (file)
@@ -53,7 +53,7 @@ static void DetectFastPatternRegisterTests(void);
 static SCFPSupportSMList *g_fp_support_smlist_list = NULL;
 
 /**
- * \brief Checks if a particular list(Signature->sm_lists[]) is in the list
+ * \brief Checks if a particular buffer is in the list
  *        of lists that need to be searched for a keyword that has fp support.
  *
  * \param list_id The list id.
@@ -259,8 +259,7 @@ static int DetectFastPatternSetup(DetectEngineCtx *de_ctx, Signature *s, const c
             goto error;
         }
         else { /*allow only one content to have fast_pattern modifier*/
-            uint32_t list_id = 0;
-            for (list_id = 0; list_id < s->init_data->smlists_array_size; list_id++) {
+            for (uint32_t list_id = 0; list_id < DETECT_SM_LIST_MAX; list_id++) {
                 SigMatch *sm = NULL;
                 for (sm = s->init_data->smlists[list_id]; sm != NULL; sm = sm->next) {
                     if (sm->type == DETECT_CONTENT) {
@@ -271,7 +270,7 @@ static int DetectFastPatternSetup(DetectEngineCtx *de_ctx, Signature *s, const c
                             goto error;
                         }
                     }
-                } /* for (sm = s->sm_lists[list_id]; sm != NULL; sm = sm->next) */
+                }
             }
         }
         cd->flags |= DETECT_CONTENT_FAST_PATTERN;
index e4cae2d7e9feb451d65c4ff782e9c6522abc4a5b..72eeaa4f6e71aaf4c0a7888ac84606b5cad507a7 100644 (file)
@@ -430,15 +430,9 @@ int DetectFlowbitsAnalyze(DetectEngineCtx *de_ctx)
     /* fill flowbit array, updating counters per sig */
     for (uint32_t i = 0; i < de_ctx->sig_array_len; i++) {
         const Signature *s = de_ctx->sig_array[i];
-        bool has_state = false;
 
         /* see if the signature uses stateful matching */
-        for (uint32_t x = DETECT_SM_LIST_DYNAMIC_START; x < s->init_data->smlists_array_size; x++) {
-            if (s->init_data->smlists[x] == NULL)
-                continue;
-            has_state = true;
-            break;
-        }
+        bool has_state = (s->init_data->buffer_index != 0);
 
         for (const SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_MATCH] ; sm != NULL; sm = sm->next) {
             switch (sm->type) {
index 8db78f6dc4a6fbfd2c951c91571cd71f63b490e7..6f32044a112cced6ebc699dd6e7f541da92c30b6 100644 (file)
@@ -179,31 +179,35 @@ static int DetectHttpHHSetup(DetectEngineCtx *de_ctx, Signature *s, const char *
 
 static bool DetectHttpHostValidateCallback(const Signature *s, const char **sigerror)
 {
-    const SigMatch *sm = s->init_data->smlists[g_http_host_buffer_id];
-    for ( ; sm != NULL; sm = sm->next) {
-        if (sm->type == DETECT_CONTENT) {
-            DetectContentData *cd = (DetectContentData *)sm->ctx;
-            if (cd->flags & DETECT_CONTENT_NOCASE) {
-                *sigerror = "http.host keyword "
-                            "specified along with \"nocase\". "
-                            "The hostname buffer is normalized "
-                            "to lowercase, specifying "
-                            "nocase is redundant.";
-                SCLogWarning("rule %u: %s", s->id, *sigerror);
-                return false;
-            } else {
-                uint32_t u;
-                for (u = 0; u < cd->content_len; u++) {
-                    if (isupper(cd->content[u]))
-                        break;
-                }
-                if (u != cd->content_len) {
-                    *sigerror = "A pattern with "
-                                "uppercase characters detected for http.host. "
-                                "The hostname buffer is normalized to lowercase, "
-                                "please specify a lowercase pattern.";
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        if (s->init_data->buffers[x].id != (uint32_t)g_http_host_buffer_id)
+            continue;
+        const SigMatch *sm = s->init_data->buffers[x].head;
+        for (; sm != NULL; sm = sm->next) {
+            if (sm->type == DETECT_CONTENT) {
+                DetectContentData *cd = (DetectContentData *)sm->ctx;
+                if (cd->flags & DETECT_CONTENT_NOCASE) {
+                    *sigerror = "http.host keyword "
+                                "specified along with \"nocase\". "
+                                "The hostname buffer is normalized "
+                                "to lowercase, specifying "
+                                "nocase is redundant.";
                     SCLogWarning("rule %u: %s", s->id, *sigerror);
                     return false;
+                } else {
+                    uint32_t u;
+                    for (u = 0; u < cd->content_len; u++) {
+                        if (isupper(cd->content[u]))
+                            break;
+                    }
+                    if (u != cd->content_len) {
+                        *sigerror = "A pattern with "
+                                    "uppercase characters detected for http.host. "
+                                    "The hostname buffer is normalized to lowercase, "
+                                    "please specify a lowercase pattern.";
+                        SCLogWarning("rule %u: %s", s->id, *sigerror);
+                        return false;
+                    }
                 }
             }
         }
index 7b8dbc3c26604ef24794b4c9bdd606e57e6f08a8..0ce246359ce9e19aad43a35b16c9c8bf4db814ea 100644 (file)
@@ -163,28 +163,32 @@ static int DetectHttpMethodSetupSticky(DetectEngineCtx *de_ctx, Signature *s, co
  */
 static bool DetectHttpMethodValidateCallback(const Signature *s, const char **sigerror)
 {
-    const SigMatch *sm = s->init_data->smlists[g_http_method_buffer_id];
-    for ( ; sm != NULL; sm = sm->next) {
-        if (sm->type != DETECT_CONTENT)
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        if (s->init_data->buffers[x].id != (uint32_t)g_http_method_buffer_id)
             continue;
-        const DetectContentData *cd = (const DetectContentData *)sm->ctx;
-        if (cd->content && cd->content_len) {
-            if (cd->content[cd->content_len-1] == 0x20) {
-                *sigerror = "http_method pattern with trailing space";
-                SCLogError("%s", *sigerror);
-                return false;
-            } else if (cd->content[0] == 0x20) {
-                *sigerror = "http_method pattern with leading space";
-                SCLogError("%s", *sigerror);
-                return false;
-            } else if (cd->content[cd->content_len-1] == 0x09) {
-                *sigerror = "http_method pattern with trailing tab";
-                SCLogError("%s", *sigerror);
-                return false;
-            } else if (cd->content[0] == 0x09) {
-                *sigerror = "http_method pattern with leading tab";
-                SCLogError("%s", *sigerror);
-                return false;
+        const SigMatch *sm = s->init_data->buffers[x].head;
+        for (; sm != NULL; sm = sm->next) {
+            if (sm->type != DETECT_CONTENT)
+                continue;
+            const DetectContentData *cd = (const DetectContentData *)sm->ctx;
+            if (cd->content && cd->content_len) {
+                if (cd->content[cd->content_len - 1] == 0x20) {
+                    *sigerror = "http_method pattern with trailing space";
+                    SCLogError("%s", *sigerror);
+                    return false;
+                } else if (cd->content[0] == 0x20) {
+                    *sigerror = "http_method pattern with leading space";
+                    SCLogError("%s", *sigerror);
+                    return false;
+                } else if (cd->content[cd->content_len - 1] == 0x09) {
+                    *sigerror = "http_method pattern with trailing tab";
+                    SCLogError("%s", *sigerror);
+                    return false;
+                } else if (cd->content[0] == 0x09) {
+                    *sigerror = "http_method pattern with leading tab";
+                    SCLogError("%s", *sigerror);
+                    return false;
+                }
             }
         }
     }
index 7b9395710f9e7e441b88160caa0c840f7564e219..7006145031b75258432a86fc365f08b290e6ceab 100644 (file)
@@ -922,42 +922,47 @@ static uint8_t DetectEngineInspectHttp2Header(DetectEngineCtx *de_ctx,
 
 static bool DetectHttp2HeaderValidateCallback(const Signature *s, const char **sigerror)
 {
-    const SigMatch *sm = s->init_data->smlists[g_http2_header_buffer_id];
-    for ( ; sm != NULL; sm = sm->next) {
-        if (sm->type != DETECT_CONTENT)
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        if (s->init_data->buffers[x].id != (uint32_t)g_http2_header_buffer_id)
             continue;
-        const DetectContentData *cd = (DetectContentData *)sm->ctx;
-        bool escaped = false;
-        bool namevaluesep = false;
-        for (size_t i = 0; i < cd->content_len; ++i) {
-            if (escaped) {
-                if (cd->content[i] == ' ') {
-                    if (namevaluesep) {
+        const SigMatch *sm = s->init_data->buffers[x].head;
+        for (; sm != NULL; sm = sm->next) {
+            if (sm->type != DETECT_CONTENT)
+                continue;
+            const DetectContentData *cd = (DetectContentData *)sm->ctx;
+            bool escaped = false;
+            bool namevaluesep = false;
+            for (size_t i = 0; i < cd->content_len; ++i) {
+                if (escaped) {
+                    if (cd->content[i] == ' ') {
+                        if (namevaluesep) {
+                            *sigerror = "Invalid http2.header string : "
+                                        "': ' is a special sequence for separation between name "
+                                        "and value "
+                                        " and thus can only be present once";
+                            SCLogWarning("rule %u: %s", s->id, *sigerror);
+                            return false;
+                        }
+                        namevaluesep = true;
+                    } else if (cd->content[i] != ':') {
                         *sigerror = "Invalid http2.header string : "
-                        "': ' is a special sequence for separation between name and value "
-                        " and thus can only be present once";
+                                    "':' is an escaping character for itself, "
+                                    "or space for the separation between name and value";
                         SCLogWarning("rule %u: %s", s->id, *sigerror);
                         return false;
                     }
-                    namevaluesep = true;
-                } else if (cd->content[i] != ':') {
-                    *sigerror = "Invalid http2.header string : "
-                                "':' is an escaping character for itself, "
-                                "or space for the separation between name and value";
-                    SCLogWarning("rule %u: %s", s->id, *sigerror);
-                    return false;
+                    escaped = false;
+                } else if (cd->content[i] == ':') {
+                    escaped = true;
                 }
-                escaped = false;
-            } else if(cd->content[i] == ':') {
-                escaped = true;
             }
-        }
-        if (escaped) {
-            *sigerror = "Invalid http2.header string : "
-            "':' is an escaping character for itself, "
-            "or space for the separation between name and value";
-            SCLogWarning("rule %u: %s", s->id, *sigerror);
-            return false;
+            if (escaped) {
+                *sigerror = "Invalid http2.header string : "
+                            "':' is an escaping character for itself, "
+                            "or space for the separation between name and value";
+                SCLogWarning("rule %u: %s", s->id, *sigerror);
+                return false;
+            }
         }
     }
     return true;
index 108abe581a4aff6fca5180f6d946e5f33ea9608c..2fc44d55bca6e87298e62790675e832196f34d0b 100644 (file)
@@ -71,6 +71,7 @@
 #include "app-layer-detect-proto.h"
 
 #include "action-globals.h"
+#include "util-validate.h"
 
 /* Table with all SigMatch registrations */
 SigTableElmt sigmatch_table[DETECT_TBLSIZE];
@@ -211,27 +212,52 @@ int DetectEngineContentModifierBufferSetup(DetectEngineCtx *de_ctx,
             }
         }
 
-        pm = DetectGetLastSMByListId(s, sm_list,
-            DETECT_CONTENT, DETECT_PCRE, -1);
-        if (pm != NULL) {
-            if (pm->type == DETECT_CONTENT) {
-                DetectContentData *tmp_cd = (DetectContentData *)pm->ctx;
-                tmp_cd->flags |= DETECT_CONTENT_RELATIVE_NEXT;
-            } else {
-                DetectPcreData *tmp_pd = (DetectPcreData *)pm->ctx;
-                tmp_pd->flags |= DETECT_PCRE_RELATIVE_NEXT;
+        if (s->init_data->curbuf != NULL && (int)s->init_data->curbuf->id == sm_list) {
+            pm = DetectGetLastSMByListPtr(
+                    s, s->init_data->curbuf->tail, DETECT_CONTENT, DETECT_PCRE, -1);
+            if (pm != NULL) {
+                if (pm->type == DETECT_CONTENT) {
+                    DetectContentData *tmp_cd = (DetectContentData *)pm->ctx;
+                    tmp_cd->flags |= DETECT_CONTENT_RELATIVE_NEXT;
+                } else {
+                    DetectPcreData *tmp_pd = (DetectPcreData *)pm->ctx;
+                    tmp_pd->flags |= DETECT_PCRE_RELATIVE_NEXT;
+                }
             }
         }
     }
     s->alproto = alproto;
     s->flags |= SIG_FLAG_APPLAYER;
 
+    if (s->init_data->curbuf == NULL || (int)s->init_data->curbuf->id != sm_list) {
+        if (s->init_data->curbuf != NULL && s->init_data->curbuf->head == NULL) {
+            SCLogError("no matches for previous buffer");
+            return -1;
+        }
+        if (SignatureInitDataBufferCheckExpand(s) < 0) {
+            SCLogError("failed to expand rule buffer array");
+            return -1;
+        }
+
+        /* initialize a new buffer */
+        s->init_data->curbuf = &s->init_data->buffers[s->init_data->buffer_index++];
+        s->init_data->curbuf->id = sm_list;
+        s->init_data->curbuf->head = NULL;
+        s->init_data->curbuf->tail = NULL;
+        SCLogDebug("idx %u list %d set up curbuf %p s->init_data->buffer_index %u",
+                s->init_data->buffer_index - 1, sm_list, s->init_data->curbuf,
+                s->init_data->buffer_index);
+    }
+
     /* transfer the sm from the pmatch list to sm_list */
-    SigMatchTransferSigMatchAcrossLists(sm,
-                                        &s->init_data->smlists[DETECT_SM_LIST_PMATCH],
-                                        &s->init_data->smlists_tail[DETECT_SM_LIST_PMATCH],
-                                        &s->init_data->smlists[sm_list],
-                                        &s->init_data->smlists_tail[sm_list]);
+    SigMatchTransferSigMatchAcrossLists(sm, &s->init_data->smlists[DETECT_SM_LIST_PMATCH],
+            &s->init_data->smlists_tail[DETECT_SM_LIST_PMATCH], &s->init_data->curbuf->head,
+            &s->init_data->curbuf->tail);
+
+    if (sm->type == DETECT_CONTENT) {
+        s->init_data->max_content_list_id =
+                MAX(s->init_data->max_content_list_id, (uint32_t)sm_list);
+    }
 
     ret = 0;
  end:
@@ -353,42 +379,74 @@ void SigTableApplyStrictCommandlineOption(const char *str)
  * \param new  The sig match to append.
  * \param list The list to append to.
  */
-void SigMatchAppendSMToList(Signature *s, SigMatch *new, int list)
+void SigMatchAppendSMToList(Signature *s, SigMatch *new, const int list)
 {
-    if (list > 0 && (uint32_t)list >= s->init_data->smlists_array_size)
-    {
-        uint32_t old_size = s->init_data->smlists_array_size;
-        uint32_t new_size = (uint32_t)list + 1;
-        void *ptr = SCRealloc(s->init_data->smlists, (new_size * sizeof(SigMatch *)));
-        if (ptr == NULL)
-            abort();
-        s->init_data->smlists = ptr;
-        ptr = SCRealloc(s->init_data->smlists_tail, (new_size * sizeof(SigMatch *)));
-        if (ptr == NULL)
-            abort();
-        s->init_data->smlists_tail = ptr;
-        for (uint32_t i = old_size; i < new_size; i++) {
-            s->init_data->smlists[i] = NULL;
-            s->init_data->smlists_tail[i] = NULL;
-        }
-        s->init_data->smlists_array_size = new_size;
+    if (new->type == DETECT_CONTENT) {
+        s->init_data->max_content_list_id = MAX(s->init_data->max_content_list_id, (uint32_t)list);
     }
 
-    if (s->init_data->smlists[list] == NULL) {
-        s->init_data->smlists[list] = new;
-        s->init_data->smlists_tail[list] = new;
-        new->next = NULL;
-        new->prev = NULL;
+    SCLogDebug("s:%p new:%p list:%d: %s, s->init_data->list_set %s s->init_data->list %d", s, new,
+            list, sigmatch_table[new->type].name, BOOL2STR(s->init_data->list_set),
+            s->init_data->list);
+
+    if (list < DETECT_SM_LIST_MAX) {
+        if (s->init_data->smlists[list] == NULL) {
+            s->init_data->smlists[list] = new;
+            s->init_data->smlists_tail[list] = new;
+            new->next = NULL;
+            new->prev = NULL;
+        } else {
+            SigMatch *cur = s->init_data->smlists_tail[list];
+            cur->next = new;
+            new->prev = cur;
+            new->next = NULL;
+            s->init_data->smlists_tail[list] = new;
+        }
+        new->idx = s->init_data->sm_cnt;
+        s->init_data->sm_cnt++;
+
     } else {
-        SigMatch *cur = s->init_data->smlists_tail[list];
-        cur->next = new;
-        new->prev = cur;
-        new->next = NULL;
-        s->init_data->smlists_tail[list] = new;
-    }
+        /* app-layer-events (and possibly others?) can get here w/o a "list"
+         * already set up. */
+
+        /* unset any existing list if it isn't the same as the new */
+        if (s->init_data->list != DETECT_SM_LIST_NOTSET && list != s->init_data->list) {
+            SCLogDebug("reset: list %d != s->init_data->list %d", list, s->init_data->list);
+            s->init_data->list = DETECT_SM_LIST_NOTSET;
+        }
+        if ((s->init_data->curbuf != NULL && (int)s->init_data->curbuf->id != list) ||
+                s->init_data->curbuf == NULL) {
+            if (SignatureInitDataBufferCheckExpand(s) < 0) {
+                SCLogError("failed to expand rule buffer array");
+                // return -1; TODO error handle
+            }
 
-    new->idx = s->init_data->sm_cnt;
-    s->init_data->sm_cnt++;
+            /* initialize new buffer */
+            s->init_data->curbuf = &s->init_data->buffers[s->init_data->buffer_index++];
+            s->init_data->curbuf->id = list;
+            /* buffer set up by sigmatch is tracked in case we add a stickybuffer for the
+             * same list. */
+            s->init_data->curbuf->sm_init = true;
+            SCLogDebug("s->init_data->buffer_index %u", s->init_data->buffer_index);
+        }
+        BUG_ON(s->init_data->curbuf == NULL);
+
+        new->prev = s->init_data->curbuf->tail;
+        if (s->init_data->curbuf->tail)
+            s->init_data->curbuf->tail->next = new;
+        if (s->init_data->curbuf->head == NULL)
+            s->init_data->curbuf->head = new;
+        s->init_data->curbuf->tail = new;
+        new->idx = s->init_data->sm_cnt;
+        s->init_data->sm_cnt++;
+        SCLogDebug("appended %s to list %d, rule pos %u (s->init_data->list %d)",
+                sigmatch_table[new->type].name, list, new->idx, s->init_data->list);
+
+        for (SigMatch *sm = s->init_data->curbuf->head; sm != NULL; sm = sm->next) {
+            SCLogDebug("buf:%p: id:%u: '%s' pos %u", s->init_data->curbuf, s->init_data->curbuf->id,
+                    sigmatch_table[sm->type].name, sm->idx);
+        }
+    }
 }
 
 void SigMatchRemoveSMFromList(Signature *s, SigMatch *sm, int sm_list)
@@ -439,21 +497,18 @@ SigMatch *DetectGetLastSMFromMpmLists(const DetectEngineCtx *de_ctx, const Signa
     SigMatch *sm_new;
     uint32_t sm_type;
 
-    /* if we have a sticky buffer, use that */
-    if (s->init_data->list != DETECT_SM_LIST_NOTSET &&
-            s->init_data->list < (int)s->init_data->smlists_array_size) {
-        if (!(DetectEngineBufferTypeSupportsMpmGetById(de_ctx, s->init_data->list))) {
-            return NULL;
+    for (uint32_t i = 0; i < s->init_data->buffer_index; i++) {
+        const int id = s->init_data->buffers[i].id;
+        if (DetectEngineBufferTypeSupportsMpmGetById(de_ctx, id)) {
+            sm_new = DetectGetLastSMByListPtr(s, s->init_data->buffers[i].tail, DETECT_CONTENT, -1);
+            if (sm_new == NULL)
+                continue;
+            if (sm_last == NULL || sm_new->idx > sm_last->idx)
+                sm_last = sm_new;
         }
-
-        sm_last = DetectGetLastSMByListPtr(s,
-                s->init_data->smlists_tail[s->init_data->list],
-                DETECT_CONTENT, -1);
-        return sm_last;
     }
-
     /* otherwise brute force it */
-    for (sm_type = 0; sm_type < s->init_data->smlists_array_size; sm_type++) {
+    for (sm_type = 0; sm_type < DETECT_SM_LIST_MAX; sm_type++) {
         if (!DetectEngineBufferTypeSupportsMpmGetById(de_ctx, sm_type))
             continue;
         SigMatch *sm_list = s->init_data->smlists_tail[sm_type];
@@ -478,8 +533,30 @@ SigMatch *DetectGetLastSMFromLists(const Signature *s, ...)
     SigMatch *sm_last = NULL;
     SigMatch *sm_new;
 
-    /* otherwise brute force it */
-    for (int buf_type = 0; buf_type < (int)s->init_data->smlists_array_size; buf_type++) {
+    SCLogDebug("s->init_data->buffer_index %u", s->init_data->buffer_index);
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        if (s->init_data->list != DETECT_SM_LIST_NOTSET &&
+                s->init_data->list != (int)s->init_data->buffers[x].id) {
+            SCLogDebug("skip x %u s->init_data->list %d (int)s->init_data->buffers[x].id %d", x,
+                    s->init_data->list, (int)s->init_data->buffers[x].id);
+
+            continue;
+        }
+        int sm_type;
+        va_list ap;
+        va_start(ap, s);
+
+        for (sm_type = va_arg(ap, int); sm_type != -1; sm_type = va_arg(ap, int)) {
+            sm_new = SigMatchGetLastSMByType(s->init_data->buffers[x].tail, sm_type);
+            if (sm_new == NULL)
+                continue;
+            if (sm_last == NULL || sm_new->idx > sm_last->idx)
+                sm_last = sm_new;
+        }
+        va_end(ap);
+    }
+
+    for (int buf_type = 0; buf_type < DETECT_SM_LIST_MAX; buf_type++) {
         if (s->init_data->smlists[buf_type] == NULL)
             continue;
         if (s->init_data->list != DETECT_SM_LIST_NOTSET &&
@@ -551,27 +628,43 @@ SigMatch *DetectGetLastSMByListId(const Signature *s, int list_id, ...)
     SigMatch *sm_new;
     int sm_type;
 
-    if ((uint32_t)list_id >= s->init_data->smlists_array_size) {
-        return NULL;
-    }
-    SigMatch *sm_list = s->init_data->smlists_tail[list_id];
-    if (sm_list == NULL)
-        return NULL;
+    if ((uint32_t)list_id >= DETECT_SM_LIST_MAX) {
+        for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+            sm_new = s->init_data->buffers[x].tail;
+            if (sm_new == NULL)
+                continue;
 
-    va_list ap;
-    va_start(ap, list_id);
+            va_list ap;
+            va_start(ap, list_id);
 
-    for (sm_type = va_arg(ap, int); sm_type != -1; sm_type = va_arg(ap, int))
-    {
-        sm_new = SigMatchGetLastSMByType(sm_list, sm_type);
-        if (sm_new == NULL)
-            continue;
-        if (sm_last == NULL || sm_new->idx > sm_last->idx)
-            sm_last = sm_new;
-    }
+            for (sm_type = va_arg(ap, int); sm_type != -1; sm_type = va_arg(ap, int)) {
+                sm_new = SigMatchGetLastSMByType(s->init_data->buffers[x].tail, sm_type);
+                if (sm_new == NULL)
+                    continue;
+                if (sm_last == NULL || sm_new->idx > sm_last->idx)
+                    sm_last = sm_new;
+            }
 
-    va_end(ap);
+            va_end(ap);
+        }
+    } else {
+        SigMatch *sm_list = s->init_data->smlists_tail[list_id];
+        if (sm_list == NULL)
+            return NULL;
+
+        va_list ap;
+        va_start(ap, list_id);
 
+        for (sm_type = va_arg(ap, int); sm_type != -1; sm_type = va_arg(ap, int)) {
+            sm_new = SigMatchGetLastSMByType(sm_list, sm_type);
+            if (sm_new == NULL)
+                continue;
+            if (sm_last == NULL || sm_new->idx > sm_last->idx)
+                sm_last = sm_new;
+        }
+
+        va_end(ap);
+    }
     return sm_last;
 }
 
@@ -582,12 +675,18 @@ SigMatch *DetectGetLastSMByListId(const Signature *s, int list_id, ...)
  */
 SigMatch *DetectGetLastSM(const Signature *s)
 {
-    const int nlists = s->init_data->smlists_array_size;
     SigMatch *sm_last = NULL;
     SigMatch *sm_new;
-    int i;
 
-    for (i = 0; i < nlists; i ++) {
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        sm_new = s->init_data->buffers[x].tail;
+        if (sm_new == NULL)
+            continue;
+        if (sm_last == NULL || sm_new->idx > sm_last->idx)
+            sm_last = sm_new;
+    }
+
+    for (int i = 0; i < DETECT_SM_LIST_MAX; i++) {
         sm_new = s->init_data->smlists_tail[i];
         if (sm_new == NULL)
             continue;
@@ -635,8 +734,16 @@ int SigMatchListSMBelongsTo(const Signature *s, const SigMatch *key_sm)
     if (key_sm == NULL)
         return -1;
 
-    const int nlists = s->init_data->smlists_array_size;
-    for (int list = 0; list < nlists; list++) {
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        const SigMatch *sm = s->init_data->buffers[x].head;
+        while (sm != NULL) {
+            if (sm == key_sm)
+                return s->init_data->buffers[x].id;
+            sm = sm->next;
+        }
+    }
+
+    for (int list = 0; list < DETECT_SM_LIST_MAX; list++) {
         const SigMatch *sm = s->init_data->smlists[list];
         while (sm != NULL) {
             if (sm == key_sm)
@@ -1271,6 +1378,28 @@ static int SigParse(DetectEngineCtx *de_ctx, Signature *s,
     SCReturnInt(ret);
 }
 
+/** \brief check if buffers array still has space left, expand if not
+ */
+int SignatureInitDataBufferCheckExpand(Signature *s)
+{
+    if (s->init_data->buffers_size >= 64)
+        return -1;
+
+    if (s->init_data->buffer_index + 1 == s->init_data->buffers_size) {
+        void *ptr = SCRealloc(s->init_data->buffers,
+                (s->init_data->buffers_size + 8) * sizeof(SignatureInitDataBuffer));
+        if (ptr == NULL)
+            return -1;
+        s->init_data->buffers = ptr;
+        for (uint32_t x = s->init_data->buffers_size; x < s->init_data->buffers_size + 8; x++) {
+            SignatureInitDataBuffer *b = &s->init_data->buffers[x];
+            memset(b, 0, sizeof(*b));
+        }
+        s->init_data->buffers_size += 8;
+    }
+    return 0;
+}
+
 Signature *SigAlloc (void)
 {
     Signature *sig = SCMalloc(sizeof(Signature));
@@ -1285,22 +1414,12 @@ Signature *SigAlloc (void)
     }
     sig->init_data->mpm_sm_list = -1;
 
-    sig->init_data->smlists_array_size = DetectBufferTypeMaxId();
-    SCLogDebug("smlists size %u", sig->init_data->smlists_array_size);
-    sig->init_data->smlists = SCCalloc(sig->init_data->smlists_array_size, sizeof(SigMatch *));
-    if (sig->init_data->smlists == NULL) {
-        SCFree(sig->init_data);
-        SCFree(sig);
-        return NULL;
-    }
-
-    sig->init_data->smlists_tail = SCCalloc(sig->init_data->smlists_array_size, sizeof(SigMatch *));
-    if (sig->init_data->smlists_tail == NULL) {
-        SCFree(sig->init_data->smlists);
-        SCFree(sig->init_data);
+    sig->init_data->buffers = SCCalloc(8, sizeof(SignatureInitDataBuffer));
+    if (sig->init_data->buffers == NULL) {
         SCFree(sig);
         return NULL;
     }
+    sig->init_data->buffers_size = 8;
 
     /* assign it to -1, so that we can later check if the value has been
      * overwritten after the Signature has been parsed, and if it hasn't been
@@ -1420,8 +1539,7 @@ void SigFree(DetectEngineCtx *de_ctx, Signature *s)
         }
     }
     if (s->init_data) {
-        const int nlists = s->init_data->smlists_array_size;
-        for (i = 0; i < nlists; i++) {
+        for (i = 0; i < DETECT_SM_LIST_MAX; i++) {
             SigMatch *sm = s->init_data->smlists[i];
             while (sm != NULL) {
                 SigMatch *nsm = sm->next;
@@ -1429,11 +1547,20 @@ void SigFree(DetectEngineCtx *de_ctx, Signature *s)
                 sm = nsm;
             }
         }
+
+        for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+            SigMatch *sm = s->init_data->buffers[x].head;
+            while (sm != NULL) {
+                SigMatch *nsm = sm->next;
+                SigMatchFree(de_ctx, sm);
+                sm = nsm;
+            }
+        }
+        SCFree(s->init_data->buffers);
+        s->init_data->buffers = NULL;
     }
     SigMatchFreeArrays(de_ctx, s, (s->init_data == NULL));
     if (s->init_data) {
-        SCFree(s->init_data->smlists);
-        SCFree(s->init_data->smlists_tail);
         SCFree(s->init_data);
         s->init_data = NULL;
     }
@@ -1685,64 +1812,96 @@ SigMatchData* SigMatchList2DataArray(SigMatch *head)
  */
 static int SigValidate(DetectEngineCtx *de_ctx, Signature *s)
 {
-    uint32_t sig_flags = 0;
-    const int nlists = s->init_data->smlists_array_size;
-
     SCEnter();
 
-    /* check for sticky buffers that were set w/o matches
-     * e.g. alert ... (file_data; sid:1;) */
-    if (s->init_data->list != DETECT_SM_LIST_NOTSET) {
-        if (s->init_data->list >= (int)s->init_data->smlists_array_size ||
-                s->init_data->smlists[s->init_data->list] == NULL) {
-            SCLogError("rule %u setup buffer %s but didn't add matches to it", s->id,
-                    DetectEngineBufferTypeGetNameById(de_ctx, s->init_data->list));
-            SCReturnInt(0);
-        }
+    uint32_t sig_flags = 0;
+    int nlists = 0;
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        nlists = MAX(nlists, (int)s->init_data->buffers[x].id);
+    }
+    nlists += (nlists > 0);
+    SCLogDebug("nlists %d", nlists);
+
+    if (s->init_data->curbuf && s->init_data->curbuf->head == NULL) {
+        SCLogError("rule %u setup buffer %s but didn't add matches to it", s->id,
+                DetectEngineBufferTypeGetNameById(de_ctx, s->init_data->curbuf->id));
+        SCReturnInt(0);
     }
 
+    bool has_frame = false;
+    bool has_app = false;
+    bool has_pkt = false;
+    bool has_pmatch = false;
+
     /* run buffer type validation callbacks if any */
     if (s->init_data->smlists[DETECT_SM_LIST_PMATCH]) {
         if (!DetectContentPMATCHValidateCallback(s))
             SCReturnInt(0);
+
+        has_pmatch = true;
     }
 
     struct BufferVsDir {
         int ts;
         int tc;
-    } bufdir[nlists];
-    memset(&bufdir, 0, nlists * sizeof(struct BufferVsDir));
-
-    int x;
-    for (x = 0; x < nlists; x++) {
-        if (s->init_data->smlists[x]) {
-            const DetectEngineAppInspectionEngine *app = de_ctx->app_inspect_engines;
-            for ( ; app != NULL; app = app->next) {
-                if (app->sm_list == x &&
-                        (AppProtoEquals(s->alproto, app->alproto) || s->alproto == 0)) {
-                    SCLogDebug("engine %s dir %d alproto %d",
-                            DetectEngineBufferTypeGetNameById(de_ctx, app->sm_list), app->dir,
-                            app->alproto);
-
-                    bufdir[x].ts += (app->dir == 0);
-                    bufdir[x].tc += (app->dir == 1);
-                }
-            }
+    } bufdir[nlists + 1];
+    memset(&bufdir, 0, (nlists + 1) * sizeof(struct BufferVsDir));
+
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        SignatureInitDataBuffer *b = &s->init_data->buffers[x];
+        const DetectBufferType *bt = DetectEngineBufferTypeGetById(de_ctx, b->id);
+        if (bt == NULL) {
+            DEBUG_VALIDATE_BUG_ON(1); // should be impossible
+            continue;
+        }
+        SCLogDebug("x %u b->id %u name %s", x, b->id, bt->name);
+        for (SigMatch *sm = b->head; sm != NULL; sm = sm->next) {
+            SCLogDebug("sm %u %s", sm->type, sigmatch_table[sm->type].name);
+        }
 
-            if (!DetectEngineBufferRunValidateCallback(de_ctx, x, s, &de_ctx->sigerror)) {
-                SCReturnInt(0);
-            }
+        if (b->head == NULL) {
+            SCLogError("no matches in sticky buffer %s", bt->name);
+            SCReturnInt(0);
+        }
+
+        has_frame |= bt->frame;
+        has_app |= (bt->frame == false && bt->packet == false);
+        has_pkt |= bt->packet;
+
+        if ((s->flags & SIG_FLAG_REQUIRE_PACKET) && bt->packet == false) {
+            SCLogError("Signature combines packet "
+                       "specific matches (like dsize, flags, ttl) with stream / "
+                       "state matching by matching on app layer proto (like using "
+                       "http_* keywords).");
+            SCReturnInt(0);
+        }
 
-            if (!DetectBsizeValidateContentCallback(s, x)) {
-                SCReturnInt(0);
+        const DetectEngineAppInspectionEngine *app = de_ctx->app_inspect_engines;
+        for (; app != NULL; app = app->next) {
+            if (app->sm_list == b->id &&
+                    (AppProtoEquals(s->alproto, app->alproto) || s->alproto == 0)) {
+                SCLogDebug("engine %s dir %d alproto %d",
+                        DetectEngineBufferTypeGetNameById(de_ctx, app->sm_list), app->dir,
+                        app->alproto);
+                SCLogDebug("b->id %d nlists %d", b->id, nlists);
+                bufdir[b->id].ts += (app->dir == 0);
+                bufdir[b->id].tc += (app->dir == 1);
             }
         }
+
+        if (!DetectEngineBufferRunValidateCallback(de_ctx, b->id, s, &de_ctx->sigerror)) {
+            SCReturnInt(0);
+        }
+
+        if (!DetectBsizeValidateContentCallback(s, b)) {
+            SCReturnInt(0);
+        }
     }
 
     int ts_excl = 0;
     int tc_excl = 0;
     int dir_amb = 0;
-    for (x = 0; x < nlists; x++) {
+    for (int x = 0; x < nlists; x++) {
         if (bufdir[x].ts == 0 && bufdir[x].tc == 0)
             continue;
         ts_excl += (bufdir[x].ts > 0 && bufdir[x].tc == 0);
@@ -1785,24 +1944,6 @@ static int SigValidate(DetectEngineCtx *de_ctx, Signature *s)
         SCReturnInt(0);
     }
 
-    bool has_pmatch = false;
-    bool has_frame = false;
-    bool has_app = false;
-    bool has_pkt = false;
-
-    for (int i = 0; i < nlists; i++) {
-        if (s->init_data->smlists[i] == NULL)
-            continue;
-        has_pmatch |= (i == DETECT_SM_LIST_PMATCH);
-
-        const DetectBufferType *b = DetectEngineBufferTypeGetById(de_ctx, i);
-        if (b == NULL)
-            continue;
-
-        has_frame |= b->frame;
-        has_app |= (b->frame == false && b->packet == false);
-        has_pkt |= b->packet;
-    }
     if (has_pmatch && has_frame) {
         SCLogError("can't mix pure content and frame inspection");
         SCReturnInt(0);
@@ -1816,23 +1957,6 @@ static int SigValidate(DetectEngineCtx *de_ctx, Signature *s)
         SCReturnInt(0);
     }
 
-    if (s->flags & SIG_FLAG_REQUIRE_PACKET) {
-        for (int i = 0; i < nlists; i++) {
-            if (s->init_data->smlists[i] == NULL)
-                continue;
-            if (!(DetectEngineBufferTypeGetNameById(de_ctx, i)))
-                continue;
-
-            if (!(DetectEngineBufferTypeSupportsPacketGetById(de_ctx, i))) {
-                SCLogError("Signature combines packet "
-                           "specific matches (like dsize, flags, ttl) with stream / "
-                           "state matching by matching on app layer proto (like using "
-                           "http_* keywords).");
-                SCReturnInt(0);
-            }
-        }
-    }
-
     /* TCP: corner cases:
      * - pkt vs stream vs depth/offset
      * - pkt vs stream vs stream_size
@@ -1861,45 +1985,10 @@ static int SigValidate(DetectEngineCtx *de_ctx, Signature *s)
             }
         }
     }
-
-    if (s->init_data->smlists[DETECT_SM_LIST_BASE64_DATA] != NULL) {
-        int list;
-        uint16_t idx = s->init_data->smlists[DETECT_SM_LIST_BASE64_DATA]->idx;
-        for (list = 0; list < nlists; list++) {
-            if (list == DETECT_SM_LIST_POSTMATCH ||
-                list == DETECT_SM_LIST_TMATCH ||
-                list == DETECT_SM_LIST_SUPPRESS ||
-                list == DETECT_SM_LIST_THRESHOLD)
-            {
-                continue;
-            }
-
-            if (list != DETECT_SM_LIST_BASE64_DATA &&
-                s->init_data->smlists[list] != NULL) {
-                if (s->init_data->smlists[list]->idx > idx) {
-                    SCLogError("Rule buffer "
-                               "cannot be reset after base64_data.");
-                    SCReturnInt(0);
-                }
-            }
-        }
-    }
-
 #ifdef HAVE_LUA
     DetectLuaPostSetup(s);
 #endif
 
-#ifdef DEBUG
-    for (int i = 0; i < nlists; i++) {
-        if (s->init_data->smlists[i] != NULL) {
-            for (SigMatch *sm = s->init_data->smlists[i]; sm != NULL; sm = sm->next) {
-                BUG_ON(sm == sm->prev);
-                BUG_ON(sm == sm->next);
-            }
-        }
-    }
-#endif
-
     if (s->init_data->init_flags & SIG_FLAG_INIT_JA3 && s->alproto != ALPROTO_UNKNOWN &&
             s->alproto != ALPROTO_TLS && s->alproto != ALPROTO_QUIC) {
         SCLogError("Cannot have ja3 with protocol %s.", AppProtoToString(s->alproto));
@@ -2021,10 +2110,13 @@ static Signature *SigInitHelper(DetectEngineCtx *de_ctx, const char *sigstr,
     SigBuildAddressMatchArray(sig);
 
     /* run buffer type callbacks if any */
-    for (uint32_t x = 0; x < sig->init_data->smlists_array_size; x++) {
+    for (uint32_t x = 0; x < DETECT_SM_LIST_MAX; x++) {
         if (sig->init_data->smlists[x])
             DetectEngineBufferRunSetupCallback(de_ctx, x, sig);
     }
+    for (uint32_t x = 0; x < sig->init_data->buffer_index; x++) {
+        DetectEngineBufferRunSetupCallback(de_ctx, sig->init_data->buffers[x].id, sig);
+    }
 
     /* validate signature, SigValidate will report the error reason */
     if (SigValidate(de_ctx, sig) == 0) {
index 95b4514bbc75c6162ae2d4748e4bcb53b0c05b02..697cd9754e2c232004e4126b394cf2e965ba83c5 100644 (file)
@@ -49,6 +49,7 @@ typedef struct DetectParseRegex {
 } DetectParseRegex;
 
 /* prototypes */
+int SignatureInitDataBufferCheckExpand(Signature *s);
 Signature *SigAlloc(void);
 void SigFree(DetectEngineCtx *de_ctx, Signature *s);
 Signature *SigInit(DetectEngineCtx *, const char *sigstr);
index c79f3ab2cb632ad643c93f91a615b29e611a9fd5..91e58eb543c1f0415d0dacf01a3d4a8cb9846e45 100644 (file)
@@ -184,39 +184,42 @@ static int PrefilterMpmQuicHashRegister(DetectEngineCtx *de_ctx, SigGroupHead *s
 
 static bool DetectQuicHashValidateCallback(const Signature *s, const char **sigerror)
 {
-    const SigMatch *sm = s->init_data->smlists[g_buffer_id];
-    for (; sm != NULL; sm = sm->next) {
-        if (sm->type != DETECT_CONTENT)
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        if (s->init_data->buffers[x].id != (uint32_t)g_buffer_id)
             continue;
+        const SigMatch *sm = s->init_data->buffers[x].head;
+        for (; sm != NULL; sm = sm->next) {
+            if (sm->type != DETECT_CONTENT)
+                continue;
 
-        const DetectContentData *cd = (DetectContentData *)sm->ctx;
+            const DetectContentData *cd = (DetectContentData *)sm->ctx;
 
-        if (cd->flags & DETECT_CONTENT_NOCASE) {
-            *sigerror = BUFFER_NAME " should not be used together with "
-                                    "nocase, since the rule is automatically "
-                                    "lowercased anyway which makes nocase redundant.";
-            SCLogWarning("rule %u: %s", s->id, *sigerror);
-        }
+            if (cd->flags & DETECT_CONTENT_NOCASE) {
+                *sigerror = BUFFER_NAME " should not be used together with "
+                                        "nocase, since the rule is automatically "
+                                        "lowercased anyway which makes nocase redundant.";
+                SCLogWarning("rule %u: %s", s->id, *sigerror);
+            }
 
-        if (cd->content_len != 32) {
-            *sigerror = "Invalid length of the specified" BUFFER_NAME " (should "
-                        "be 32 characters long). This rule will therefore "
-                        "never match.";
-            SCLogWarning("rule %u: %s", s->id, *sigerror);
-            return FALSE;
-        }
-        for (size_t i = 0; i < cd->content_len; ++i) {
-            if (!isxdigit(cd->content[i])) {
-                *sigerror = "Invalid " BUFFER_NAME
-                            " string (should be string of hexadecimal characters)."
-                            "This rule will therefore never match.";
+            if (cd->content_len != 32) {
+                *sigerror = "Invalid length of the specified" BUFFER_NAME " (should "
+                            "be 32 characters long). This rule will therefore "
+                            "never match.";
                 SCLogWarning("rule %u: %s", s->id, *sigerror);
-                return FALSE;
+                return false;
+            }
+            for (size_t i = 0; i < cd->content_len; ++i) {
+                if (!isxdigit(cd->content[i])) {
+                    *sigerror = "Invalid " BUFFER_NAME
+                                " string (should be string of hexadecimal characters)."
+                                "This rule will therefore never match.";
+                    SCLogWarning("rule %u: %s", s->id, *sigerror);
+                    return false;
+                }
             }
         }
     }
-
-    return TRUE;
+    return true;
 }
 
 void DetectQuicCyuHashRegister(void)
index bb2933e47956851614e96053b07ad69fafb0246d..fccc8a73f9fc616ce81ebc1b1e4314c3b4e32641 100644 (file)
@@ -72,28 +72,32 @@ static int DetectSipMethodSetup(DetectEngineCtx *de_ctx, Signature *s, const cha
 
 static bool DetectSipMethodValidateCallback(const Signature *s, const char **sigerror)
 {
-    const SigMatch *sm = s->init_data->smlists[g_buffer_id];
-    for ( ; sm != NULL; sm = sm->next) {
-        if (sm->type != DETECT_CONTENT)
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        if (s->init_data->buffers[x].id != (uint32_t)g_buffer_id)
             continue;
-        const DetectContentData *cd = (const DetectContentData *)sm->ctx;
-        if (cd->content && cd->content_len) {
-            if (cd->content[cd->content_len-1] == 0x20) {
-                *sigerror = "sip.method pattern with trailing space";
-                SCLogError("%s", *sigerror);
-                return true;
-            } else if (cd->content[0] == 0x20) {
-                *sigerror = "sip.method pattern with leading space";
-                SCLogError("%s", *sigerror);
-                return true;
-            } else if (cd->content[cd->content_len-1] == 0x09) {
-                *sigerror = "sip.method pattern with trailing tab";
-                SCLogError("%s", *sigerror);
-                return true;
-            } else if (cd->content[0] == 0x09) {
-                *sigerror = "sip.method pattern with leading tab";
-                SCLogError("%s", *sigerror);
-                return true;
+        const SigMatch *sm = s->init_data->buffers[x].head;
+        for (; sm != NULL; sm = sm->next) {
+            if (sm->type != DETECT_CONTENT)
+                continue;
+            const DetectContentData *cd = (const DetectContentData *)sm->ctx;
+            if (cd->content && cd->content_len) {
+                if (cd->content[cd->content_len - 1] == 0x20) {
+                    *sigerror = "sip.method pattern with trailing space";
+                    SCLogError("%s", *sigerror);
+                    return true;
+                } else if (cd->content[0] == 0x20) {
+                    *sigerror = "sip.method pattern with leading space";
+                    SCLogError("%s", *sigerror);
+                    return true;
+                } else if (cd->content[cd->content_len - 1] == 0x09) {
+                    *sigerror = "sip.method pattern with trailing tab";
+                    SCLogError("%s", *sigerror);
+                    return true;
+                } else if (cd->content[0] == 0x09) {
+                    *sigerror = "sip.method pattern with leading tab";
+                    SCLogError("%s", *sigerror);
+                    return true;
+                }
             }
         }
     }
index 4f04287428336147a0c33254dabac41e3fed5288..29dfedb58d557575818558fc8a516f44cd0cbc7d 100644 (file)
@@ -120,66 +120,68 @@ static int DetectSshHasshServerSetup(DetectEngineCtx *de_ctx, Signature *s, cons
 
 static bool DetectSshHasshServerHashValidateCallback(const Signature *s, const char **sigerror)
 {
-    const SigMatch *sm = s->init_data->smlists[g_ssh_hassh_buffer_id];
-    for ( ; sm != NULL; sm = sm->next)
-    {
-        if (sm->type != DETECT_CONTENT)
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        if (s->init_data->buffers[x].id != (uint32_t)g_ssh_hassh_buffer_id)
             continue;
+        const SigMatch *sm = s->init_data->buffers[x].head;
+        for (; sm != NULL; sm = sm->next) {
+            if (sm->type != DETECT_CONTENT)
+                continue;
 
-        const DetectContentData *cd = (DetectContentData *)sm->ctx;
+            const DetectContentData *cd = (DetectContentData *)sm->ctx;
 
-        if (cd->flags & DETECT_CONTENT_NOCASE) {
-            *sigerror = "ssh.hassh.server should not be used together with "
-                        "nocase, since the rule is automatically "
-                        "lowercased anyway which makes nocase redundant.";
-            SCLogWarning("rule %u: %s", s->id, *sigerror);
-        }
+            if (cd->flags & DETECT_CONTENT_NOCASE) {
+                *sigerror = "ssh.hassh.server should not be used together with "
+                            "nocase, since the rule is automatically "
+                            "lowercased anyway which makes nocase redundant.";
+                SCLogWarning("rule %u: %s", s->id, *sigerror);
+            }
 
-        if (cd->content_len != 32)
-        {
-            *sigerror = "Invalid length of the specified ssh.hassh.server (should "
-                        "be 32 characters long). This rule will therefore "
-                        "never match.";
-            SCLogWarning("rule %u: %s", s->id, *sigerror);
-            return false;
-        }
-        for (size_t i = 0; i < cd->content_len; ++i)
-        {
-            if(!isxdigit(cd->content[i])) 
-            {
-                *sigerror = "Invalid ssh.hassh.server string (should be string of hexademical characters)."
-                            "This rule will therefore never match.";
+            if (cd->content_len != 32) {
+                *sigerror = "Invalid length of the specified ssh.hassh.server (should "
+                            "be 32 characters long). This rule will therefore "
+                            "never match.";
                 SCLogWarning("rule %u: %s", s->id, *sigerror);
                 return false;
             }
+            for (size_t i = 0; i < cd->content_len; ++i) {
+                if (!isxdigit(cd->content[i])) {
+                    *sigerror = "Invalid ssh.hassh.server string (should be string of hexademical "
+                                "characters)."
+                                "This rule will therefore never match.";
+                    SCLogWarning("rule %u: %s", s->id, *sigerror);
+                    return false;
+                }
+            }
         }
     }
-
     return true;
 }
 
 static void DetectSshHasshServerHashSetupCallback(const DetectEngineCtx *de_ctx,
                                           Signature *s)
 {
-    SigMatch *sm = s->init_data->smlists[g_ssh_hassh_buffer_id];
-    for ( ; sm != NULL; sm = sm->next)
-    {
-        if (sm->type != DETECT_CONTENT)
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        if (s->init_data->buffers[x].id != (uint32_t)g_ssh_hassh_buffer_id)
             continue;
-
-        DetectContentData *cd = (DetectContentData *)sm->ctx;
-
-        uint32_t u;
-        for (u = 0; u < cd->content_len; u++)
-        {
-            if (isupper(cd->content[u])) {
-                cd->content[u] = u8_tolower(cd->content[u]);
+        SigMatch *sm = s->init_data->buffers[x].head;
+        for (; sm != NULL; sm = sm->next) {
+            if (sm->type != DETECT_CONTENT)
+                continue;
+
+            DetectContentData *cd = (DetectContentData *)sm->ctx;
+
+            uint32_t u;
+            for (u = 0; u < cd->content_len; u++) {
+                if (isupper(cd->content[u])) {
+                    cd->content[u] = u8_tolower(cd->content[u]);
+                }
             }
-        }
 
-        SpmDestroyCtx(cd->spm_ctx);
-        cd->spm_ctx = SpmInitCtx(cd->content, cd->content_len, 1,
-                       de_ctx->spm_global_thread_ctx);
+            SpmDestroyCtx(cd->spm_ctx);
+            cd->spm_ctx =
+                    SpmInitCtx(cd->content, cd->content_len, 1, de_ctx->spm_global_thread_ctx);
+        }
     }
 }
 
index a2da4e2c3b43b8064eb9d591a988b79f1db889fe..75a88d25d53c6573918088433b567db1873d6c58 100644 (file)
@@ -122,66 +122,68 @@ static int DetectSshHasshSetup(DetectEngineCtx *de_ctx, Signature *s, const char
 static bool DetectSshHasshHashValidateCallback(const Signature *s,
                                               const char **sigerror)
 {
-    const SigMatch *sm = s->init_data->smlists[g_ssh_hassh_buffer_id];
-    for ( ; sm != NULL; sm = sm->next)
-    {
-        if (sm->type != DETECT_CONTENT)
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        if (s->init_data->buffers[x].id != (uint32_t)g_ssh_hassh_buffer_id)
             continue;
+        const SigMatch *sm = s->init_data->buffers[x].head;
+        for (; sm != NULL; sm = sm->next) {
+            if (sm->type != DETECT_CONTENT)
+                continue;
 
-        const DetectContentData *cd = (DetectContentData *)sm->ctx;
+            const DetectContentData *cd = (DetectContentData *)sm->ctx;
 
-        if (cd->flags & DETECT_CONTENT_NOCASE) {
-            *sigerror = "ssh.hassh should not be used together with "
-                        "nocase, since the rule is automatically "
-                        "lowercased anyway which makes nocase redundant.";
-            SCLogWarning("rule %u: %s", s->id, *sigerror);
-        }
+            if (cd->flags & DETECT_CONTENT_NOCASE) {
+                *sigerror = "ssh.hassh should not be used together with "
+                            "nocase, since the rule is automatically "
+                            "lowercased anyway which makes nocase redundant.";
+                SCLogWarning("rule %u: %s", s->id, *sigerror);
+            }
 
-        if (cd->content_len != 32)
-        {
-            *sigerror = "Invalid length of the specified ssh.hassh (should "
-                        "be 32 characters long). This rule will therefore "
-                        "never match.";
-            SCLogWarning("rule %u: %s", s->id, *sigerror);
-            return false;
-        }
-        for (size_t i = 0; i < cd->content_len; ++i)
-        {
-            if(!isxdigit(cd->content[i])) 
-            {
-                *sigerror = "Invalid ssh.hassh string (should be string of hexademical characters)."
-                            "This rule will therefore never match.";
+            if (cd->content_len != 32) {
+                *sigerror = "Invalid length of the specified ssh.hassh (should "
+                            "be 32 characters long). This rule will therefore "
+                            "never match.";
                 SCLogWarning("rule %u: %s", s->id, *sigerror);
                 return false;
             }
+            for (size_t i = 0; i < cd->content_len; ++i) {
+                if (!isxdigit(cd->content[i])) {
+                    *sigerror =
+                            "Invalid ssh.hassh string (should be string of hexademical characters)."
+                            "This rule will therefore never match.";
+                    SCLogWarning("rule %u: %s", s->id, *sigerror);
+                    return false;
+                }
+            }
         }
     }
-
     return true;
 }
 
 static void DetectSshHasshHashSetupCallback(const DetectEngineCtx *de_ctx,
                                           Signature *s)
 {
-    SigMatch *sm = s->init_data->smlists[g_ssh_hassh_buffer_id];
-    for ( ; sm != NULL; sm = sm->next)
-    {
-        if (sm->type != DETECT_CONTENT)
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        if (s->init_data->buffers[x].id != (uint32_t)g_ssh_hassh_buffer_id)
             continue;
-
-        DetectContentData *cd = (DetectContentData *)sm->ctx;
-
-        uint32_t u;
-        for (u = 0; u < cd->content_len; u++)
-        {
-            if (isupper(cd->content[u])) {
-                cd->content[u] = u8_tolower(cd->content[u]);
+        SigMatch *sm = s->init_data->buffers[x].head;
+        for (; sm != NULL; sm = sm->next) {
+            if (sm->type != DETECT_CONTENT)
+                continue;
+
+            DetectContentData *cd = (DetectContentData *)sm->ctx;
+
+            uint32_t u;
+            for (u = 0; u < cd->content_len; u++) {
+                if (isupper(cd->content[u])) {
+                    cd->content[u] = u8_tolower(cd->content[u]);
+                }
             }
-        }
 
-        SpmDestroyCtx(cd->spm_ctx);
-        cd->spm_ctx = SpmInitCtx(cd->content, cd->content_len, 1,
-                       de_ctx->spm_global_thread_ctx);
+            SpmDestroyCtx(cd->spm_ctx);
+            cd->spm_ctx =
+                    SpmInitCtx(cd->content, cd->content_len, 1, de_ctx->spm_global_thread_ctx);
+        }
     }
 }
 
index b459c440ce9e16fe4482d74eeb7e16654a0a7690..bee1d0177572db93db6362daef9949848aa1aa91 100644 (file)
@@ -163,76 +163,79 @@ static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx,
 static bool DetectTlsFingerprintValidateCallback(const Signature *s,
                                                   const char **sigerror)
 {
-    const SigMatch *sm = s->init_data->smlists[g_tls_cert_fingerprint_buffer_id];
-    for ( ; sm != NULL; sm = sm->next)
-    {
-        if (sm->type != DETECT_CONTENT)
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        if (s->init_data->buffers[x].id != (uint32_t)g_tls_cert_fingerprint_buffer_id)
             continue;
+        const SigMatch *sm = s->init_data->buffers[x].head;
+        for (; sm != NULL; sm = sm->next) {
+            if (sm->type != DETECT_CONTENT)
+                continue;
+
+            const DetectContentData *cd = (DetectContentData *)sm->ctx;
+
+            if (cd->content_len != 59) {
+                *sigerror = "Invalid length of the specified fingerprint. "
+                            "This rule will therefore never match.";
+                SCLogWarning("rule %u: %s", s->id, *sigerror);
+                return false;
+            }
 
-        const DetectContentData *cd = (DetectContentData *)sm->ctx;
-
-        if (cd->content_len != 59) {
-            *sigerror = "Invalid length of the specified fingerprint. "
-                        "This rule will therefore never match.";
-            SCLogWarning("rule %u: %s", s->id, *sigerror);
-            return false;
-        }
-
-        bool have_delimiters = false;
-        uint32_t u;
-        for (u = 0; u < cd->content_len; u++)
-        {
-            if (cd->content[u] == ':') {
-                have_delimiters = true;
-                break;
+            bool have_delimiters = false;
+            uint32_t u;
+            for (u = 0; u < cd->content_len; u++) {
+                if (cd->content[u] == ':') {
+                    have_delimiters = true;
+                    break;
+                }
             }
-        }
 
-        if (!have_delimiters) {
-            *sigerror = "No colon delimiters ':' detected in content after "
-                        "tls.cert_fingerprint. This rule will therefore "
-                        "never match.";
-            SCLogWarning("rule %u: %s", s->id, *sigerror);
-            return false;
-        }
+            if (!have_delimiters) {
+                *sigerror = "No colon delimiters ':' detected in content after "
+                            "tls.cert_fingerprint. This rule will therefore "
+                            "never match.";
+                SCLogWarning("rule %u: %s", s->id, *sigerror);
+                return false;
+            }
 
-        if (cd->flags & DETECT_CONTENT_NOCASE) {
-            *sigerror = "tls.cert_fingerprint should not be used together "
-                        "with nocase, since the rule is automatically "
-                        "lowercased anyway which makes nocase redundant.";
-            SCLogWarning("rule %u: %s", s->id, *sigerror);
+            if (cd->flags & DETECT_CONTENT_NOCASE) {
+                *sigerror = "tls.cert_fingerprint should not be used together "
+                            "with nocase, since the rule is automatically "
+                            "lowercased anyway which makes nocase redundant.";
+                SCLogWarning("rule %u: %s", s->id, *sigerror);
+            }
         }
     }
-
     return true;
 }
 
 static void DetectTlsFingerprintSetupCallback(const DetectEngineCtx *de_ctx,
                                               Signature *s)
 {
-    SigMatch *sm = s->init_data->smlists[g_tls_cert_fingerprint_buffer_id];
-    for ( ; sm != NULL; sm = sm->next)
-    {
-        if (sm->type != DETECT_CONTENT)
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        if (s->init_data->buffers[x].id != (uint32_t)g_tls_cert_fingerprint_buffer_id)
             continue;
-
-        DetectContentData *cd = (DetectContentData *)sm->ctx;
-
-        bool changed = false;
-        uint32_t u;
-        for (u = 0; u < cd->content_len; u++)
-        {
-            if (isupper(cd->content[u])) {
-                cd->content[u] = u8_tolower(cd->content[u]);
-                changed = true;
+        SigMatch *sm = s->init_data->buffers[x].head;
+        for (; sm != NULL; sm = sm->next) {
+            if (sm->type != DETECT_CONTENT)
+                continue;
+
+            DetectContentData *cd = (DetectContentData *)sm->ctx;
+
+            bool changed = false;
+            uint32_t u;
+            for (u = 0; u < cd->content_len; u++) {
+                if (isupper(cd->content[u])) {
+                    cd->content[u] = u8_tolower(cd->content[u]);
+                    changed = true;
+                }
             }
-        }
 
-        /* recreate the context if changes were made */
-        if (changed) {
-            SpmDestroyCtx(cd->spm_ctx);
-            cd->spm_ctx = SpmInitCtx(cd->content, cd->content_len, 1,
-                                     de_ctx->spm_global_thread_ctx);
+            /* recreate the context if changes were made */
+            if (changed) {
+                SpmDestroyCtx(cd->spm_ctx);
+                cd->spm_ctx =
+                        SpmInitCtx(cd->content, cd->content_len, 1, de_ctx->spm_global_thread_ctx);
+            }
         }
     }
 }
index be03423d394490605a1cc79aac8b9b9ba10b05a7..19c86be80e2484bff68f1abe3c5548c4ebfb1992 100644 (file)
@@ -162,67 +162,71 @@ static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx,
 static bool DetectTlsSerialValidateCallback(const Signature *s,
                                              const char **sigerror)
 {
-    const SigMatch *sm = s->init_data->smlists[g_tls_cert_serial_buffer_id];
-    for ( ; sm != NULL; sm = sm->next)
-    {
-        if (sm->type != DETECT_CONTENT)
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        if (s->init_data->buffers[x].id != (uint32_t)g_tls_cert_serial_buffer_id)
             continue;
+        const SigMatch *sm = s->init_data->buffers[x].head;
+        for (; sm != NULL; sm = sm->next) {
+            if (sm->type != DETECT_CONTENT)
+                continue;
+
+            const DetectContentData *cd = (DetectContentData *)sm->ctx;
+
+            if (cd->flags & DETECT_CONTENT_NOCASE) {
+                *sigerror = "tls.cert_serial should not be used together "
+                            "with nocase, since the rule is automatically "
+                            "uppercased anyway which makes nocase redundant.";
+                SCLogWarning("rule %u: %s", s->id, *sigerror);
+            }
 
-        const DetectContentData *cd = (DetectContentData *)sm->ctx;
-
-        if (cd->flags & DETECT_CONTENT_NOCASE) {
-            *sigerror = "tls.cert_serial should not be used together "
-                        "with nocase, since the rule is automatically "
-                        "uppercased anyway which makes nocase redundant.";
-            SCLogWarning("rule %u: %s", s->id, *sigerror);
-        }
-
-        /* no need to worry about this if the content is short enough */
-        if (cd->content_len <= 2)
-            return true;
-
-        uint32_t u;
-        for (u = 0; u < cd->content_len; u++)
-            if (cd->content[u] == ':')
+            /* no need to worry about this if the content is short enough */
+            if (cd->content_len <= 2)
                 return true;
 
-        *sigerror = "No colon delimiters ':' detected in content after "
-                    "tls.cert_serial. This rule will therefore never "
-                    "match.";
-        SCLogWarning("rule %u: %s", s->id, *sigerror);
+            uint32_t u;
+            for (u = 0; u < cd->content_len; u++)
+                if (cd->content[u] == ':')
+                    return true;
 
-        return false;
-    }
+            *sigerror = "No colon delimiters ':' detected in content after "
+                        "tls.cert_serial. This rule will therefore never "
+                        "match.";
+            SCLogWarning("rule %u: %s", s->id, *sigerror);
 
+            return false;
+        }
+    }
     return true;
 }
 
 static void DetectTlsSerialSetupCallback(const DetectEngineCtx *de_ctx,
                                          Signature *s)
 {
-    SigMatch *sm = s->init_data->smlists[g_tls_cert_serial_buffer_id];
-    for ( ; sm != NULL; sm = sm->next)
-    {
-        if (sm->type != DETECT_CONTENT)
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        if (s->init_data->buffers[x].id != (uint32_t)g_tls_cert_serial_buffer_id)
             continue;
-
-        DetectContentData *cd = (DetectContentData *)sm->ctx;
-
-        bool changed = false;
-        uint32_t u;
-        for (u = 0; u < cd->content_len; u++)
-        {
-            if (islower(cd->content[u])) {
-                cd->content[u] = u8_toupper(cd->content[u]);
-                changed = true;
+        SigMatch *sm = s->init_data->buffers[x].head;
+        for (; sm != NULL; sm = sm->next) {
+            if (sm->type != DETECT_CONTENT)
+                continue;
+
+            DetectContentData *cd = (DetectContentData *)sm->ctx;
+
+            bool changed = false;
+            uint32_t u;
+            for (u = 0; u < cd->content_len; u++) {
+                if (islower(cd->content[u])) {
+                    cd->content[u] = u8_toupper(cd->content[u]);
+                    changed = true;
+                }
             }
-        }
 
-        /* recreate the context if changes were made */
-        if (changed) {
-            SpmDestroyCtx(cd->spm_ctx);
-            cd->spm_ctx = SpmInitCtx(cd->content, cd->content_len, 1,
-                                     de_ctx->spm_global_thread_ctx);
+            /* recreate the context if changes were made */
+            if (changed) {
+                SpmDestroyCtx(cd->spm_ctx);
+                cd->spm_ctx =
+                        SpmInitCtx(cd->content, cd->content_len, 1, de_ctx->spm_global_thread_ctx);
+            }
         }
     }
 }
index 8aa1f58f303a51310a424307405845becfe8e448..7660fde4c2a07227f272bb32787adbdfe39bcfc2 100644 (file)
@@ -164,60 +164,64 @@ static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx,
 static bool DetectTlsJa3HashValidateCallback(const Signature *s,
                                               const char **sigerror)
 {
-    const SigMatch *sm = s->init_data->smlists[g_tls_ja3_hash_buffer_id];
-    for ( ; sm != NULL; sm = sm->next)
-    {
-        if (sm->type != DETECT_CONTENT)
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        if (s->init_data->buffers[x].id != (uint32_t)g_tls_ja3_hash_buffer_id)
             continue;
+        const SigMatch *sm = s->init_data->buffers[x].head;
+        for (; sm != NULL; sm = sm->next) {
+            if (sm->type != DETECT_CONTENT)
+                continue;
+
+            const DetectContentData *cd = (DetectContentData *)sm->ctx;
+
+            if (cd->flags & DETECT_CONTENT_NOCASE) {
+                *sigerror = "ja3.hash should not be used together with "
+                            "nocase, since the rule is automatically "
+                            "lowercased anyway which makes nocase redundant.";
+                SCLogWarning("rule %u: %s", s->id, *sigerror);
+            }
 
-        const DetectContentData *cd = (DetectContentData *)sm->ctx;
+            if (cd->content_len == SC_MD5_HEX_LEN)
+                return true;
 
-        if (cd->flags & DETECT_CONTENT_NOCASE) {
-            *sigerror = "ja3.hash should not be used together with "
-                        "nocase, since the rule is automatically "
-                        "lowercased anyway which makes nocase redundant.";
+            *sigerror = "Invalid length of the specified JA3 hash (should "
+                        "be 32 characters long). This rule will therefore "
+                        "never match.";
             SCLogWarning("rule %u: %s", s->id, *sigerror);
+            return false;
         }
-
-        if (cd->content_len == SC_MD5_HEX_LEN)
-            return true;
-
-        *sigerror = "Invalid length of the specified JA3 hash (should "
-                    "be 32 characters long). This rule will therefore "
-                    "never match.";
-        SCLogWarning("rule %u: %s", s->id, *sigerror);
-        return false;
     }
-
     return true;
 }
 
 static void DetectTlsJa3HashSetupCallback(const DetectEngineCtx *de_ctx,
                                           Signature *s)
 {
-    SigMatch *sm = s->init_data->smlists[g_tls_ja3_hash_buffer_id];
-    for ( ; sm != NULL; sm = sm->next)
-    {
-        if (sm->type != DETECT_CONTENT)
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        if (s->init_data->buffers[x].id != (uint32_t)g_tls_ja3_hash_buffer_id)
             continue;
-
-        DetectContentData *cd = (DetectContentData *)sm->ctx;
-
-        bool changed = false;
-        uint32_t u;
-        for (u = 0; u < cd->content_len; u++)
-        {
-            if (isupper(cd->content[u])) {
-                cd->content[u] = u8_tolower(cd->content[u]);
-                changed = true;
+        SigMatch *sm = s->init_data->buffers[x].head;
+        for (; sm != NULL; sm = sm->next) {
+            if (sm->type != DETECT_CONTENT)
+                continue;
+
+            DetectContentData *cd = (DetectContentData *)sm->ctx;
+
+            bool changed = false;
+            uint32_t u;
+            for (u = 0; u < cd->content_len; u++) {
+                if (isupper(cd->content[u])) {
+                    cd->content[u] = u8_tolower(cd->content[u]);
+                    changed = true;
+                }
             }
-        }
 
-        /* recreate the context if changes were made */
-        if (changed) {
-            SpmDestroyCtx(cd->spm_ctx);
-            cd->spm_ctx = SpmInitCtx(cd->content, cd->content_len, 1,
-                                     de_ctx->spm_global_thread_ctx);
+            /* recreate the context if changes were made */
+            if (changed) {
+                SpmDestroyCtx(cd->spm_ctx);
+                cd->spm_ctx =
+                        SpmInitCtx(cd->content, cd->content_len, 1, de_ctx->spm_global_thread_ctx);
+            }
         }
     }
 }
index 93dc9e24abd8a55864d9c37fdce488eed5fadefa..583566012d08cde28b54eb8e4672a4c9e937c8de 100644 (file)
@@ -162,60 +162,64 @@ static InspectionBuffer *GetData(DetectEngineThreadCtx *det_ctx,
 static bool DetectTlsJa3SHashValidateCallback(const Signature *s,
                                                const char **sigerror)
 {
-    const SigMatch *sm = s->init_data->smlists[g_tls_ja3s_hash_buffer_id];
-    for ( ; sm != NULL; sm = sm->next)
-    {
-        if (sm->type != DETECT_CONTENT)
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        if (s->init_data->buffers[x].id != (uint32_t)g_tls_ja3s_hash_buffer_id)
             continue;
+        const SigMatch *sm = s->init_data->buffers[x].head;
+        for (; sm != NULL; sm = sm->next) {
+            if (sm->type != DETECT_CONTENT)
+                continue;
+
+            const DetectContentData *cd = (DetectContentData *)sm->ctx;
+
+            if (cd->flags & DETECT_CONTENT_NOCASE) {
+                *sigerror = "ja3s.hash should not be used together with "
+                            "nocase, since the rule is automatically "
+                            "lowercased anyway which makes nocase redundant.";
+                SCLogWarning("rule %u: %s", s->id, *sigerror);
+            }
 
-        const DetectContentData *cd = (DetectContentData *)sm->ctx;
+            if (cd->content_len == SC_MD5_HEX_LEN)
+                return true;
 
-        if (cd->flags & DETECT_CONTENT_NOCASE) {
-            *sigerror = "ja3s.hash should not be used together with "
-                        "nocase, since the rule is automatically "
-                        "lowercased anyway which makes nocase redundant.";
-            SCLogWarning("rule %u: %s", s->id, *sigerror);
+            *sigerror = "Invalid length of the specified JA3S hash (should "
+                        "be 32 characters long). This rule will therefore "
+                        "never match.";
+            SCLogError("rule %u: %s", s->id, *sigerror);
+            return false;
         }
-
-        if (cd->content_len == SC_MD5_HEX_LEN)
-            return true;
-
-        *sigerror = "Invalid length of the specified JA3S hash (should "
-                    "be 32 characters long). This rule will therefore "
-                    "never match.";
-        SCLogError("rule %u: %s", s->id, *sigerror);
-        return false;
     }
-
     return true;
 }
 
 static void DetectTlsJa3SHashSetupCallback(const DetectEngineCtx *de_ctx,
                                            Signature *s)
 {
-    SigMatch *sm = s->init_data->smlists[g_tls_ja3s_hash_buffer_id];
-    for ( ; sm != NULL; sm = sm->next)
-    {
-        if (sm->type != DETECT_CONTENT)
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        if (s->init_data->buffers[x].id != (uint32_t)g_tls_ja3s_hash_buffer_id)
             continue;
-
-        DetectContentData *cd = (DetectContentData *)sm->ctx;
-
-        bool changed = false;
-        uint32_t u;
-        for (u = 0; u < cd->content_len; u++)
-        {
-            if (isupper(cd->content[u])) {
-                cd->content[u] = u8_tolower(cd->content[u]);
-                changed = true;
+        SigMatch *sm = s->init_data->buffers[x].head;
+        for (; sm != NULL; sm = sm->next) {
+            if (sm->type != DETECT_CONTENT)
+                continue;
+
+            DetectContentData *cd = (DetectContentData *)sm->ctx;
+
+            bool changed = false;
+            uint32_t u;
+            for (u = 0; u < cd->content_len; u++) {
+                if (isupper(cd->content[u])) {
+                    cd->content[u] = u8_tolower(cd->content[u]);
+                    changed = true;
+                }
             }
-        }
 
-        /* recreate the context if changes were made */
-        if (changed) {
-            SpmDestroyCtx(cd->spm_ctx);
-            cd->spm_ctx = SpmInitCtx(cd->content, cd->content_len, 1,
-                                     de_ctx->spm_global_thread_ctx);
+            /* recreate the context if changes were made */
+            if (changed) {
+                SpmDestroyCtx(cd->spm_ctx);
+                cd->spm_ctx =
+                        SpmInitCtx(cd->content, cd->content_len, 1, de_ctx->spm_global_thread_ctx);
+            }
         }
     }
 }
index 6f7c4da2062ccf9e9384fce3e3b26f0d13f06b13..9add5838cc07af436cc7b9178a9678cbc445dd55 100644 (file)
@@ -147,86 +147,93 @@ static void DetectUrilenFree(DetectEngineCtx *de_ctx, void *ptr)
  */
 void DetectUrilenApplyToContent(Signature *s, int list)
 {
-    uint16_t high = UINT16_MAX;
-    bool found = false;
-
-    SigMatch *sm = s->init_data->smlists[list];
-    for ( ; sm != NULL; sm = sm->next) {
-        if (sm->type != DETECT_AL_URILEN)
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        if (s->init_data->buffers[x].id != (uint32_t)list)
             continue;
 
-        DetectUrilenData *dd = (DetectUrilenData *)sm->ctx;
-
-        switch (dd->du16.mode) {
-            case DETECT_UINT_LT:
-                if (dd->du16.arg1 < UINT16_MAX) {
-                    high = dd->du16.arg1 + 1;
-                }
-                break;
-            case DETECT_UINT_LTE:
-                // fallthrough
-            case DETECT_UINT_EQ:
-                high = dd->du16.arg1;
-                break;
-            case DETECT_UINT_RA:
-                if (dd->du16.arg2 < UINT16_MAX) {
-                    high = dd->du16.arg2 + 1;
-                }
-                break;
-            case DETECT_UINT_NE:
-                // fallthrough
-            case DETECT_UINT_GTE:
-                // fallthrough
-            case DETECT_UINT_GT:
-                high = UINT16_MAX;
-                break;
-        }
-        found = true;
-    }
-
-    // skip 65535 to avoid mismatch on uri > 64k
-    if (!found || high == UINT16_MAX)
-        return;
-
-    SCLogDebug("high %u", high);
-
-    sm = s->init_data->smlists[list];
-    for ( ; sm != NULL;  sm = sm->next) {
-        if (sm->type != DETECT_CONTENT) {
-            continue;
-        }
-        DetectContentData *cd = (DetectContentData *)sm->ctx;
-        if (cd == NULL) {
-            continue;
+        uint16_t high = UINT16_MAX;
+        bool found = false;
+
+        for (SigMatch *sm = s->init_data->buffers[x].head; sm != NULL; sm = sm->next) {
+            if (sm->type != DETECT_AL_URILEN)
+                continue;
+
+            DetectUrilenData *dd = (DetectUrilenData *)sm->ctx;
+
+            switch (dd->du16.mode) {
+                case DETECT_UINT_LT:
+                    if (dd->du16.arg1 < UINT16_MAX) {
+                        high = dd->du16.arg1 + 1;
+                    }
+                    break;
+                case DETECT_UINT_LTE:
+                    // fallthrough
+                case DETECT_UINT_EQ:
+                    high = dd->du16.arg1;
+                    break;
+                case DETECT_UINT_RA:
+                    if (dd->du16.arg2 < UINT16_MAX) {
+                        high = dd->du16.arg2 + 1;
+                    }
+                    break;
+                case DETECT_UINT_NE:
+                    // fallthrough
+                case DETECT_UINT_GTE:
+                    // fallthrough
+                case DETECT_UINT_GT:
+                    high = UINT16_MAX;
+                    break;
+            }
+            found = true;
         }
 
-        if (cd->depth == 0 || cd->depth > high) {
-            cd->depth = high;
-            cd->flags |= DETECT_CONTENT_DEPTH;
-            SCLogDebug("updated %u, content %u to have depth %u "
-                    "because of urilen.", s->id, cd->id, cd->depth);
+        // skip 65535 to avoid mismatch on uri > 64k
+        if (!found || high == UINT16_MAX)
+            return;
+
+        SCLogDebug("high %u", high);
+
+        for (SigMatch *sm = s->init_data->buffers[x].head; sm != NULL; sm = sm->next) {
+            if (sm->type != DETECT_CONTENT) {
+                continue;
+            }
+            DetectContentData *cd = (DetectContentData *)sm->ctx;
+            if (cd == NULL) {
+                continue;
+            }
+
+            if (cd->depth == 0 || cd->depth > high) {
+                cd->depth = high;
+                cd->flags |= DETECT_CONTENT_DEPTH;
+                SCLogDebug("updated %u, content %u to have depth %u "
+                           "because of urilen.",
+                        s->id, cd->id, cd->depth);
+            }
         }
     }
 }
 
 bool DetectUrilenValidateContent(const Signature *s, int list, const char **sigerror)
 {
-    const SigMatch *sm = s->init_data->smlists[list];
-    for ( ; sm != NULL;  sm = sm->next) {
-        if (sm->type != DETECT_CONTENT) {
+    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+        if (s->init_data->buffers[x].id != (uint32_t)list)
             continue;
-        }
-        DetectContentData *cd = (DetectContentData *)sm->ctx;
-        if (cd == NULL) {
-            continue;
-        }
-
-        if (cd->depth && cd->depth < cd->content_len) {
-            *sigerror = "depth or urilen smaller than content len";
-            SCLogError("depth or urilen %u smaller "
-                       "than content len %u",
-                    cd->depth, cd->content_len);
-            return false;
+        for (const SigMatch *sm = s->init_data->buffers[x].head; sm != NULL; sm = sm->next) {
+            if (sm->type != DETECT_CONTENT) {
+                continue;
+            }
+            DetectContentData *cd = (DetectContentData *)sm->ctx;
+            if (cd == NULL) {
+                continue;
+            }
+
+            if (cd->depth && cd->depth < cd->content_len) {
+                *sigerror = "depth or urilen smaller than content len";
+                SCLogError("depth or urilen %u smaller "
+                           "than content len %u",
+                        cd->depth, cd->content_len);
+                return false;
+            }
         }
     }
     return true;
index bcaec38a32c7a46a1e6407291fc38672f1fdec33..a41b74ed8cf8f704117664b63b99f6cfb1c18ee5 100644 (file)
@@ -73,7 +73,7 @@ struct SCSigSignatureWrapper_;
 
 /* holds the values for different possible lists in struct Signature.
  * These codes are access points to particular lists in the array
- * Signature->sm_lists[DETECT_SM_LIST_MAX]. */
+ * Signature->init_data->smlists[DETECT_SM_LIST_MAX]. */
 enum DetectSigmatchListEnum {
     /* list for non-payload per packet matches, e.g. ttl, flow keyword */
     DETECT_SM_LIST_MATCH = 0,
@@ -420,6 +420,7 @@ typedef struct DetectBufferType_ {
     bool packet; /**< compat to packet matches */
     bool frame;  /**< is about Frame inspection */
     bool supports_transforms;
+    bool multi_instance; /**< buffer supports multiple buffer instances per tx */
     void (*SetupCallback)(const struct DetectEngineCtx_ *, struct Signature_ *);
     bool (*ValidateCallback)(const struct Signature_ *, const char **sigerror);
     DetectEngineTransforms transforms;
@@ -483,10 +484,15 @@ typedef struct DetectEngineFrameInspectionEngine {
     struct DetectEngineFrameInspectionEngine *next;
 } DetectEngineFrameInspectionEngine;
 
-#ifdef UNITTESTS
-#define sm_lists init_data->smlists
-#define sm_lists_tail init_data->smlists_tail
-#endif
+typedef struct SignatureInitDataBuffer_ {
+    uint32_t id;  /**< buffer id */
+    bool sm_init; /**< initialized by sigmatch, which is likely something like `urilen:10; http.uri;
+                     content:"abc";`. These need to be in the same list. Unset once `http.uri` is
+                     set up. */
+    /* sig match list */
+    SigMatch *head;
+    SigMatch *tail;
+} SignatureInitDataBuffer;
 
 typedef struct SignatureInitData_ {
     /** Number of sigmatches. Used for assigning SigMatch::idx */
@@ -531,11 +537,19 @@ typedef struct SignatureInitData_ {
 
     int prefilter_list;
 
-    uint32_t smlists_array_size;
-    /* holds all sm lists */
-    struct SigMatch_ **smlists;
-    /* holds all sm lists' tails */
-    struct SigMatch_ **smlists_tail;
+    /* holds built-in sm lists */
+    struct SigMatch_ *smlists[DETECT_SM_LIST_MAX];
+    /* holds built-in sm lists' tails */
+    struct SigMatch_ *smlists_tail[DETECT_SM_LIST_MAX];
+
+    /* Storage for buffers. */
+    SignatureInitDataBuffer *buffers;
+    uint32_t buffer_index;
+    uint32_t buffers_size;
+    SignatureInitDataBuffer *curbuf;
+
+    /* highest list/buffer id which holds a DETECT_CONTENT */
+    uint32_t max_content_list_id;
 } SignatureInitData;
 
 /** \brief Signature container */