• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OISF / suricata / 23374838686

21 Mar 2026 07:29AM UTC coverage: 59.341% (-20.0%) from 79.315%
23374838686

Pull #15075

github

web-flow
Merge 90b4e834f into 6587e363a
Pull Request #15075: Stack 8001 v16.4

38 of 70 new or added lines in 10 files covered. (54.29%)

34165 existing lines in 563 files now uncovered.

119621 of 201584 relevant lines covered (59.34%)

650666.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.29
/src/detect-flowbits.c
1
/* Copyright (C) 2007-2025 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17

18
/**
19
 * \file
20
 *
21
 *  \author Victor Julien <victor@inliniac.net>
22
 *  \author Breno Silva <breno.silva@gmail.com>
23
 *
24
 * Implements the flowbits keyword
25
 */
26

27
#include "suricata-common.h"
28
#include "decode.h"
29
#include "action-globals.h"
30
#include "detect.h"
31
#include "threads.h"
32
#include "flow.h"
33
#include "flow-bit.h"
34
#include "flow-util.h"
35
#include "detect-flowbits.h"
36
#include "util-spm.h"
37
#include "rust.h"
38

39
#include "app-layer-parser.h"
40

41
#include "detect-parse.h"
42
#include "detect-engine.h"
43
#include "detect-engine-mpm.h"
44
#include "detect-engine-state.h"
45
#include "detect-engine-build.h"
46
#include "detect-engine-prefilter.h"
47

48
#include "tree.h"
49

50
#include "util-enum.h"
51
#include "util-var-name.h"
52
#include "util-unittest.h"
53
#include "util-debug.h"
54
#include "util-conf.h"
55

56
#define PARSE_REGEX         "^([a-z]+)(?:,\\s*(.*))?"
3✔
57
static DetectParseRegex parse_regex;
58

59
#define MAX_TOKENS 100
35,637✔
60

61
int DetectFlowbitMatch (DetectEngineThreadCtx *, Packet *,
62
        const Signature *, const SigMatchCtx *);
63
static int DetectFlowbitSetup (DetectEngineCtx *, Signature *, const char *);
64
static int FlowbitOrAddData(DetectEngineCtx *, DetectFlowbitsData *, char *);
65
void DetectFlowbitFree (DetectEngineCtx *, void *);
66
#ifdef UNITTESTS
67
void FlowBitsRegisterTests(void);
68
#endif
69
static bool PrefilterFlowbitIsPrefilterable(const Signature *s);
70
static int PrefilterSetupFlowbits(DetectEngineCtx *de_ctx, SigGroupHead *sgh);
71

72
void DetectFlowbitsRegister (void)
73
{
3✔
74
    sigmatch_table[DETECT_FLOWBITS].name = "flowbits";
3✔
75
    sigmatch_table[DETECT_FLOWBITS].desc = "operate on flow flag";
3✔
76
    sigmatch_table[DETECT_FLOWBITS].url = "/rules/flow-keywords.html#flowbits";
3✔
77
    sigmatch_table[DETECT_FLOWBITS].Match = DetectFlowbitMatch;
3✔
78
    sigmatch_table[DETECT_FLOWBITS].Setup = DetectFlowbitSetup;
3✔
79
    sigmatch_table[DETECT_FLOWBITS].Free  = DetectFlowbitFree;
3✔
80
#ifdef UNITTESTS
81
    sigmatch_table[DETECT_FLOWBITS].RegisterTests = FlowBitsRegisterTests;
82
#endif
83
    /* this is compatible to ip-only signatures */
84
    sigmatch_table[DETECT_FLOWBITS].flags |= (SIGMATCH_IPONLY_COMPAT | SIGMATCH_SUPPORT_FIREWALL);
3✔
85

86
    sigmatch_table[DETECT_FLOWBITS].SupportsPrefilter = PrefilterFlowbitIsPrefilterable;
3✔
87
    sigmatch_table[DETECT_FLOWBITS].SetupPrefilter = PrefilterSetupFlowbits;
3✔
88
    /* all but pre_flow */
89
    sigmatch_table[DETECT_FLOWBITS].tables =
3✔
90
            DETECT_TABLE_PACKET_PRE_STREAM_FLAG | DETECT_TABLE_PACKET_FILTER_FLAG |
3✔
91
            DETECT_TABLE_PACKET_TD_FLAG | DETECT_TABLE_APP_FILTER_FLAG | DETECT_TABLE_APP_TD_FLAG;
3✔
92
    DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
3✔
93
}
3✔
94

95
static bool DetectFlowbitIsPostmatch(uint8_t cmd)
96
{
134,558✔
97
    DEBUG_VALIDATE_BUG_ON(cmd >= DETECT_FLOWBITS_CMD_MAX);
134,558✔
98

99
    switch (cmd) {
134,558✔
100
        case DETECT_FLOWBITS_CMD_TOGGLE:
22,950✔
101
        case DETECT_FLOWBITS_CMD_SET:
55,492✔
102
        case DETECT_FLOWBITS_CMD_UNSET:
82,431✔
103
            return true;
82,431✔
104
    }
134,558✔
105
    return false;
52,127✔
106
}
134,558✔
107

108
SCEnumCharMap flowbit_cmds[] = {
109
    { "set", DETECT_FLOWBITS_CMD_SET },
110
    { "toggle", DETECT_FLOWBITS_CMD_TOGGLE },
111
    { "unset", DETECT_FLOWBITS_CMD_UNSET },
112
    { "isnotset", DETECT_FLOWBITS_CMD_ISNOTSET },
113
    { "isset", DETECT_FLOWBITS_CMD_ISSET },
114
};
115

116
static inline int DetectFlowbitValidateDo(
117
        Signature *s, uint8_t cmd, uint8_t cmd2, uint32_t idx, bool err)
118
{
134,558✔
119
    bool postmatch = DetectFlowbitIsPostmatch(cmd);
134,558✔
120
    SigMatch *list = postmatch ? s->init_data->smlists[DETECT_SM_LIST_POSTMATCH]
134,558✔
121
                               : s->init_data->smlists[DETECT_SM_LIST_MATCH];
134,558✔
122

123
    for (SigMatch *sm = list; sm != NULL; sm = sm->next) {
415,218✔
124
        if (sm->type != DETECT_FLOWBITS)
280,673✔
125
            continue;
80,211✔
126

127
        DetectFlowbitsData *fd = (DetectFlowbitsData *)sm->ctx;
200,462✔
128
        if ((fd->idx == idx) && (fd->cmd == cmd)) {
200,462✔
129
            if (err) {
13✔
130
                SCLogError("invalid flowbit command combination in the same signature: isset and "
13✔
131
                           "isnotset");
13✔
132
                return -1;
13✔
133
            }
13✔
UNCOV
134
            SCLogWarning(
×
UNCOV
135
                    "inconsequential flowbit command combination in the same signature: %s and %s",
×
UNCOV
136
                    SCMapEnumValueToName(cmd, flowbit_cmds),
×
UNCOV
137
                    SCMapEnumValueToName(cmd2, flowbit_cmds));
×
UNCOV
138
            return 0;
×
139
        }
13✔
140
    }
200,462✔
141

142
    /* no invalid or inconsequential command pair was found */
143
    return 1;
134,545✔
144
}
134,558✔
145

146
static int DetectFlowbitValidate(Signature *s, DetectFlowbitsData *fd)
147
{
55,804✔
148
    struct DetectFlowbitInvalidCmdMap_ {
55,804✔
149
        uint8_t cmd1;
55,804✔
150
        uint8_t cmd2;
55,804✔
151
        bool err; /* Error out if rule is unsatisfiable at runtime */
55,804✔
152
    };
55,804✔
153

154
    struct DetectFlowbitInvalidCmdMap_ icmds_map[] = {
55,804✔
155
        /* POSTMATCH, MATCH combinations */
156
        { DETECT_FLOWBITS_CMD_UNSET, DETECT_FLOWBITS_CMD_ISNOTSET, false },
55,804✔
157
        { DETECT_FLOWBITS_CMD_SET, DETECT_FLOWBITS_CMD_ISSET, false },
55,804✔
158
        /* POSTMATCH, POSTMATCH combinations */
159
        { DETECT_FLOWBITS_CMD_SET, DETECT_FLOWBITS_CMD_TOGGLE, false },
55,804✔
160
        { DETECT_FLOWBITS_CMD_UNSET, DETECT_FLOWBITS_CMD_TOGGLE, false },
55,804✔
161
        { DETECT_FLOWBITS_CMD_SET, DETECT_FLOWBITS_CMD_UNSET, false },
55,804✔
162
        /* MATCH, MATCH combinations */
163
        { DETECT_FLOWBITS_CMD_ISSET, DETECT_FLOWBITS_CMD_ISNOTSET, true },
55,804✔
164
    };
55,804✔
165

166
    int ret = 0;
55,804✔
167

168
    for (uint8_t i = 0; i < ARRAY_SIZE(icmds_map); i++) {
390,615✔
169
        if (fd->cmd == icmds_map[i].cmd1) {
334,824✔
170
            ret = DetectFlowbitValidateDo(
79,829✔
171
                    s, icmds_map[i].cmd2, icmds_map[i].cmd1, fd->idx, icmds_map[i].err);
79,829✔
172
            if (ret != 1) {
79,829✔
173
                return ret;
13✔
174
            }
13✔
175
        } else if (fd->cmd == icmds_map[i].cmd2) {
254,995✔
176
            ret = DetectFlowbitValidateDo(
54,729✔
177
                    s, icmds_map[i].cmd1, icmds_map[i].cmd2, fd->idx, icmds_map[i].err);
54,729✔
178
            if (ret != 1) {
54,729✔
UNCOV
179
                return ret;
×
UNCOV
180
            }
×
181
        }
54,729✔
182
    }
334,824✔
183

184
    return 0;
55,791✔
185
}
55,804✔
186

187
static int FlowbitOrAddData(DetectEngineCtx *de_ctx, DetectFlowbitsData *cd, char *arrptr)
188
{
14,246✔
189
    char *strarr[MAX_TOKENS];
14,246✔
190
    char *token;
14,246✔
191
    char *saveptr = NULL;
14,246✔
192
    uint8_t i = 0;
14,246✔
193

194
    while ((token = strtok_r(arrptr, "|", &saveptr))) {
51,649✔
195
        // Check for leading/trailing spaces in the token
196
        while(isspace((unsigned char)*token))
37,734✔
197
            token++;
2,488✔
198
        if (*token == 0)
37,734✔
199
            goto next;
1,767✔
200
        char *end = token + strlen(token) - 1;
35,967✔
201
        while(end > token && isspace((unsigned char)*end))
36,984✔
202
            *(end--) = '\0';
1,017✔
203

204
        // Check for spaces in between the flowbit names
205
        if (strchr(token, ' ') != NULL) {
35,967✔
206
            SCLogError("Spaces are not allowed in flowbit names.");
330✔
207
            return -1;
330✔
208
        }
330✔
209

210
        if (i == MAX_TOKENS) {
35,637✔
211
            SCLogError("Number of flowbits exceeds "
1✔
212
                       "maximum allowed: %d.",
1✔
213
                    MAX_TOKENS);
1✔
214
            return -1;
1✔
215
        }
1✔
216
        strarr[i++] = token;
35,636✔
217
    next:
37,403✔
218
        arrptr = NULL;
37,403✔
219
    }
37,403✔
220
    if (i == 0) {
13,915✔
221
        SCLogError("No valid flowbits specified");
2✔
222
        return -1;
2✔
223
    }
2✔
224

225
    cd->or_list_size = i;
13,913✔
226
    cd->or_list = SCCalloc(cd->or_list_size, sizeof(uint32_t));
13,913✔
227
    if (unlikely(cd->or_list == NULL))
13,913✔
228
        return -1;
×
229
    for (uint8_t j = 0; j < cd->or_list_size ; j++) {
49,187✔
230
        uint32_t varname_id = VarNameStoreRegister(strarr[j], VAR_TYPE_FLOW_BIT);
35,274✔
231
        if (unlikely(varname_id == 0))
35,274✔
232
            return -1;
×
233
        cd->or_list[j] = varname_id;
35,274✔
234
        de_ctx->max_fb_id = MAX(cd->or_list[j], de_ctx->max_fb_id);
35,274✔
235
    }
35,274✔
236

237
    return 1;
13,913✔
238
}
13,913✔
239

240
static int DetectFlowbitMatchToggle (Packet *p, const DetectFlowbitsData *fd)
241
{
3,292✔
242
    if (p->flow == NULL)
3,292✔
243
        return -1;
27✔
244

245
    return FlowBitToggle(p->flow, fd->idx);
3,265✔
246
}
3,292✔
247

248
static int DetectFlowbitMatchUnset (Packet *p, const DetectFlowbitsData *fd)
249
{
4,416✔
250
    if (p->flow == NULL)
4,416✔
251
        return 0;
293✔
252

253
    FlowBitUnset(p->flow,fd->idx);
4,123✔
254

255
    return 1;
4,123✔
256
}
4,416✔
257

258
static int DetectFlowbitMatchSet (Packet *p, const DetectFlowbitsData *fd)
259
{
4,184✔
260
    if (p->flow == NULL)
4,184✔
261
        return -1;
834✔
262

263
    int r = FlowBitSet(p->flow, fd->idx);
3,350✔
264
    SCLogDebug("set %u", fd->idx);
3,350✔
265
    return r;
3,350✔
266
}
4,184✔
267

268
static int DetectFlowbitMatchIsset (Packet *p, const DetectFlowbitsData *fd)
269
{
9,343✔
270
    if (p->flow == NULL)
9,343✔
271
        return 0;
×
272
    if (fd->or_list_size > 0) {
9,343✔
273
        for (uint8_t i = 0; i < fd->or_list_size; i++) {
17,203✔
274
            if (FlowBitIsset(p->flow, fd->or_list[i]) == 1)
12,655✔
275
                return 1;
2,978✔
276
        }
12,655✔
277
        return 0;
4,548✔
278
    }
7,526✔
279

280
    return FlowBitIsset(p->flow,fd->idx);
1,817✔
281
}
9,343✔
282

283
static int DetectFlowbitMatchIsnotset (Packet *p, const DetectFlowbitsData *fd)
284
{
11,385✔
285
    if (p->flow == NULL)
11,385✔
286
        return 0;
×
287
    if (fd->or_list_size > 0) {
11,385✔
288
        for (uint8_t i = 0; i < fd->or_list_size; i++) {
10,156✔
289
            if (FlowBitIsnotset(p->flow, fd->or_list[i]) == 1)
9,886✔
290
                return 1;
7,875✔
291
        }
9,886✔
292
        return 0;
270✔
293
    }
8,145✔
294
    return FlowBitIsnotset(p->flow,fd->idx);
3,240✔
295
}
11,385✔
296

297
/*
298
 * returns 0: no match (or error)
299
 *         1: match
300
 */
301

302
int DetectFlowbitMatch (DetectEngineThreadCtx *det_ctx, Packet *p,
303
        const Signature *s, const SigMatchCtx *ctx)
304
{
32,620✔
305
    const DetectFlowbitsData *fd = (const DetectFlowbitsData *)ctx;
32,620✔
306
    if (fd == NULL)
32,620✔
307
        return 0;
×
308

309
    switch (fd->cmd) {
32,620✔
310
        case DETECT_FLOWBITS_CMD_ISSET:
9,343✔
311
            return DetectFlowbitMatchIsset(p,fd);
9,343✔
312
        case DETECT_FLOWBITS_CMD_ISNOTSET:
11,385✔
313
            return DetectFlowbitMatchIsnotset(p,fd);
11,385✔
314
        case DETECT_FLOWBITS_CMD_SET: {
4,184✔
315
            int r = DetectFlowbitMatchSet(p, fd);
4,184✔
316
            /* only on a new "set" invoke the prefilter */
317
            if (r == 1 && fd->post_rule_match_prefilter) {
4,184✔
318
                SCLogDebug("flowbit set, appending to work queue");
7✔
319
                PostRuleMatchWorkQueueAppend(det_ctx, s, DETECT_FLOWBITS, fd->idx);
7✔
320
            }
7✔
321
            return (r != -1);
4,184✔
322
        }
×
323
        case DETECT_FLOWBITS_CMD_UNSET:
4,416✔
324
            return DetectFlowbitMatchUnset(p,fd);
4,416✔
325
        case DETECT_FLOWBITS_CMD_TOGGLE: {
3,292✔
326
            int r = DetectFlowbitMatchToggle(p, fd);
3,292✔
327
            if (r == 1 && fd->post_rule_match_prefilter) {
3,292✔
328
                SCLogDebug("flowbit set (by toggle), appending to work queue");
67✔
329
                PostRuleMatchWorkQueueAppend(det_ctx, s, DETECT_FLOWBITS, fd->idx);
67✔
330
            }
67✔
331
            return (r != -1);
3,292✔
332
        }
×
333
        default:
×
334
            SCLogError("unknown cmd %" PRIu32 "", fd->cmd);
×
335
            return 0;
×
336
    }
32,620✔
337

338
    return 0;
×
339
}
32,620✔
340

341
static int DetectFlowbitParse(const char *str, char *cmd, int cmd_len, char *name,
342
    int name_len)
343
{
70,279✔
344
    int rc;
70,279✔
345
    size_t pcre2len;
70,279✔
346
    pcre2_match_data *match = NULL;
70,279✔
347

348
    int count = DetectParsePcreExec(&parse_regex, &match, str, 0, 0);
70,279✔
349
    if (count != 2 && count != 3) {
70,279✔
350
        SCLogError("\"%s\" is not a valid setting for flowbits.", str);
158✔
351
        goto error;
158✔
352
    }
158✔
353

354
    pcre2len = cmd_len;
70,121✔
355
    rc = pcre2_substring_copy_bynumber(match, 1, (PCRE2_UCHAR8 *)cmd, &pcre2len);
70,121✔
356
    if (rc < 0) {
70,121✔
357
        SCLogError("pcre2_substring_copy_bynumber failed");
53✔
358
        goto error;
53✔
359
    }
53✔
360

361
    if (count == 3) {
70,068✔
362
        pcre2len = name_len;
58,683✔
363
        rc = pcre2_substring_copy_bynumber(match, 2, (PCRE2_UCHAR8 *)name, &pcre2len);
58,683✔
364
        if (rc < 0) {
58,683✔
365
            SCLogError("pcre2_substring_copy_bynumber failed");
131✔
366
            goto error;
131✔
367
        }
131✔
368

369
        /* Trim trailing whitespace. */
370
        while (strlen(name) > 0 && isblank(name[strlen(name) - 1])) {
58,829✔
371
            name[strlen(name) - 1] = '\0';
277✔
372
        }
277✔
373

374
        if (strchr(name, '|') == NULL) {
58,552✔
375
            /* Validate name, spaces are not allowed. */
376
            for (size_t i = 0; i < strlen(name); i++) {
671,216✔
377
                if (isblank(name[i])) {
629,078✔
378
                    SCLogError("spaces not allowed in flowbit names");
2,145✔
379
                    goto error;
2,145✔
380
                }
2,145✔
381
            }
629,078✔
382
        }
44,283✔
383
    }
58,552✔
384

385
    pcre2_match_data_free(match);
70,068✔
386
    return 1;
67,792✔
387

388
error:
2,487✔
389
    if (match) {
2,487✔
390
        pcre2_match_data_free(match);
2,487✔
391
    }
2,487✔
392
    return 0;
2,487✔
393
}
70,068✔
394

395
int DetectFlowbitSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
396
{
70,279✔
397
    DetectFlowbitsData *cd = NULL;
70,279✔
398
    uint8_t fb_cmd = 0;
70,279✔
399
    char fb_cmd_str[16] = "", fb_name[256] = "";
70,279✔
400

401
    if (!DetectFlowbitParse(rawstr, fb_cmd_str, sizeof(fb_cmd_str), fb_name,
70,279✔
402
            sizeof(fb_name))) {
70,279✔
403
        return -1;
2,487✔
404
    }
2,487✔
405

406
    if (strcmp(fb_cmd_str,"noalert") == 0) {
67,792✔
407
        if (strlen(fb_name) != 0)
6,686✔
408
            goto error;
1✔
409
        s->action &= ~ACTION_ALERT;
6,685✔
410
        return 0;
6,685✔
411
    } else if (strcmp(fb_cmd_str,"isset") == 0) {
61,106✔
412
        fb_cmd = DETECT_FLOWBITS_CMD_ISSET;
20,588✔
413
    } else if (strcmp(fb_cmd_str,"isnotset") == 0) {
40,518✔
414
        fb_cmd = DETECT_FLOWBITS_CMD_ISNOTSET;
9,307✔
415
    } else if (strcmp(fb_cmd_str,"set") == 0) {
31,211✔
416
        fb_cmd = DETECT_FLOWBITS_CMD_SET;
14,259✔
417
    } else if (strcmp(fb_cmd_str,"unset") == 0) {
16,952✔
418
        fb_cmd = DETECT_FLOWBITS_CMD_UNSET;
8,944✔
419
    } else if (strcmp(fb_cmd_str,"toggle") == 0) {
8,944✔
420
        fb_cmd = DETECT_FLOWBITS_CMD_TOGGLE;
3,677✔
421
    } else {
4,331✔
422
        SCLogError("ERROR: flowbits action \"%s\" is not supported.", fb_cmd_str);
4,331✔
423
        goto error;
4,331✔
424
    }
4,331✔
425

426
    switch (fb_cmd) {
56,775✔
427
        case DETECT_FLOWBITS_CMD_ISNOTSET:
9,307✔
428
        case DETECT_FLOWBITS_CMD_ISSET:
29,895✔
429
        case DETECT_FLOWBITS_CMD_SET:
44,154✔
430
        case DETECT_FLOWBITS_CMD_UNSET:
53,098✔
431
        case DETECT_FLOWBITS_CMD_TOGGLE:
56,775✔
432
        default:
56,775✔
433
            if (strlen(fb_name) == 0)
56,775✔
434
                goto error;
638✔
435
            break;
56,137✔
436
    }
56,775✔
437

438
    cd = SCCalloc(1, sizeof(DetectFlowbitsData));
56,137✔
439
    if (unlikely(cd == NULL))
56,137✔
440
        goto error;
×
441
    if (strchr(fb_name, '|') != NULL) {
56,137✔
442
        int retval = FlowbitOrAddData(de_ctx, cd, fb_name);
14,246✔
443
        if (retval == -1) {
14,246✔
444
            goto error;
333✔
445
        }
333✔
446
        cd->cmd = fb_cmd;
13,913✔
447
    } else {
41,891✔
448
        uint32_t varname_id = VarNameStoreRegister(fb_name, VAR_TYPE_FLOW_BIT);
41,891✔
449
        if (unlikely(varname_id == 0))
41,891✔
450
            goto error;
×
451
        cd->idx = varname_id;
41,891✔
452
        de_ctx->max_fb_id = MAX(cd->idx, de_ctx->max_fb_id);
41,891✔
453
        cd->cmd = fb_cmd;
41,891✔
454
        cd->or_list_size = 0;
41,891✔
455
        cd->or_list = NULL;
41,891✔
456
        SCLogDebug("idx %" PRIu32 ", cmd %s, name %s",
41,891✔
457
            cd->idx, fb_cmd_str, strlen(fb_name) ? fb_name : "(none)");
41,891✔
458
    }
41,891✔
459

460
    if (DetectFlowbitValidate(s, cd) != 0) {
55,804✔
461
        goto error;
13✔
462
    }
13✔
463

464
    /* Okay so far so good, lets get this into a SigMatch
465
     * and put it in the Signature. */
466

467
    switch (fb_cmd) {
55,791✔
468
        /* noalert can't happen here */
469
        case DETECT_FLOWBITS_CMD_ISNOTSET:
9,255✔
470
        case DETECT_FLOWBITS_CMD_ISSET:
29,164✔
471
            /* checks, so packet list */
472
            if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_FLOWBITS, (SigMatchCtx *)cd,
29,164✔
473
                        DETECT_SM_LIST_MATCH) == NULL) {
29,164✔
474
                goto error;
×
475
            }
×
476
            break;
29,164✔
477

478
        case DETECT_FLOWBITS_CMD_SET:
29,164✔
479
        case DETECT_FLOWBITS_CMD_UNSET:
22,950✔
480
        case DETECT_FLOWBITS_CMD_TOGGLE:
26,627✔
481
            /* modifiers, only run when entire sig has matched */
482
            if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_FLOWBITS, (SigMatchCtx *)cd,
26,627✔
483
                        DETECT_SM_LIST_POSTMATCH) == NULL) {
26,627✔
484
                goto error;
×
485
            }
×
486
            break;
26,627✔
487

488
        // suppress coverity warning as scan-build-7 warns w/o this.
489
        // coverity[deadcode : FALSE]
490
        default:
26,627✔
491
            goto error;
×
492
    }
55,791✔
493

494
    return 0;
55,791✔
495

496
error:
5,316✔
497
    if (cd != NULL)
5,316✔
498
        DetectFlowbitFree(de_ctx, cd);
346✔
499
    return -1;
5,316✔
500
}
55,791✔
501

502
void DetectFlowbitFree (DetectEngineCtx *de_ctx, void *ptr)
503
{
56,137✔
504
    DetectFlowbitsData *fd = (DetectFlowbitsData *)ptr;
56,137✔
505
    if (fd == NULL)
56,137✔
506
        return;
×
507
    VarNameStoreUnregister(fd->idx, VAR_TYPE_FLOW_BIT);
56,137✔
508
    if (fd->or_list != NULL) {
56,137✔
509
        for (uint8_t i = 0; i < fd->or_list_size; i++) {
49,187✔
510
            VarNameStoreUnregister(fd->or_list[i], VAR_TYPE_FLOW_BIT);
35,274✔
511
        }
35,274✔
512
        SCFree(fd->or_list);
13,913✔
513
    }
13,913✔
514
    SCFree(fd);
56,137✔
515
}
56,137✔
516

517
struct FBAnalyzer {
518
    struct FBAnalyze *array;
519
    uint32_t array_size;
520
};
521

522
struct FBAnalyze {
523
    uint16_t cnts[DETECT_FLOWBITS_CMD_MAX];
524
    uint16_t state_cnts[DETECT_FLOWBITS_CMD_MAX];
525

526
    uint32_t *set_sids;
527
    uint32_t set_sids_idx;
528
    uint32_t set_sids_size;
529

530
    uint32_t *isset_sids;
531
    uint32_t isset_sids_idx;
532
    uint32_t isset_sids_size;
533

534
    uint32_t *isnotset_sids;
535
    uint32_t isnotset_sids_idx;
536
    uint32_t isnotset_sids_size;
537

538
    uint32_t *unset_sids;
539
    uint32_t unset_sids_idx;
540
    uint32_t unset_sids_size;
541

542
    uint32_t *toggle_sids;
543
    uint32_t toggle_sids_idx;
544
    uint32_t toggle_sids_size;
545
};
546

547
extern bool rule_engine_analysis_set;
548
static void DetectFlowbitsAnalyzeDump(const DetectEngineCtx *de_ctx,
549
        struct FBAnalyze *array, uint32_t elements);
550

551
static void FBAnalyzerArrayFree(struct FBAnalyze *array, const uint32_t array_size)
552
{
2,845✔
553
    if (array) {
2,845✔
554
        for (uint32_t i = 0; i < array_size; i++) {
449,602✔
555
            SCFree(array[i].set_sids);
446,757✔
556
            SCFree(array[i].unset_sids);
446,757✔
557
            SCFree(array[i].isset_sids);
446,757✔
558
            SCFree(array[i].isnotset_sids);
446,757✔
559
            SCFree(array[i].toggle_sids);
446,757✔
560
        }
446,757✔
561
        SCFree(array);
2,845✔
562
    }
2,845✔
563
}
2,845✔
564

565
static void FBAnalyzerFree(struct FBAnalyzer *fba)
566
{
2,845✔
567
    if (fba && fba->array) {
2,845✔
568
        FBAnalyzerArrayFree(fba->array, fba->array_size);
2,845✔
569
        fba->array = NULL;
2,845✔
570
        fba->array_size = 0;
2,845✔
571
    }
2,845✔
572
}
2,845✔
573

574
#define MAX_SIDS 8
575
static bool CheckExpand(const uint32_t sids_idx, uint32_t **sids, uint32_t *sids_size)
576
{
7,115✔
577
    if (sids_idx >= *sids_size) {
7,115✔
578
        const uint32_t old_size = *sids_size;
5,855✔
579
        const uint32_t new_size = MAX(2 * old_size, MAX_SIDS);
5,855✔
580

581
        void *ptr = SCRealloc(*sids, new_size * sizeof(uint32_t));
5,855✔
582
        if (ptr == NULL)
5,855✔
583
            return false;
×
584
        *sids_size = new_size;
5,855✔
585
        *sids = ptr;
5,855✔
586
    }
5,855✔
587
    return true;
7,115✔
588
}
7,115✔
589

590
static int DetectFlowbitsAnalyzeSignature(const Signature *s, struct FBAnalyzer *fba)
591
{
43,965✔
592
    struct FBAnalyze *array = fba->array;
43,965✔
593
    if (array == NULL)
43,965✔
594
        return -1;
×
595

596
    /* see if the signature uses stateful matching TODO is there not a flag? */
597
    bool has_state = (s->init_data->buffer_index != 0);
43,965✔
598

599
    for (const SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; sm != NULL;
72,226✔
600
            sm = sm->next) {
43,965✔
601
        if (sm->type != DETECT_FLOWBITS)
28,261✔
602
            continue;
25,272✔
603
        /* figure out the flowbit action */
604
        const DetectFlowbitsData *fb = (DetectFlowbitsData *)sm->ctx;
2,989✔
605
        // Handle flowbit array in case of ORed flowbits
606
        for (uint8_t k = 0; k < fb->or_list_size; k++) {
4,841✔
607
            struct FBAnalyze *fa = &array[fb->or_list[k]];
1,852✔
608
            fa->cnts[fb->cmd]++;
1,852✔
609
            fa->state_cnts[fb->cmd] += has_state;
1,852✔
610

611
            if (fb->cmd == DETECT_FLOWBITS_CMD_ISSET) {
1,852✔
612
                if (!CheckExpand(fa->isset_sids_idx, &fa->isset_sids, &fa->isset_sids_size))
1,187✔
613
                    return -1;
×
614
                fa->isset_sids[fa->isset_sids_idx] = s->iid;
1,187✔
615
                fa->isset_sids_idx++;
1,187✔
616
            } else if (fb->cmd == DETECT_FLOWBITS_CMD_ISNOTSET) {
1,187✔
617
                if (!CheckExpand(
665✔
618
                            fa->isnotset_sids_idx, &fa->isnotset_sids, &fa->isnotset_sids_size))
665✔
619
                    return -1;
×
620
                fa->isnotset_sids[fa->isnotset_sids_idx] = s->iid;
665✔
621
                fa->isnotset_sids_idx++;
665✔
622
            }
665✔
623
        }
1,852✔
624
        if (fb->or_list_size == 0) {
2,989✔
625
            struct FBAnalyze *fa = &array[fb->idx];
2,083✔
626
            fa->cnts[fb->cmd]++;
2,083✔
627
            fa->state_cnts[fb->cmd] += has_state;
2,083✔
628

629
            if (fb->cmd == DETECT_FLOWBITS_CMD_ISSET) {
2,083✔
630
                if (!CheckExpand(fa->isset_sids_idx, &fa->isset_sids, &fa->isset_sids_size))
1,701✔
631
                    return -1;
×
632
                fa->isset_sids[fa->isset_sids_idx] = s->iid;
1,701✔
633
                fa->isset_sids_idx++;
1,701✔
634
            } else if (fb->cmd == DETECT_FLOWBITS_CMD_ISNOTSET) {
1,701✔
635
                if (!CheckExpand(
382✔
636
                            fa->isnotset_sids_idx, &fa->isnotset_sids, &fa->isnotset_sids_size))
382✔
637
                    return -1;
×
638
                fa->isnotset_sids[fa->isnotset_sids_idx] = s->iid;
382✔
639
                fa->isnotset_sids_idx++;
382✔
640
            }
382✔
641
        }
2,083✔
642
    }
2,989✔
643
    for (const SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_POSTMATCH]; sm != NULL;
52,886✔
644
            sm = sm->next) {
43,965✔
645
        if (sm->type != DETECT_FLOWBITS)
8,921✔
646
            continue;
5,741✔
647
        /* figure out what flowbit action */
648
        const DetectFlowbitsData *fb = (DetectFlowbitsData *)sm->ctx;
3,180✔
649
        struct FBAnalyze *fa = &array[fb->idx];
3,180✔
650
        fa->cnts[fb->cmd]++;
3,180✔
651
        fa->state_cnts[fb->cmd] += has_state;
3,180✔
652

653
        if (fb->cmd == DETECT_FLOWBITS_CMD_SET) {
3,180✔
654
            if (!CheckExpand(fa->set_sids_idx, &fa->set_sids, &fa->set_sids_size))
2,160✔
655
                return -1;
×
656
            fa->set_sids[fa->set_sids_idx] = s->iid;
2,160✔
657
            fa->set_sids_idx++;
2,160✔
658
        } else if (fb->cmd == DETECT_FLOWBITS_CMD_UNSET) {
2,160✔
659
            if (!CheckExpand(fa->unset_sids_idx, &fa->unset_sids, &fa->unset_sids_size))
448✔
660
                return -1;
×
661
            fa->unset_sids[fa->unset_sids_idx] = s->iid;
448✔
662
            fa->unset_sids_idx++;
448✔
663
        } else if (fb->cmd == DETECT_FLOWBITS_CMD_TOGGLE) {
572✔
664
            if (!CheckExpand(fa->toggle_sids_idx, &fa->toggle_sids, &fa->toggle_sids_size))
572✔
665
                return -1;
×
666
            fa->toggle_sids[fa->toggle_sids_idx] = s->iid;
572✔
667
            fa->toggle_sids_idx++;
572✔
668
        }
572✔
669
    }
3,180✔
670
    return 0;
43,965✔
671
}
43,965✔
672

673
int DetectFlowbitsAnalyze(DetectEngineCtx *de_ctx)
674
{
35,438✔
675
    const uint32_t max_fb_id = de_ctx->max_fb_id;
35,438✔
676
    if (max_fb_id == 0)
35,438✔
677
        return 0;
32,991✔
678

679
    struct FBAnalyzer fba = { .array = NULL, .array_size = 0 };
2,447✔
680
    const uint32_t array_size = max_fb_id + 1;
2,447✔
681
    struct FBAnalyze *array = SCCalloc(array_size, sizeof(struct FBAnalyze));
2,447✔
682
    if (array == NULL) {
2,447✔
683
        SCLogError("Unable to allocate flowbit analyze array");
×
684
        return -1;
×
685
    }
×
686
    fba.array = array;
2,447✔
687
    fba.array_size = array_size;
2,447✔
688

689
    SCLogDebug("fb analyzer array size: %"PRIu64,
2,447✔
690
            (uint64_t)(array_size * sizeof(struct FBAnalyze)));
2,447✔
691

692
    /* fill flowbit array, updating counters per sig */
693
    for (uint32_t i = 0; i < de_ctx->sig_array_len; i++) {
38,203✔
694
        const Signature *s = de_ctx->sig_array[i];
35,756✔
695

696
        int r = DetectFlowbitsAnalyzeSignature(s, &fba);
35,756✔
697
        if (r < 0) {
35,756✔
698
            FBAnalyzerFree(&fba);
×
699
            return -1;
×
700
        }
×
701
    }
35,756✔
702

703
    /* walk array to see if all bits make sense */
704
    for (uint32_t i = 0; i < array_size; i++) {
366,058✔
705
        const char *varname = VarNameStoreSetupLookup(i, VAR_TYPE_FLOW_BIT);
363,611✔
706
        if (varname == NULL)
363,611✔
707
            continue;
111,954✔
708

709
        bool to_state = false;
363,611✔
710

711
        if (array[i].cnts[DETECT_FLOWBITS_CMD_ISSET] &&
251,657✔
712
            array[i].cnts[DETECT_FLOWBITS_CMD_TOGGLE] == 0 &&
251,657✔
713
            array[i].cnts[DETECT_FLOWBITS_CMD_SET] == 0) {
251,657✔
714

715
            const Signature *s = de_ctx->sig_array[array[i].isset_sids[0]];
1,334✔
716
            SCLogWarning("flowbit '%s' is checked but not "
1,334✔
717
                         "set. Checked in %u and %u other sigs",
1,334✔
718
                    varname, s->id, array[i].isset_sids_idx - 1);
1,334✔
719
        }
1,334✔
720
        if (array[i].state_cnts[DETECT_FLOWBITS_CMD_ISSET] &&
251,657✔
721
            array[i].state_cnts[DETECT_FLOWBITS_CMD_SET] == 0)
251,657✔
722
        {
245✔
723
            SCLogDebug("flowbit %s/%u: isset in state, set not in state", varname, i);
245✔
724
        }
245✔
725

726
        /* if signature depends on 'stateful' flowbits, then turn the
727
         * sig into a stateful sig itself */
728
        if (array[i].cnts[DETECT_FLOWBITS_CMD_ISSET] > 0 &&
251,657✔
729
            array[i].state_cnts[DETECT_FLOWBITS_CMD_ISSET] == 0 &&
251,657✔
730
            array[i].state_cnts[DETECT_FLOWBITS_CMD_SET])
251,657✔
731
        {
4✔
732
            SCLogDebug("flowbit %s/%u: isset not in state, set in state", varname, i);
4✔
733
            to_state = true;
4✔
734
        }
4✔
735

736
        SCLogDebug("ALL flowbit %s/%u: sets %u toggles %u unsets %u isnotsets %u issets %u", varname, i,
251,657✔
737
                array[i].cnts[DETECT_FLOWBITS_CMD_SET], array[i].cnts[DETECT_FLOWBITS_CMD_TOGGLE],
251,657✔
738
                array[i].cnts[DETECT_FLOWBITS_CMD_UNSET], array[i].cnts[DETECT_FLOWBITS_CMD_ISNOTSET],
251,657✔
739
                array[i].cnts[DETECT_FLOWBITS_CMD_ISSET]);
251,657✔
740
        SCLogDebug("STATE flowbit %s/%u: sets %u toggles %u unsets %u isnotsets %u issets %u", varname, i,
251,657✔
741
                array[i].state_cnts[DETECT_FLOWBITS_CMD_SET], array[i].state_cnts[DETECT_FLOWBITS_CMD_TOGGLE],
251,657✔
742
                array[i].state_cnts[DETECT_FLOWBITS_CMD_UNSET], array[i].state_cnts[DETECT_FLOWBITS_CMD_ISNOTSET],
251,657✔
743
                array[i].state_cnts[DETECT_FLOWBITS_CMD_ISSET]);
251,657✔
744
        for (uint32_t x = 0; x < array[i].set_sids_idx; x++) {
253,336✔
745
            SCLogDebug("SET flowbit %s/%u: SID %u", varname, i,
1,679✔
746
                    de_ctx->sig_array[array[i].set_sids[x]]->id);
1,679✔
747
        }
1,679✔
748
        if (to_state) {
251,657✔
749
            for (uint32_t x = 0; x < array[i].isset_sids_idx; x++) {
9✔
750
                Signature *s = de_ctx->sig_array[array[i].isset_sids[x]];
5✔
751
                SCLogDebug("GET flowbit %s/%u: SID %u", varname, i, s->id);
5✔
752

753
                s->init_data->init_flags |= SIG_FLAG_INIT_STATE_MATCH;
5✔
754
                s->init_data->is_rule_state_dependant = true;
5✔
755

756
                uint32_t sids_array_size = array[i].set_sids_idx;
5✔
757

758
                // save information about flowbits that affect this rule's state
759
                if (s->init_data->rule_state_dependant_sids_array == NULL) {
5✔
760
                    s->init_data->rule_state_dependant_sids_array =
5✔
761
                            SCCalloc(sids_array_size, sizeof(uint32_t));
5✔
762
                    if (s->init_data->rule_state_dependant_sids_array == NULL) {
5✔
763
                        SCLogError("Failed to allocate memory for rule_state_dependant_ids");
×
764
                        goto error;
×
765
                    }
×
766
                    s->init_data->rule_state_flowbits_ids_size = 1;
5✔
767
                    s->init_data->rule_state_flowbits_ids_array =
5✔
768
                            SCCalloc(s->init_data->rule_state_flowbits_ids_size, sizeof(uint32_t));
5✔
769
                    if (s->init_data->rule_state_flowbits_ids_array == NULL) {
5✔
770
                        SCLogError("Failed to allocate memory for rule_state_variable_idx");
×
771
                        goto error;
×
772
                    }
×
773
                    s->init_data->rule_state_dependant_sids_size = sids_array_size;
5✔
774
                    SCLogDebug("alloc'ed array for rule dependency and fbs idx array, sid %u, "
5✔
775
                               "sizes are %u and %u",
5✔
776
                            s->id, s->init_data->rule_state_dependant_sids_size,
5✔
777
                            s->init_data->rule_state_flowbits_ids_size);
5✔
778
                } else {
5✔
UNCOV
779
                    uint32_t new_array_size =
×
UNCOV
780
                            s->init_data->rule_state_dependant_sids_size + sids_array_size;
×
UNCOV
781
                    void *tmp_ptr = SCRealloc(s->init_data->rule_state_dependant_sids_array,
×
UNCOV
782
                            new_array_size * sizeof(uint32_t));
×
UNCOV
783
                    if (tmp_ptr == NULL) {
×
784
                        SCLogError("Failed to allocate memory for rule_state_variable_idx");
×
785
                        goto error;
×
786
                    }
×
UNCOV
787
                    s->init_data->rule_state_dependant_sids_array = tmp_ptr;
×
UNCOV
788
                    s->init_data->rule_state_dependant_sids_size = new_array_size;
×
UNCOV
789
                    SCLogDebug("realloc'ed array for rule dependency, sid %u, new size is %u",
×
UNCOV
790
                            s->id, s->init_data->rule_state_dependant_sids_size);
×
UNCOV
791
                    uint32_t new_fb_array_size = s->init_data->rule_state_flowbits_ids_size + 1;
×
UNCOV
792
                    void *tmp_fb_ptr = SCRealloc(s->init_data->rule_state_flowbits_ids_array,
×
UNCOV
793
                            new_fb_array_size * sizeof(uint32_t));
×
UNCOV
794
                    s->init_data->rule_state_flowbits_ids_array = tmp_fb_ptr;
×
UNCOV
795
                    if (s->init_data->rule_state_flowbits_ids_array == NULL) {
×
796
                        SCLogError("Failed to reallocate memory for rule_state_variable_idx");
×
797
                        goto error;
×
798
                    }
×
UNCOV
799
                    SCLogDebug(
×
UNCOV
800
                            "realloc'ed array for flowbits ids, new size is %u", new_fb_array_size);
×
UNCOV
801
                    s->init_data->rule_state_dependant_sids_size = new_array_size;
×
UNCOV
802
                    s->init_data->rule_state_flowbits_ids_size = new_fb_array_size;
×
UNCOV
803
                }
×
804
                for (uint32_t idx = 0; idx < s->init_data->rule_state_dependant_sids_size; idx++) {
10✔
805
                    if (idx < array[i].set_sids_idx) {
5✔
806
                        s->init_data->rule_state_dependant_sids_array
5✔
807
                                [s->init_data->rule_state_dependant_sids_idx] =
5✔
808
                                de_ctx->sig_array[array[i].set_sids[idx]]->id;
5✔
809
                        s->init_data->rule_state_dependant_sids_idx++;
5✔
810
                    }
5✔
811
                }
5✔
812
                s->init_data
5✔
813
                        ->rule_state_flowbits_ids_array[s->init_data->rule_state_flowbits_ids_size -
5✔
814
                                                        1] = i;
5✔
815
                s->init_data->rule_state_flowbits_ids_size += 1;
5✔
816
                // flowbit info saving for rule made stateful rule work finished
817

818
                SCLogDebug("made SID %u stateful because it depends on "
5✔
819
                        "stateful rules that set flowbit %s", s->id, varname);
5✔
820
            }
5✔
821
        }
4✔
822
    }
251,657✔
823

824
    if (rule_engine_analysis_set) {
2,447✔
UNCOV
825
        DetectFlowbitsAnalyzeDump(de_ctx, array, array_size);
×
UNCOV
826
    }
×
827

828
    FBAnalyzerFree(&fba);
2,447✔
829
    return 0;
2,447✔
830
error:
×
831
    FBAnalyzerFree(&fba);
×
832
    return -1;
×
833
}
2,447✔
834

835
// TODO misses IPOnly rules. IPOnly flowbit rules are set only though.
836
static struct FBAnalyzer DetectFlowbitsAnalyzeForGroup(
837
        const DetectEngineCtx *de_ctx, SigGroupHead *sgh)
838
{
398✔
839
    struct FBAnalyzer fba = { .array = NULL, .array_size = 0 };
398✔
840

841
    const uint32_t max_fb_id = de_ctx->max_fb_id;
398✔
842
    if (max_fb_id == 0)
398✔
UNCOV
843
        return fba;
×
844

845
    uint32_t array_size = max_fb_id + 1;
398✔
846
    struct FBAnalyze *array = SCCalloc(array_size, sizeof(struct FBAnalyze));
398✔
847
    if (array == NULL) {
398✔
848
        SCLogError("Unable to allocate flowbit analyze array");
×
849
        return fba;
×
850
    }
×
851
    SCLogDebug(
398✔
852
            "fb analyzer array size: %" PRIu64, (uint64_t)(array_size * sizeof(struct FBAnalyze)));
398✔
853
    fba.array = array;
398✔
854
    fba.array_size = array_size;
398✔
855

856
    /* fill flowbit array, updating counters per sig */
857
    for (uint32_t i = 0; i < sgh->init->sig_cnt; i++) {
8,607✔
858
        const Signature *s = sgh->init->match_array[i];
8,209✔
859
        SCLogDebug("sgh %p: s->id %u", sgh, s->id);
8,209✔
860

861
        int r = DetectFlowbitsAnalyzeSignature(s, &fba);
8,209✔
862
        if (r < 0) {
8,209✔
863
            FBAnalyzerFree(&fba);
×
864
            return fba;
×
865
        }
×
866
    }
8,209✔
867

868
    /* walk array to see if all bits make sense */
869
    for (uint32_t i = 0; i < array_size; i++) {
83,544✔
870
        const char *varname = VarNameStoreSetupLookup(i, VAR_TYPE_FLOW_BIT);
83,146✔
871
        if (varname == NULL)
83,146✔
872
            continue;
24,897✔
873

874
        bool to_state = false;
83,146✔
875
        if (array[i].state_cnts[DETECT_FLOWBITS_CMD_ISSET] &&
58,249✔
876
                array[i].state_cnts[DETECT_FLOWBITS_CMD_SET] == 0) {
58,249✔
877
            SCLogDebug("flowbit %s/%u: isset in state, set not in state", varname, i);
88✔
878
        }
88✔
879

880
        /* if signature depends on 'stateful' flowbits, then turn the
881
         * sig into a stateful sig itself */
882
        if (array[i].cnts[DETECT_FLOWBITS_CMD_ISSET] > 0 &&
58,249✔
883
                array[i].state_cnts[DETECT_FLOWBITS_CMD_ISSET] == 0 &&
58,249✔
884
                array[i].state_cnts[DETECT_FLOWBITS_CMD_SET]) {
58,249✔
885
            SCLogDebug("flowbit %s/%u: isset not in state, set in state", varname, i);
5✔
886
            to_state = true;
5✔
887
        }
5✔
888

889
        SCLogDebug("ALL flowbit %s/%u: sets %u toggles %u unsets %u isnotsets %u issets %u",
58,249✔
890
                varname, i, array[i].cnts[DETECT_FLOWBITS_CMD_SET],
58,249✔
891
                array[i].cnts[DETECT_FLOWBITS_CMD_TOGGLE], array[i].cnts[DETECT_FLOWBITS_CMD_UNSET],
58,249✔
892
                array[i].cnts[DETECT_FLOWBITS_CMD_ISNOTSET],
58,249✔
893
                array[i].cnts[DETECT_FLOWBITS_CMD_ISSET]);
58,249✔
894
        SCLogDebug("STATE flowbit %s/%u: sets %u toggles %u unsets %u isnotsets %u issets %u",
58,249✔
895
                varname, i, array[i].state_cnts[DETECT_FLOWBITS_CMD_SET],
58,249✔
896
                array[i].state_cnts[DETECT_FLOWBITS_CMD_TOGGLE],
58,249✔
897
                array[i].state_cnts[DETECT_FLOWBITS_CMD_UNSET],
58,249✔
898
                array[i].state_cnts[DETECT_FLOWBITS_CMD_ISNOTSET],
58,249✔
899
                array[i].state_cnts[DETECT_FLOWBITS_CMD_ISSET]);
58,249✔
900
        for (uint32_t x = 0; x < array[i].set_sids_idx; x++) {
58,611✔
901
            SCLogDebug("SET flowbit %s/%u: SID %u", varname, i,
362✔
902
                    de_ctx->sig_array[array[i].set_sids[x]]->id);
362✔
903
        }
362✔
904
        for (uint32_t x = 0; x < array[i].isset_sids_idx; x++) {
59,021✔
905
            Signature *s = de_ctx->sig_array[array[i].isset_sids[x]];
772✔
906
            SCLogDebug("GET flowbit %s/%u: SID %u", varname, i, s->id);
772✔
907

908
            if (to_state) {
772✔
909
                s->init_data->init_flags |= SIG_FLAG_INIT_STATE_MATCH;
5✔
910
                SCLogDebug("made SID %u stateful because it depends on "
5✔
911
                           "stateful rules that set flowbit %s",
5✔
912
                        s->id, varname);
5✔
913
            }
5✔
914
        }
772✔
915
    }
58,249✔
916

917
    return fba;
398✔
918
}
398✔
919

920
SCMutex g_flowbits_dump_write_m = SCMUTEX_INITIALIZER;
921
static void DetectFlowbitsAnalyzeDump(const DetectEngineCtx *de_ctx,
922
        struct FBAnalyze *array, uint32_t elements)
UNCOV
923
{
×
UNCOV
924
    SCJsonBuilder *js = SCJbNewObject();
×
UNCOV
925
    if (js == NULL)
×
926
        return;
×
927

UNCOV
928
    SCJbOpenArray(js, "flowbits");
×
UNCOV
929
    for (uint32_t x = 0; x < elements; x++) {
×
UNCOV
930
        const char *varname = VarNameStoreSetupLookup(x, VAR_TYPE_FLOW_BIT);
×
UNCOV
931
        if (varname == NULL)
×
UNCOV
932
            continue;
×
933

UNCOV
934
        const struct FBAnalyze *e = &array[x];
×
935

UNCOV
936
        SCJbStartObject(js);
×
UNCOV
937
        SCJbSetString(js, "name", varname);
×
UNCOV
938
        SCJbSetUint(js, "internal_id", x);
×
UNCOV
939
        SCJbSetUint(js, "set_cnt", e->cnts[DETECT_FLOWBITS_CMD_SET]);
×
UNCOV
940
        SCJbSetUint(js, "unset_cnt", e->cnts[DETECT_FLOWBITS_CMD_UNSET]);
×
UNCOV
941
        SCJbSetUint(js, "toggle_cnt", e->cnts[DETECT_FLOWBITS_CMD_TOGGLE]);
×
UNCOV
942
        SCJbSetUint(js, "isset_cnt", e->cnts[DETECT_FLOWBITS_CMD_ISSET]);
×
UNCOV
943
        SCJbSetUint(js, "isnotset_cnt", e->cnts[DETECT_FLOWBITS_CMD_ISNOTSET]);
×
944

945
        // sets
UNCOV
946
        if (e->cnts[DETECT_FLOWBITS_CMD_SET]) {
×
UNCOV
947
            SCJbOpenArray(js, "sets");
×
UNCOV
948
            for (uint32_t i = 0; i < e->set_sids_idx; i++) {
×
UNCOV
949
                const Signature *s = de_ctx->sig_array[e->set_sids[i]];
×
UNCOV
950
                SCJbAppendUint(js, s->id);
×
UNCOV
951
            }
×
UNCOV
952
            SCJbClose(js);
×
UNCOV
953
        }
×
954
        // gets
UNCOV
955
        if (e->cnts[DETECT_FLOWBITS_CMD_ISSET]) {
×
UNCOV
956
            SCJbOpenArray(js, "isset");
×
UNCOV
957
            for (uint32_t i = 0; i < e->isset_sids_idx; i++) {
×
UNCOV
958
                const Signature *s = de_ctx->sig_array[e->isset_sids[i]];
×
UNCOV
959
                SCJbAppendUint(js, s->id);
×
UNCOV
960
            }
×
UNCOV
961
            SCJbClose(js);
×
UNCOV
962
        }
×
963
        // isnotset
UNCOV
964
        if (e->cnts[DETECT_FLOWBITS_CMD_ISNOTSET]) {
×
UNCOV
965
            SCJbOpenArray(js, "isnotset");
×
UNCOV
966
            for (uint32_t i = 0; i < e->isnotset_sids_idx; i++) {
×
UNCOV
967
                const Signature *s = de_ctx->sig_array[e->isnotset_sids[i]];
×
UNCOV
968
                SCJbAppendUint(js, s->id);
×
UNCOV
969
            }
×
UNCOV
970
            SCJbClose(js);
×
UNCOV
971
        }
×
972
        // unset
UNCOV
973
        if (e->cnts[DETECT_FLOWBITS_CMD_UNSET]) {
×
UNCOV
974
            SCJbOpenArray(js, "unset");
×
UNCOV
975
            for (uint32_t i = 0; i < e->unset_sids_idx; i++) {
×
UNCOV
976
                const Signature *s = de_ctx->sig_array[e->unset_sids[i]];
×
UNCOV
977
                SCJbAppendUint(js, s->id);
×
UNCOV
978
            }
×
UNCOV
979
            SCJbClose(js);
×
UNCOV
980
        }
×
981
        // toggle
UNCOV
982
        if (e->cnts[DETECT_FLOWBITS_CMD_TOGGLE]) {
×
UNCOV
983
            SCJbOpenArray(js, "toggle");
×
UNCOV
984
            for (uint32_t i = 0; i < e->toggle_sids_idx; i++) {
×
UNCOV
985
                const Signature *s = de_ctx->sig_array[e->toggle_sids[i]];
×
UNCOV
986
                SCJbAppendUint(js, s->id);
×
UNCOV
987
            }
×
UNCOV
988
            SCJbClose(js);
×
UNCOV
989
        }
×
UNCOV
990
        SCJbClose(js);
×
UNCOV
991
    }
×
UNCOV
992
    SCJbClose(js); // array
×
UNCOV
993
    SCJbClose(js); // object
×
994

UNCOV
995
    const char *filename = "flowbits.json";
×
UNCOV
996
    const char *log_dir = SCConfigGetLogDirectory();
×
UNCOV
997
    char log_path[PATH_MAX] = "";
×
UNCOV
998
    snprintf(log_path, sizeof(log_path), "%s/%s", log_dir, filename);
×
999

UNCOV
1000
    SCMutexLock(&g_flowbits_dump_write_m);
×
UNCOV
1001
    FILE *fp = fopen(log_path, "w");
×
UNCOV
1002
    if (fp != NULL) {
×
UNCOV
1003
        fwrite(SCJbPtr(js), SCJbLen(js), 1, fp);
×
UNCOV
1004
        fprintf(fp, "\n");
×
UNCOV
1005
        fclose(fp);
×
UNCOV
1006
    }
×
UNCOV
1007
    SCMutexUnlock(&g_flowbits_dump_write_m);
×
1008

UNCOV
1009
    SCJbFree(js);
×
UNCOV
1010
}
×
1011

1012
static bool PrefilterFlowbitIsPrefilterable(const Signature *s)
1013
{
×
1014
    SCLogDebug("sid:%u: checking", s->id);
×
1015

1016
    for (const SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; sm != NULL;
×
1017
            sm = sm->next) {
×
1018
        switch (sm->type) {
×
1019
            case DETECT_FLOWBITS: {
×
1020
                const DetectFlowbitsData *fb = (DetectFlowbitsData *)sm->ctx;
×
1021
                if (fb->cmd == DETECT_FLOWBITS_CMD_ISSET) {
×
1022
                    SCLogDebug("sid:%u: FLOWBITS ISSET can prefilter", s->id);
×
1023
                    return true;
×
1024
                }
×
1025
                break;
×
1026
            }
×
1027
        }
×
1028
    }
×
1029
    SCLogDebug("sid:%u: no flowbit prefilter", s->id);
×
1030
    return false;
×
1031
}
×
1032

1033
/** core flowbit data structure: map a flowbit id to the signatures that need inspecting after it is
1034
 * found. Part of a rb-tree. */
1035
typedef struct PrefilterFlowbit {
1036
    uint32_t *rule_id;     /**< array of signature iid that are part of this prefilter */
1037
    RB_ENTRY(PrefilterFlowbit) __attribute__((__packed__)) rb;
1038
    uint32_t id;           /**< flowbit id */
1039
    uint32_t rule_id_size; /**< size in elements of `rule_id` */
1040
    uint32_t rule_id_cnt;  /**< usage in elements of `rule_id` */
1041
} __attribute__((__packed__)) PrefilterFlowbit;
1042

1043
static int PrefilterFlowbitCompare(const PrefilterFlowbit *a, const PrefilterFlowbit *b)
1044
{
2,053✔
1045
    if (a->id > b->id)
2,053✔
1046
        return 1;
503✔
1047
    else if (a->id < b->id)
1,550✔
1048
        return -1;
742✔
1049
    else
808✔
1050
        return 0;
808✔
1051
}
2,053✔
1052

1053
/** red-black tree prototype for PFB (Prefilter Flow Bits) */
1054
RB_HEAD(PFB, PrefilterFlowbit);
1055
RB_PROTOTYPE(PFB, PrefilterFlowbit, rb, PrefilterFlowbitCompare);
1056
RB_GENERATE(PFB, PrefilterFlowbit, rb, PrefilterFlowbitCompare);
7,000✔
1057

7,000✔
1058
struct PrefilterEngineFlowbits {
7,000✔
1059
    struct PFB fb_tree;
7,000✔
1060
};
7,000✔
1061

7,000✔
1062
static void PrefilterFlowbitFree(void *vctx)
7,000✔
1063
{
7,000✔
1064
    struct PrefilterEngineFlowbits *ctx = vctx;
417✔
1065
    struct PrefilterFlowbit *rec, *safe = NULL;
417✔
1066
    RB_FOREACH_SAFE (rec, PFB, &ctx->fb_tree, safe) {
624✔
1067
        PFB_RB_REMOVE(&ctx->fb_tree, rec);
624✔
1068
        SCFree(rec->rule_id);
624✔
1069
        SCFree(rec);
624✔
1070
    }
624✔
1071

1072
    SCFree(ctx);
417✔
1073
}
417✔
1074

1075
static void PrefilterFlowbitMatch(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
1076
{
1,684✔
1077
    struct PrefilterEngineFlowbits *ctx = (struct PrefilterEngineFlowbits *)pectx;
1,684✔
1078
    SCLogDebug("%" PRIu64 ": ctx %p", PcapPacketCntGet(p), ctx);
1,684✔
1079

1080
    if (p->flow == NULL) {
1,684✔
1081
        SCReturn;
×
1082
    }
×
1083

1084
    for (GenericVar *gv = p->flow->flowvar; gv != NULL; gv = gv->next) {
2,427✔
1085
        if (gv->type != DETECT_FLOWBITS)
743✔
1086
            continue;
×
1087

1088
        PrefilterFlowbit lookup;
743✔
1089
        memset(&lookup, 0, sizeof(lookup));
743✔
1090
        lookup.id = gv->idx;
743✔
1091
        SCLogDebug("flowbit %u", gv->idx);
743✔
1092

1093
        PrefilterFlowbit *b = PFB_RB_FIND(&ctx->fb_tree, &lookup);
743✔
1094
        if (b == NULL) {
743✔
1095
            SCLogDebug("flowbit %u not in the tree", lookup.id);
113✔
1096
        } else {
630✔
1097
            SCLogDebug("flowbit %u found in the tree: %u", lookup.id, b->id);
630✔
1098

1099
            PrefilterAddSids(&det_ctx->pmq, b->rule_id, b->rule_id_cnt);
630✔
1100
#ifdef DEBUG
1101
            for (uint32_t x = 0; x < b->rule_id_cnt; x++) {
1102
                const Signature *s = det_ctx->de_ctx->sig_array[b->rule_id[x]];
1103
                SCLogDebug("flowbit %u -> sig %u", gv->idx, s->id);
1104
            }
1105
#endif
1106
        }
630✔
1107
    }
743✔
1108
}
1,684✔
1109

1110
static void PrefilterFlowbitPostRuleMatch(
1111
        DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f)
1112
{
51✔
1113
    struct PrefilterEngineFlowbits *ctx = (struct PrefilterEngineFlowbits *)pectx;
51✔
1114
    SCLogDebug("%" PRIu64 ": ctx %p", PcapPacketCntGet(p), ctx);
51✔
1115

1116
    if (p->flow == NULL) {
51✔
1117
        SCReturn;
×
1118
    }
×
1119

1120
    for (uint32_t i = 0; i < det_ctx->post_rule_work_queue.len; i++) {
124✔
1121
        const PostRuleMatchWorkQueueItem *w = &det_ctx->post_rule_work_queue.q[i];
73✔
1122
        if (w->sm_type != DETECT_FLOWBITS)
73✔
1123
            continue;
×
1124

1125
        PrefilterFlowbit lookup;
73✔
1126
        memset(&lookup, 0, sizeof(lookup));
73✔
1127
        lookup.id = w->value;
73✔
1128

1129
        PrefilterFlowbit *b = PFB_RB_FIND(&ctx->fb_tree, &lookup);
73✔
1130
        if (b == NULL) {
73✔
1131
            SCLogDebug("flowbit %u not in the tree", lookup.id);
×
1132
        } else {
73✔
1133
            SCLogDebug("flowbit %u found in the tree: %u. Adding %u sids", lookup.id, b->id,
73✔
1134
                    b->rule_id_cnt);
73✔
1135
            PrefilterAddSids(&det_ctx->pmq, b->rule_id, b->rule_id_cnt);
73✔
1136
#ifdef DEBUG
1137
            // SCLogDebug("b %u", b->rule_id_cnt);
1138
            for (uint32_t x = 0; x < b->rule_id_cnt; x++) {
1139
                Signature *s = det_ctx->de_ctx->sig_array[b->rule_id[x]];
1140
                SCLogDebug("flowbit %u -> sig %u (triggered by %u)", w->value, s->id,
1141
                        det_ctx->de_ctx->sig_array[w->id]->id);
1142
            }
1143
#endif
1144
        }
73✔
1145
    }
73✔
1146
}
51✔
1147

1148
#define BLOCK_SIZE 8
978✔
1149

1150
static int AddBitAndSid(
1151
        struct PrefilterEngineFlowbits *ctx, const Signature *s, const uint32_t flowbit_id)
1152
{
511✔
1153
    PrefilterFlowbit x;
511✔
1154
    memset(&x, 0, sizeof(x));
511✔
1155
    x.id = flowbit_id;
511✔
1156

1157
    PrefilterFlowbit *pfb = PFB_RB_FIND(&ctx->fb_tree, &x);
511✔
1158
    if (pfb == NULL) {
511✔
1159
        PrefilterFlowbit *add = SCCalloc(1, sizeof(*add));
424✔
1160
        if (add == NULL)
424✔
1161
            return -1;
×
1162

1163
        add->id = flowbit_id;
424✔
1164
        add->rule_id = SCCalloc(1, BLOCK_SIZE * sizeof(uint32_t));
424✔
1165
        if (add->rule_id == NULL) {
424✔
1166
            SCFree(add);
×
1167
            return -1;
×
1168
        }
×
1169
        add->rule_id_size = BLOCK_SIZE;
424✔
1170
        add->rule_id_cnt = 1;
424✔
1171
        add->rule_id[0] = s->iid;
424✔
1172

1173
        PrefilterFlowbit *res = PFB_RB_INSERT(&ctx->fb_tree, add);
424✔
1174
        SCLogDebug("not found, so added (res %p)", res);
424✔
1175
        if (res != NULL) {
424✔
1176
            // duplicate, shouldn't be possible after the FIND above
1177
            BUG_ON(1);
×
1178
            return -1;
×
1179
        }
×
1180
    } else {
424✔
1181
        SCLogDebug("found! pfb %p id %u", pfb, pfb->id);
87✔
1182

1183
        if (pfb->rule_id_cnt < pfb->rule_id_size) {
87✔
1184
            pfb->rule_id[pfb->rule_id_cnt++] = s->iid;
87✔
1185
        } else {
87✔
1186
            uint32_t *ptr =
×
1187
                    SCRealloc(pfb->rule_id, (pfb->rule_id_size + BLOCK_SIZE) * sizeof(uint32_t));
×
1188
            if (ptr == NULL) {
×
1189
                // memory stays in the tree
1190
                return -1;
×
1191
            }
×
1192
            pfb->rule_id = ptr;
×
1193
            pfb->rule_id_size += BLOCK_SIZE;
×
1194
            pfb->rule_id[pfb->rule_id_cnt++] = s->iid;
×
1195
        }
×
1196
    }
87✔
1197
    return 0;
511✔
1198
}
511✔
1199

1200
static int AddBitsAndSid(const DetectEngineCtx *de_ctx, struct PrefilterEngineFlowbits *ctx,
1201
        const DetectFlowbitsData *fb, const Signature *s)
1202
{
511✔
1203
    if (fb->or_list_size == 0) {
511✔
1204
        if (AddBitAndSid(ctx, s, fb->idx) < 0) {
503✔
1205
            return -1;
×
1206
        }
×
1207
    } else {
503✔
1208
        for (uint8_t i = 0; i < fb->or_list_size; i++) {
16✔
1209
            SCLogDebug("flowbit OR: bit %u", fb->or_list[i]);
8✔
1210
            if (AddBitAndSid(ctx, s, fb->or_list[i]) < 0) {
8✔
1211
                return -1;
×
1212
            }
×
1213
        }
8✔
1214
    }
8✔
1215
    return 0;
511✔
1216
}
511✔
1217

1218
static uint32_t NextMultiple(const uint32_t v, const uint32_t m)
1219
{
554✔
1220
    return v + (m - v % m);
554✔
1221
}
554✔
1222

1223
/** \internal
1224
 *  \brief adds sids for 'isset' prefilter flowbits
1225
 *  \retval int 1 if we added sid(s), 0 if we didn't, -1 on error */
1226
// TODO skip sids that aren't set by this sgh
1227
// TODO skip sids that doesn't have a isset in the same direction
1228
static int AddIssetSidsForBit(const DetectEngineCtx *de_ctx, const struct FBAnalyzer *fba,
1229
        const DetectFlowbitsData *fb, PrefilterFlowbit *add)
1230
{
572✔
1231
    int added = 0;
572✔
1232
    for (uint32_t i = 0; i < fba->array[fb->idx].isset_sids_idx; i++) {
945✔
1233
        const uint32_t sig_iid = fba->array[fb->idx].isset_sids[i];
373✔
1234
        const Signature *s = de_ctx->sig_array[sig_iid];
373✔
1235
        SCLogDebug("flowbit: %u => considering sid %u (iid:%u)", fb->idx, s->id, s->iid);
373✔
1236

1237
        /* Skip sids that aren't prefilter. These would just run all the time. */
1238
        if (s->init_data->prefilter_sm == NULL ||
373✔
1239
                s->init_data->prefilter_sm->type != DETECT_FLOWBITS) {
373✔
1240
#ifdef DEBUG
1241
            const char *name = s->init_data->prefilter_sm
1242
                                       ? sigmatch_table[s->init_data->prefilter_sm->type].name
1243
                                       : "none";
1244
            SCLogDebug("flowbit: %u => rejected sid %u (iid:%u). No prefilter or prefilter not "
1245
                       "flowbits (%p, %s, %d)",
1246
                    fb->idx, s->id, sig_iid, s->init_data->prefilter_sm, name,
1247
                    s->init_data->prefilter_sm ? s->init_data->prefilter_sm->type : -1);
1248
#endif
1249
            continue;
123✔
1250
        }
123✔
1251

1252
        /* only add sids that match our bit */
1253
        const DetectFlowbitsData *fs_fb =
250✔
1254
                (const DetectFlowbitsData *)s->init_data->prefilter_sm->ctx;
250✔
1255
        if (fs_fb->idx != fb->idx) {
250✔
1256
            SCLogDebug(
2✔
1257
                    "flowbit: %u => rejected sid %u (iid:%u). Sig prefilters on different bit %u",
2✔
1258
                    fb->idx, s->id, sig_iid, fs_fb->idx);
2✔
1259
            continue;
2✔
1260
        }
2✔
1261

1262
        bool dup = false;
250✔
1263
        for (uint32_t x = 0; x < add->rule_id_cnt; x++) {
298✔
1264
            if (add->rule_id[x] == sig_iid) {
50✔
1265
                dup = true;
18✔
1266
            }
18✔
1267
        }
50✔
1268

1269
        if (!dup) {
248✔
1270
            if (add->rule_id_cnt < add->rule_id_size) {
230✔
1271
                add->rule_id[add->rule_id_cnt++] = sig_iid;
230✔
1272
            } else {
230✔
1273
                uint32_t *ptr = SCRealloc(
×
1274
                        add->rule_id, (add->rule_id_size + BLOCK_SIZE) * sizeof(uint32_t));
×
1275
                if (ptr == NULL) {
×
1276
                    return -1;
×
1277
                }
×
1278
                add->rule_id = ptr;
×
1279
                add->rule_id_size += BLOCK_SIZE;
×
1280
                add->rule_id[add->rule_id_cnt++] = sig_iid;
×
1281
            }
×
1282
            added = 1;
230✔
1283
            SCLogDebug("flowbit: %u => accepted sid %u (iid:%u)", fb->idx, s->id, sig_iid);
230✔
1284
        }
230✔
1285
    }
248✔
1286
    return added;
572✔
1287
}
572✔
1288

1289
/* TODO shouldn't add sids for which Signature::num is < our num. Is this possible after sorting? */
1290

1291
/** \brief For set/toggle flowbits, build "set" post-rule-match engine
1292
 *
1293
 *  For set/toggle flowbits, a special post-rule-match engine is constructed
1294
 *  to update the running match array during rule matching.
1295
 */
1296
static int AddBitSetToggle(const DetectEngineCtx *de_ctx, struct FBAnalyzer *fba,
1297
        struct PrefilterEngineFlowbits *ctx, const DetectFlowbitsData *fb, const Signature *s)
1298
{
572✔
1299
    PrefilterFlowbit x;
572✔
1300
    memset(&x, 0, sizeof(x));
572✔
1301
    x.id = fb->idx;
572✔
1302
    PrefilterFlowbit *pfb = PFB_RB_FIND(&ctx->fb_tree, &x);
572✔
1303
    if (pfb == NULL) {
572✔
1304
        PrefilterFlowbit *add = SCCalloc(1, sizeof(*add));
554✔
1305
        if (add == NULL)
554✔
1306
            return -1;
×
1307

1308
        add->id = fb->idx;
554✔
1309
        add->rule_id_size = NextMultiple(fba->array[fb->idx].isset_sids_idx, BLOCK_SIZE);
554✔
1310
        add->rule_id = SCCalloc(1, add->rule_id_size * sizeof(uint32_t));
554✔
1311
        if (add->rule_id == NULL) {
554✔
1312
            SCFree(add);
×
1313
            return -1;
×
1314
        }
×
1315

1316
        if (AddIssetSidsForBit(de_ctx, fba, fb, add) != 1) {
554✔
1317
            SCLogDebug("no sids added");
354✔
1318
            SCFree(add->rule_id);
354✔
1319
            SCFree(add);
354✔
1320
            return 0;
354✔
1321
        }
354✔
1322
        PrefilterFlowbit *res = PFB_RB_INSERT(&ctx->fb_tree, add);
200✔
1323
        SCLogDebug("not found, so added (res %p)", res);
200✔
1324
        BUG_ON(res != NULL); // TODO if res != NULL we have a duplicate which should be impossible
200✔
1325
    } else {
200✔
1326
        SCLogDebug("found! pfb %p id %u", pfb, pfb->id);
18✔
1327

1328
        int r = AddIssetSidsForBit(de_ctx, fba, fb, pfb);
18✔
1329
        if (r < 0) {
18✔
1330
            return -1;
×
1331
        } else if (r == 0) {
18✔
1332
            SCLogDebug("no sids added");
18✔
1333
            return 0;
18✔
1334
        }
18✔
1335
    }
18✔
1336
    return 1;
200✔
1337
}
572✔
1338

1339
/** \brief build flowbit prefilter state(s)
1340
 *
1341
 *  Build "set" and "isset" states.
1342
 *
1343
 *  For each flowbit "isset" in the sgh, we need to check:
1344
 *  1. is it supported
1345
 *  2. is prefilter enabled
1346
 *  3. does it match in the same dir or only opposing dir
1347
 */
1348
static int PrefilterSetupFlowbits(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
1349
{
398✔
1350
    if (sgh == NULL)
398✔
1351
        return 0;
×
1352

1353
    SCLogDebug("sgh %p: setting up prefilter", sgh);
398✔
1354
    struct PrefilterEngineFlowbits *isset_ctx = NULL;
398✔
1355
    struct PrefilterEngineFlowbits *set_ctx = NULL;
398✔
1356

1357
    struct FBAnalyzer fb_analysis = DetectFlowbitsAnalyzeForGroup(de_ctx, sgh);
398✔
1358
    if (fb_analysis.array == NULL)
398✔
UNCOV
1359
        goto error;
×
1360

1361
    for (uint32_t i = 0; i < sgh->init->sig_cnt; i++) {
8,607✔
1362
        Signature *s = sgh->init->match_array[i];
8,209✔
1363
        if (s == NULL)
8,209✔
1364
            continue;
×
1365

1366
        SCLogDebug("checking sid %u", s->id);
8,209✔
1367

1368
        /* first build the 'set' state */
1369
        for (SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_POSTMATCH]; sm != NULL;
9,385✔
1370
                sm = sm->next) {
8,209✔
1371
            if (sm->type != DETECT_FLOWBITS) {
1,176✔
1372
                SCLogDebug("skip non flowbits sm");
588✔
1373
                continue;
588✔
1374
            }
588✔
1375

1376
            DetectFlowbitsData *fb = (DetectFlowbitsData *)sm->ctx;
588✔
1377
            if (fb->cmd == DETECT_FLOWBITS_CMD_SET) {
588✔
1378
                SCLogDebug(
362✔
1379
                        "DETECT_SM_LIST_POSTMATCH: sid %u DETECT_FLOWBITS set %u", s->id, fb->idx);
362✔
1380
            } else if (fb->cmd == DETECT_FLOWBITS_CMD_TOGGLE) {
362✔
1381
                SCLogDebug("DETECT_SM_LIST_POSTMATCH: sid %u DETECT_FLOWBITS toggle %u", s->id,
221✔
1382
                        fb->idx);
221✔
1383
            } else {
221✔
1384
                SCLogDebug("unsupported flowbits setting");
5✔
1385
                continue;
5✔
1386
            }
5✔
1387

1388
            if (fb_analysis.array[fb->idx].isnotset_sids_idx ||
583✔
1389
                    fb_analysis.array[fb->idx].unset_sids_idx) {
583✔
1390
                SCLogDebug("flowbit %u not supported: unset in use", fb->idx);
11✔
1391
                continue;
11✔
1392
            }
11✔
1393

1394
            if (set_ctx == NULL) {
572✔
1395
                set_ctx = SCCalloc(1, sizeof(*set_ctx));
208✔
1396
                if (set_ctx == NULL)
208✔
1397
                    goto error;
×
1398
            }
208✔
1399

1400
            SCLogDebug("setting up sets/toggles for sid %u", s->id);
572✔
1401
            if (AddBitSetToggle(de_ctx, &fb_analysis, set_ctx, fb, s) == 1) {
572✔
1402
                // flag the set/toggle to trigger the post-rule match logic
1403
                SCLogDebug("set up sets/toggles for sid %u", s->id);
200✔
1404
                fb->post_rule_match_prefilter = true;
200✔
1405
            }
200✔
1406

1407
            // TODO don't add for sigs that don't have isset in this sgh. Reasoning:
1408
            // prefilter post match logic only makes sense in the same dir as otherwise
1409
            // the regular 'isset' logic can simply run with the regular prefilters
1410
            // before the rule loop
1411
        }
572✔
1412

1413
        /* next, build the 'isset' state */
1414
        if (s->init_data->prefilter_sm == NULL ||
8,209✔
1415
                s->init_data->prefilter_sm->type != DETECT_FLOWBITS) {
8,209✔
1416
            SCLogDebug("no prefilter or prefilter not flowbits");
7,698✔
1417
            continue;
7,698✔
1418
        }
7,698✔
1419

1420
        const DetectFlowbitsData *fb = (DetectFlowbitsData *)s->init_data->prefilter_sm->ctx;
511✔
1421
        if (fb_analysis.array[fb->idx].isnotset_sids_idx ||
511✔
1422
                fb_analysis.array[fb->idx].unset_sids_idx) {
511✔
1423
            SCLogDebug("flowbit %u not supported: toggle or unset in use", fb->idx);
×
1424
            s->init_data->prefilter_sm = NULL;
×
1425
            s->flags &= ~SIG_FLAG_PREFILTER;
×
1426
            continue;
×
1427
        }
×
1428

1429
        SCLogDebug("isset: adding sid %u, flowbit %u", s->id, fb->idx);
511✔
1430

1431
        if (isset_ctx == NULL) {
511✔
1432
            isset_ctx = SCCalloc(1, sizeof(*isset_ctx));
209✔
1433
            if (isset_ctx == NULL)
209✔
1434
                goto error;
×
1435
        }
209✔
1436
        if (AddBitsAndSid(de_ctx, isset_ctx, fb, s) < 0) {
511✔
1437
            goto error;
×
1438
        }
×
1439
    }
511✔
1440

1441
    /* finally, register the states with their engines */
1442
    static const char *g_prefilter_flowbits_isset = "flowbits:isset";
398✔
1443
    if (isset_ctx != NULL) {
398✔
1444
        enum SignatureHookPkt hook = SIGNATURE_HOOK_PKT_NOT_SET; // TODO review
209✔
1445
        PrefilterAppendEngine(de_ctx, sgh, PrefilterFlowbitMatch, SIG_MASK_REQUIRE_FLOW, hook,
209✔
1446
                isset_ctx, PrefilterFlowbitFree, g_prefilter_flowbits_isset);
209✔
1447
        SCLogDebug("isset: added prefilter engine");
209✔
1448

1449
        if (set_ctx != NULL && !RB_EMPTY(&set_ctx->fb_tree)) {
209✔
1450
            static const char *g_prefilter_flowbits_set = "flowbits:set";
118✔
1451
            PrefilterAppendPostRuleEngine(de_ctx, sgh, PrefilterFlowbitPostRuleMatch, set_ctx,
118✔
1452
                    PrefilterFlowbitFree, g_prefilter_flowbits_set);
118✔
1453
            SCLogDebug("set/toggle: added prefilter engine");
118✔
1454
        } else {
118✔
1455
            if (set_ctx) {
91✔
1456
                PrefilterFlowbitFree(set_ctx);
25✔
1457
            }
25✔
1458
            SCLogDebug("set/toggle: NO prefilter engine added");
91✔
1459
        }
91✔
1460
    } else if (set_ctx != NULL) {
209✔
1461
        PrefilterFlowbitFree(set_ctx);
65✔
1462
    }
65✔
1463
    FBAnalyzerFree(&fb_analysis);
398✔
1464
    return 0;
398✔
1465

UNCOV
1466
error:
×
UNCOV
1467
    if (set_ctx) {
×
1468
        PrefilterFlowbitFree(set_ctx);
×
1469
    }
×
UNCOV
1470
    if (isset_ctx) {
×
1471
        PrefilterFlowbitFree(isset_ctx);
×
1472
    }
×
UNCOV
1473
    FBAnalyzerFree(&fb_analysis);
×
UNCOV
1474
    return -1;
×
1475
}
398✔
1476

1477
#ifdef UNITTESTS
1478

1479
static int FlowBitsTestParse01(void)
1480
{
1481
    char command[16] = "", name[16] = "";
1482

1483
    /* Single argument version. */
1484
    FAIL_IF(!DetectFlowbitParse("noalert", command, sizeof(command), name,
1485
            sizeof(name)));
1486
    FAIL_IF(strcmp(command, "noalert") != 0);
1487

1488
    /* No leading or trailing spaces. */
1489
    FAIL_IF(!DetectFlowbitParse("set,flowbit", command, sizeof(command), name,
1490
            sizeof(name)));
1491
    FAIL_IF(strcmp(command, "set") != 0);
1492
    FAIL_IF(strcmp(name, "flowbit") != 0);
1493

1494
    /* Leading space. */
1495
    FAIL_IF(!DetectFlowbitParse("set, flowbit", command, sizeof(command), name,
1496
            sizeof(name)));
1497
    FAIL_IF(strcmp(command, "set") != 0);
1498
    FAIL_IF(strcmp(name, "flowbit") != 0);
1499

1500
    /* Trailing space. */
1501
    FAIL_IF(!DetectFlowbitParse("set,flowbit ", command, sizeof(command), name,
1502
            sizeof(name)));
1503
    FAIL_IF(strcmp(command, "set") != 0);
1504
    FAIL_IF(strcmp(name, "flowbit") != 0);
1505

1506
    /* Leading and trailing space. */
1507
    FAIL_IF(!DetectFlowbitParse("set, flowbit ", command, sizeof(command), name,
1508
            sizeof(name)));
1509
    FAIL_IF(strcmp(command, "set") != 0);
1510
    FAIL_IF(strcmp(name, "flowbit") != 0);
1511

1512
    /* Spaces are not allowed in the name. */
1513
    FAIL_IF(DetectFlowbitParse("set,namewith space", command, sizeof(command),
1514
            name, sizeof(name)));
1515

1516
    PASS;
1517
}
1518

1519
/**
1520
 * \test FlowBitsTestSig01 is a test for a valid noalert flowbits option
1521
 *
1522
 *  \retval 1 on success
1523
 *  \retval 0 on failure
1524
 */
1525

1526
static int FlowBitsTestSig01(void)
1527
{
1528
    Signature *s = NULL;
1529
    DetectEngineCtx *de_ctx = NULL;
1530

1531
    de_ctx = DetectEngineCtxInit();
1532
    FAIL_IF_NULL(de_ctx);
1533

1534
    de_ctx->flags |= DE_QUIET;
1535

1536
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Noalert\"; flowbits:noalert,wrongusage; content:\"GET \"; sid:1;)");
1537
    FAIL_IF_NOT_NULL(s);
1538

1539
    SigGroupBuild(de_ctx);
1540
    DetectEngineCtxFree(de_ctx);
1541
    PASS;
1542
}
1543

1544
/**
1545
 * \test FlowBitsTestSig02 is a test for a valid isset,set,isnotset,unset,toggle flowbits options
1546
 *
1547
 *  \retval 1 on success
1548
 *  \retval 0 on failure
1549
 */
1550

1551
static int FlowBitsTestSig02(void)
1552
{
1553
    Signature *s = NULL;
1554
    ThreadVars th_v;
1555
    DetectEngineCtx *de_ctx = NULL;
1556

1557
    memset(&th_v, 0, sizeof(th_v));
1558

1559
    de_ctx = DetectEngineCtxInit();
1560
    FAIL_IF_NULL(de_ctx);
1561

1562
    de_ctx->flags |= DE_QUIET;
1563

1564
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"isset rule need an option\"; flowbits:isset; content:\"GET \"; sid:1;)");
1565
    FAIL_IF_NOT_NULL(s);
1566

1567
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"isnotset rule need an option\"; flowbits:isnotset; content:\"GET \"; sid:2;)");
1568
    FAIL_IF_NOT_NULL(s);
1569

1570
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"set rule need an option\"; flowbits:set; content:\"GET \"; sid:3;)");
1571
    FAIL_IF_NOT_NULL(s);
1572

1573
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"unset rule need an option\"; flowbits:unset; content:\"GET \"; sid:4;)");
1574
    FAIL_IF_NOT_NULL(s);
1575

1576
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"toggle rule need an option\"; flowbits:toggle; content:\"GET \"; sid:5;)");
1577
    FAIL_IF_NOT_NULL(s);
1578

1579
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"!set is not an option\"; flowbits:!set,myerr; content:\"GET \"; sid:6;)");
1580
    FAIL_IF_NOT_NULL(s);
1581

1582
    SigGroupBuild(de_ctx);
1583
    DetectEngineCtxFree(de_ctx);
1584

1585
    PASS;
1586
}
1587

1588
/**
1589
 * \test FlowBitsTestSig03 is a test for a invalid flowbits option
1590
 *
1591
 *  \retval 1 on success
1592
 *  \retval 0 on failure
1593
 */
1594

1595
static int FlowBitsTestSig03(void)
1596
{
1597
    Signature *s = NULL;
1598
    DetectEngineCtx *de_ctx = NULL;
1599

1600
    de_ctx = DetectEngineCtxInit();
1601
    FAIL_IF_NULL(de_ctx);
1602

1603
    de_ctx->flags |= DE_QUIET;
1604

1605
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Unknown cmd\"; flowbits:wrongcmd; content:\"GET \"; sid:1;)");
1606
    FAIL_IF_NOT_NULL(s);
1607

1608
    SigGroupBuild(de_ctx);
1609
    DetectEngineCtxFree(de_ctx);
1610
    PASS;
1611
}
1612

1613
/**
1614
 * \test FlowBitsTestSig04 is a test check idx value
1615
 *
1616
 *  \retval 1 on success
1617
 *  \retval 0 on failure
1618
 */
1619

1620
static int FlowBitsTestSig04(void)
1621
{
1622
    Signature *s = NULL;
1623
    DetectEngineCtx *de_ctx = NULL;
1624
    int idx = 0;
1625
    de_ctx = DetectEngineCtxInit();
1626
    FAIL_IF_NULL(de_ctx);
1627

1628
    de_ctx->flags |= DE_QUIET;
1629

1630
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"isset option\"; flowbits:isset,fbt; content:\"GET \"; sid:1;)");
1631
    FAIL_IF_NULL(s);
1632

1633
    idx = VarNameStoreRegister("fbt", VAR_TYPE_FLOW_BIT);
1634
    FAIL_IF(idx == 0);
1635

1636
    SigGroupBuild(de_ctx);
1637
    DetectEngineCtxFree(de_ctx);
1638
    PASS;
1639
}
1640

1641
/**
1642
 * \test FlowBitsTestSig05 is a test check noalert flag
1643
 *
1644
 *  \retval 1 on success
1645
 *  \retval 0 on failure
1646
 */
1647

1648
static int FlowBitsTestSig05(void)
1649
{
1650
    Signature *s = NULL;
1651
    DetectEngineCtx *de_ctx = NULL;
1652

1653
    de_ctx = DetectEngineCtxInit();
1654
    FAIL_IF_NULL(de_ctx);
1655

1656
    de_ctx->flags |= DE_QUIET;
1657

1658
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Noalert\"; flowbits:noalert; content:\"GET \"; sid:1;)");
1659
    FAIL_IF_NULL(s);
1660
    FAIL_IF((s->action & ACTION_ALERT) != 0);
1661

1662
    SigGroupBuild(de_ctx);
1663
    DetectEngineCtxFree(de_ctx);
1664
    PASS;
1665
}
1666

1667
/**
1668
 * \test FlowBitsTestSig06 is a test set flowbits option
1669
 *
1670
 *  \retval 1 on success
1671
 *  \retval 0 on failure
1672
 */
1673

1674
static int FlowBitsTestSig06(void)
1675
{
1676
    uint8_t *buf = (uint8_t *)
1677
                    "GET /one/ HTTP/1.1\r\n"
1678
                    "Host: one.example.org\r\n"
1679
                    "\r\n";
1680
    uint16_t buflen = strlen((char *)buf);
1681
    Packet *p = PacketGetFromAlloc();
1682
    FAIL_IF_NULL(p);
1683
    Signature *s = NULL;
1684
    ThreadVars th_v;
1685
    DetectEngineThreadCtx *det_ctx = NULL;
1686
    DetectEngineCtx *de_ctx = NULL;
1687
    Flow f;
1688
    GenericVar flowvar, *gv = NULL;
1689
    int result = 0;
1690
    uint32_t idx = 0;
1691

1692
    memset(&th_v, 0, sizeof(th_v));
1693
    StatsThreadInit(&th_v.stats);
1694
    memset(&f, 0, sizeof(Flow));
1695
    memset(&flowvar, 0, sizeof(GenericVar));
1696

1697
    FLOW_INITIALIZE(&f);
1698
    p->flow = &f;
1699
    p->flow->flowvar = &flowvar;
1700

1701
    p->src.family = AF_INET;
1702
    p->dst.family = AF_INET;
1703
    p->payload = buf;
1704
    p->payload_len = buflen;
1705
    p->proto = IPPROTO_TCP;
1706
    p->flags |= PKT_HAS_FLOW;
1707
    p->flowflags |= (FLOW_PKT_TOSERVER | FLOW_PKT_TOSERVER_FIRST);
1708

1709
    de_ctx = DetectEngineCtxInit();
1710
    FAIL_IF_NULL(de_ctx);
1711

1712
    de_ctx->flags |= DE_QUIET;
1713

1714
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit set\"; flowbits:set,myflow; sid:10;)");
1715
    FAIL_IF_NULL(s);
1716

1717
    idx = VarNameStoreRegister("myflow", VAR_TYPE_FLOW_BIT);
1718
    FAIL_IF_NOT(idx);
1719
    SigGroupBuild(de_ctx);
1720
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1721

1722
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1723

1724
    gv = p->flow->flowvar;
1725
    FAIL_IF_NULL(gv);
1726
    for ( ; gv != NULL; gv = gv->next) {
1727
        if (gv->type == DETECT_FLOWBITS && gv->idx == idx) {
1728
                result = 1;
1729
        }
1730
    }
1731
    FAIL_IF_NOT(result);
1732

1733
    PacketFree(p);
1734
    FLOW_DESTROY(&f);
1735

1736
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1737
    DetectEngineCtxFree(de_ctx);
1738
    StatsThreadCleanup(&th_v.stats);
1739
    PASS;
1740
}
1741

1742
/**
1743
 * \test FlowBitsTestSig07 is a test unset flowbits option
1744
 *
1745
 *  \retval 1 on success
1746
 *  \retval 0 on failure
1747
 */
1748

1749
static int FlowBitsTestSig07(void)
1750
{
1751
    uint8_t *buf = (uint8_t *)
1752
                    "GET /one/ HTTP/1.1\r\n"
1753
                    "Host: one.example.org\r\n"
1754
                    "\r\n";
1755
    uint16_t buflen = strlen((char *)buf);
1756
    Packet *p = PacketGetFromAlloc();
1757
    FAIL_IF_NULL(p);
1758
    Signature *s = NULL;
1759
    ThreadVars th_v;
1760
    DetectEngineThreadCtx *det_ctx = NULL;
1761
    DetectEngineCtx *de_ctx = NULL;
1762
    Flow f;
1763
    GenericVar flowvar, *gv = NULL;
1764
    int result = 0;
1765
    uint32_t idx = 0;
1766

1767
    memset(&th_v, 0, sizeof(th_v));
1768
    StatsThreadInit(&th_v.stats);
1769
    memset(&f, 0, sizeof(Flow));
1770
    memset(&flowvar, 0, sizeof(GenericVar));
1771

1772
    FLOW_INITIALIZE(&f);
1773
    p->flow = &f;
1774
    p->flow->flowvar = &flowvar;
1775

1776
    p->src.family = AF_INET;
1777
    p->dst.family = AF_INET;
1778
    p->payload = buf;
1779
    p->payload_len = buflen;
1780
    p->proto = IPPROTO_TCP;
1781

1782
    de_ctx = DetectEngineCtxInit();
1783
    FAIL_IF_NULL(de_ctx);
1784

1785
    de_ctx->flags |= DE_QUIET;
1786

1787
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit set\"; flowbits:set,myflow2; sid:10;)");
1788
    FAIL_IF_NULL(s);
1789

1790
    s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit unset\"; flowbits:unset,myflow2; sid:11;)");
1791
    FAIL_IF_NULL(s);
1792

1793
    idx = VarNameStoreRegister("myflow", VAR_TYPE_FLOW_BIT);
1794
    FAIL_IF_NOT(idx);
1795
    SigGroupBuild(de_ctx);
1796
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1797

1798
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1799

1800
    gv = p->flow->flowvar;
1801
    FAIL_IF_NULL(gv);
1802

1803
    for ( ; gv != NULL; gv = gv->next) {
1804
        if (gv->type == DETECT_FLOWBITS && gv->idx == idx) {
1805
                result = 1;
1806
        }
1807
    }
1808
    FAIL_IF(result);
1809

1810
    PacketFree(p);
1811
    FLOW_DESTROY(&f);
1812
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1813
    DetectEngineCtxFree(de_ctx);
1814
    StatsThreadCleanup(&th_v.stats);
1815
    PASS;
1816
}
1817

1818
/**
1819
 * \test FlowBitsTestSig08 is a test toggle flowbits option
1820
 *
1821
 *  \retval 1 on success
1822
 *  \retval 0 on failure
1823
 */
1824

1825
static int FlowBitsTestSig08(void)
1826
{
1827
    uint8_t *buf = (uint8_t *)
1828
                    "GET /one/ HTTP/1.1\r\n"
1829
                    "Host: one.example.org\r\n"
1830
                    "\r\n";
1831
    uint16_t buflen = strlen((char *)buf);
1832
    Packet *p = PacketGetFromAlloc();
1833
    if (unlikely(p == NULL))
1834
        return 0;
1835
    Signature *s = NULL;
1836
    ThreadVars th_v;
1837
    DetectEngineThreadCtx *det_ctx = NULL;
1838
    DetectEngineCtx *de_ctx = NULL;
1839
    Flow f;
1840
    GenericVar flowvar, *gv = NULL;
1841
    int result = 0;
1842
    uint32_t idx = 0;
1843

1844
    memset(&th_v, 0, sizeof(th_v));
1845
    StatsThreadInit(&th_v.stats);
1846
    memset(&f, 0, sizeof(Flow));
1847
    memset(&flowvar, 0, sizeof(GenericVar));
1848

1849
    FLOW_INITIALIZE(&f);
1850
    p->flow = &f;
1851
    p->flow->flowvar = &flowvar;
1852

1853
    p->src.family = AF_INET;
1854
    p->dst.family = AF_INET;
1855
    p->payload = buf;
1856
    p->payload_len = buflen;
1857
    p->proto = IPPROTO_TCP;
1858

1859
    de_ctx = DetectEngineCtxInit();
1860
    FAIL_IF_NULL(de_ctx);
1861

1862
    de_ctx->flags |= DE_QUIET;
1863

1864
    s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit set\"; flowbits:set,myflow2; sid:10;)");
1865
    FAIL_IF_NULL(s);
1866

1867
    s = s->next  = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit unset\"; flowbits:toggle,myflow2; sid:11;)");
1868
    FAIL_IF_NULL(s);
1869

1870
    idx = VarNameStoreRegister("myflow", VAR_TYPE_FLOW_BIT);
1871
    FAIL_IF_NOT(idx);
1872
    SigGroupBuild(de_ctx);
1873
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1874

1875
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1876

1877
    gv = p->flow->flowvar;
1878
    FAIL_IF_NULL(gv);
1879

1880
    for ( ; gv != NULL; gv = gv->next) {
1881
        if (gv->type == DETECT_FLOWBITS && gv->idx == idx) {
1882
                result = 1;
1883
        }
1884
    }
1885
    FAIL_IF(result);
1886

1887
    PacketFree(p);
1888
    FLOW_DESTROY(&f);
1889
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1890
    DetectEngineCtxFree(de_ctx);
1891
    StatsThreadCleanup(&th_v.stats);
1892
    PASS;
1893
}
1894

1895
/**
1896
 * \brief this function registers unit tests for FlowBits
1897
 */
1898
void FlowBitsRegisterTests(void)
1899
{
1900
    UtRegisterTest("FlowBitsTestParse01", FlowBitsTestParse01);
1901
    UtRegisterTest("FlowBitsTestSig01", FlowBitsTestSig01);
1902
    UtRegisterTest("FlowBitsTestSig02", FlowBitsTestSig02);
1903
    UtRegisterTest("FlowBitsTestSig03", FlowBitsTestSig03);
1904
    UtRegisterTest("FlowBitsTestSig04", FlowBitsTestSig04);
1905
    UtRegisterTest("FlowBitsTestSig05", FlowBitsTestSig05);
1906
    UtRegisterTest("FlowBitsTestSig06", FlowBitsTestSig06);
1907
    UtRegisterTest("FlowBitsTestSig07", FlowBitsTestSig07);
1908
    UtRegisterTest("FlowBitsTestSig08", FlowBitsTestSig08);
1909
}
1910
#endif /* UNITTESTS */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc