• 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

84.55
/src/detect-engine-prefilter.c
1
/* Copyright (C) 2016-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
 *
23
 * Prefilter engine
24
 *
25
 * Prefilter engines have as purpose to check for a critical common part of
26
 * a set of rules. If the condition is present in the traffic, the rules
27
 * will have to be inspected individually. Otherwise, the rules can be
28
 * skipped.
29
 *
30
 * The best example of this is the MPM. From each rule take a pattern and
31
 * add it to the MPM state machine. Inspect that in one step and only
32
 * individually inspect the rules that had a match in MPM.
33
 *
34
 * This prefilter API is designed to abstract this logic so that it becomes
35
 * easier to add other types of prefilters.
36
 *
37
 * The prefilter engines are structured as a simple list of engines. Each
38
 * engine checks for a condition using it's callback function and private
39
 * data. It then adds the rule match candidates to the PrefilterRuleStore
40
 * structure.
41
 *
42
 * After the engines have run the resulting list of match candidates is
43
 * sorted by the rule id's so that the individual inspection happens in
44
 * the correct order.
45
 */
46

47
#include "suricata-common.h"
48
#include "suricata.h"
49

50
#include "detect-engine.h"
51
#include "detect-engine-prefilter.h"
52
#include "detect-engine-mpm.h"
53
#include "detect-engine-frame.h"
54
#include "detect-engine-uint.h"
55

56
#include "app-layer-parser.h"
57
#include "app-layer-htp.h"
58

59
#include "util-profiling.h"
60
#include "util-validate.h"
61
#include "util-hash-string.h"
62

63
static int PrefilterStoreGetId(DetectEngineCtx *de_ctx,
64
        const char *name, void (*FreeFunc)(void *));
65
static const PrefilterStore *PrefilterStoreGetStore(const DetectEngineCtx *de_ctx,
66
        const uint32_t id);
67

68
static inline void QuickSortSigIntId(SigIntId *sids, uint32_t n)
69
{
615,053✔
70
    if (n < 2)
615,053✔
71
        return;
360,354✔
72
    SigIntId p = sids[n / 2];
254,699✔
73
    SigIntId *l = sids;
254,699✔
74
    SigIntId *r = sids + n - 1;
254,699✔
75
    while (l <= r) {
1,534,984✔
76
        if (*l < p)
1,280,285✔
77
            l++;
577,551✔
78
        else if (*r > p)
702,734✔
79
            r--;
446,826✔
80
        else {
255,908✔
81
            SigIntId t = *l;
255,908✔
82
            *l = *r;
255,908✔
83
            *r = t;
255,908✔
84
            l++;
255,908✔
85
            r--;
255,908✔
86
        }
255,908✔
87
    }
1,280,285✔
88
    QuickSortSigIntId(sids, (uint32_t)(r - sids) + 1);
254,699✔
89
    QuickSortSigIntId(l, (uint32_t)(sids + n - l));
254,699✔
90
}
254,699✔
91

92
/**
93
 * \brief run prefilter engines on a transaction
94
 */
95
void DetectRunPrefilterTx(DetectEngineThreadCtx *det_ctx,
96
        const SigGroupHead *sgh,
97
        Packet *p,
98
        const uint8_t ipproto,
99
        const uint8_t flow_flags,
100
        const AppProto alproto,
101
        void *alstate,
102
        DetectTransaction *tx)
103
{
266,304✔
104
    /* reset rule store */
105
    det_ctx->pmq.rule_id_array_cnt = 0;
266,304✔
106

107
    SCLogDebug("packet %" PRIu64 " tx %p progress %d tx->detect_progress %02x", PcapPacketCntGet(p),
266,304✔
108
            tx->tx_ptr, tx->tx_progress, tx->detect_progress);
266,304✔
109

110
    PrefilterEngine *engine = sgh->tx_engines;
266,304✔
111
    do {
1,003,749✔
112
        // based on flow alproto, and engine, we get right tx_ptr
113
        void *tx_ptr = DetectGetInnerTx(tx->tx_ptr, alproto, engine->alproto, flow_flags);
1,003,749✔
114
        if (tx_ptr == NULL) {
1,003,749✔
115
            // incompatible engine->alproto with flow alproto
116
            goto next;
707,305✔
117
        }
707,305✔
118

119
        if (engine->ctx.tx_min_progress != -1) {
296,444✔
120
#ifdef DEBUG
121
            const char *pname = AppLayerParserGetStateNameById(ipproto, engine->alproto,
122
                    engine->ctx.tx_min_progress, flow_flags & (STREAM_TOSERVER | STREAM_TOCLIENT));
123
            SCLogDebug("engine %p min_progress %d %s:%s", engine, engine->ctx.tx_min_progress,
124
                    AppProtoToString(engine->alproto), pname);
125
#endif
126
            /* if engine needs tx state to be higher, break out. */
127
            if (engine->ctx.tx_min_progress > tx->tx_progress)
296,378✔
128
                break;
4,899✔
129
            if (tx->tx_progress > engine->ctx.tx_min_progress) {
291,479✔
130
                SCLogDebug("tx->tx_progress %u > engine->ctx.tx_min_progress %d", tx->tx_progress,
207,089✔
131
                        engine->ctx.tx_min_progress);
207,089✔
132

133
                /* if state value is at or beyond engine state, we can skip it. It means we ran at
134
                 * least once already. */
135
                if (tx->detect_progress > engine->ctx.tx_min_progress) {
207,089✔
136
                    SCLogDebug("tx already marked progress as beyond engine: %u > %u",
8,009✔
137
                            tx->detect_progress, engine->ctx.tx_min_progress);
8,009✔
138
                    goto next;
8,009✔
139
                } else {
199,080✔
140
                    SCLogDebug("tx->tx_progress %u > engine->ctx.tx_min_progress %d: "
199,080✔
141
                               "tx->detect_progress %u",
199,080✔
142
                            tx->tx_progress, engine->ctx.tx_min_progress, tx->detect_progress);
199,080✔
143
                }
199,080✔
144
            }
207,089✔
145
#ifdef DEBUG
146
            uint32_t old = det_ctx->pmq.rule_id_array_cnt;
147
#endif
148
            PREFILTER_PROFILING_START(det_ctx);
283,470✔
149
            engine->cb.PrefilterTx(det_ctx, engine->pectx, p, p->flow, tx_ptr, tx->tx_id,
283,470✔
150
                    tx->tx_data_ptr, flow_flags);
283,470✔
151
            PREFILTER_PROFILING_END(det_ctx, engine->gid);
283,470✔
152
            SCLogDebug("engine %p min_progress %d %s:%s: results %u", engine,
283,470✔
153
                    engine->ctx.tx_min_progress, AppProtoToString(engine->alproto), pname,
283,470✔
154
                    det_ctx->pmq.rule_id_array_cnt - old);
283,470✔
155

156
            if (tx->tx_progress > engine->ctx.tx_min_progress && engine->is_last_for_progress) {
283,470✔
157
                /* track with an offset of one, so that tx->progress 0 complete is tracked
158
                 * as 1, progress 1 as 2, etc. This is to allow 0 to mean: nothing tracked, even
159
                 * though a parser may use 0 as a valid value. */
160
                tx->detect_progress = engine->ctx.tx_min_progress + 1;
162,988✔
161
                SCLogDebug("tx->tx_progress %d engine->ctx.tx_min_progress %d "
162,988✔
162
                           "engine->is_last_for_progress %d => tx->detect_progress updated to %02x",
162,988✔
163
                        tx->tx_progress, engine->ctx.tx_min_progress, engine->is_last_for_progress,
162,988✔
164
                        tx->detect_progress);
162,988✔
165
            }
162,988✔
166
        } else {
283,470✔
167
            PREFILTER_PROFILING_START(det_ctx);
66✔
168
            engine->cb.PrefilterTx(det_ctx, engine->pectx, p, p->flow, tx_ptr, tx->tx_id,
66✔
169
                    tx->tx_data_ptr, flow_flags);
66✔
170
            PREFILTER_PROFILING_END(det_ctx, engine->gid);
66✔
171
        }
66✔
172
    next:
998,850✔
173
        if (engine->is_last)
998,850✔
174
            break;
261,405✔
175
        engine++;
737,445✔
176
    } while (1);
737,445✔
177

178
    /* Sort the rule list to lets look at pmq.
179
     * NOTE due to merging of 'stream' pmqs we *MAY* have duplicate entries */
180
    if (likely(det_ctx->pmq.rule_id_array_cnt > 1)) {
266,304✔
181
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_SORT1);
29,870✔
182
        QuickSortSigIntId(det_ctx->pmq.rule_id_array, det_ctx->pmq.rule_id_array_cnt);
29,870✔
183
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_SORT1);
29,870✔
184
    }
29,870✔
185
}
266,304✔
186

187
/** \brief invoke post-rule match "prefilter" engines
188
 *
189
 * Invoke prefilter engines that depend on a rule match to run.
190
 * e.g. the flowbits:set prefilter that adds sids that depend on
191
 * a flowbit "set" to the match array.
192
 */
193
void PrefilterPostRuleMatch(
194
        DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p, Flow *f)
195
{
57✔
196
    SCLogDebug("post-rule-match engines %p", sgh->post_rule_match_engines);
57✔
197
    if (sgh->post_rule_match_engines) {
57✔
198
        PrefilterEngine *engine = sgh->post_rule_match_engines;
51✔
199
        do {
51✔
200
            SCLogDebug("running post-rule-match engine");
51✔
201
            PREFILTER_PROFILING_START(det_ctx);
51✔
202
            engine->cb.PrefilterPostRule(det_ctx, engine->pectx, p, f);
51✔
203
            PREFILTER_PROFILING_END(det_ctx, engine->gid);
51✔
204

205
            if (engine->is_last)
51✔
206
                break;
51✔
207
            engine++;
×
208
        } while (1);
×
209

210
        if (det_ctx->pmq.rule_id_array_cnt > 1) {
51✔
211
            QuickSortSigIntId(det_ctx->pmq.rule_id_array, det_ctx->pmq.rule_id_array_cnt);
23✔
212
        }
23✔
213
    }
51✔
214
}
57✔
215

216
void Prefilter(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p,
217
        const uint8_t flags, const SignatureMask mask)
218
{
829,949✔
219
    SCEnter();
829,949✔
220
#if 0
221
    /* TODO review this check */
222
    SCLogDebug("sgh %p frame_engines %p", sgh, sgh->frame_engines);
223
    if (p->proto == IPPROTO_TCP && sgh->frame_engines && p->flow &&
224
            p->flow->alproto != ALPROTO_UNKNOWN && p->flow->alparser != NULL) {
225
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_RECORD);
226
        PrefilterFrames(det_ctx, sgh, p, flags, p->flow->alproto);
227
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_RECORD);
228
    }
229
#endif
230
    if (sgh->pkt_engines) {
829,949✔
231
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_PKT);
247,576✔
232
        /* run packet engines */
233
        PrefilterEngine *engine = sgh->pkt_engines;
247,576✔
234
        do {
305,725✔
235
            /* run engine if:
236
             * mask matches
237
             * no hook is used OR hook matches
238
             */
239
            if (((engine->ctx.pkt.mask & mask) == engine->ctx.pkt.mask) &&
305,725✔
240
                    (engine->ctx.pkt.hook == 0 || (p->pkt_hooks & BIT_U16(engine->ctx.pkt.hook)))) {
305,725✔
241
                PREFILTER_PROFILING_START(det_ctx);
289,181✔
242
                engine->cb.Prefilter(det_ctx, p, engine->pectx);
289,181✔
243
                PREFILTER_PROFILING_END(det_ctx, engine->gid);
289,181✔
244
            }
289,181✔
245

246
            if (engine->is_last)
305,725✔
247
                break;
247,576✔
248
            engine++;
58,149✔
249
        } while (1);
58,149✔
UNCOV
250
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_PKT);
×
251
    }
247,576✔
252

253
    /* run payload inspecting engines */
254
    if (sgh->payload_engines &&
829,949✔
255
        (p->payload_len || (p->flags & PKT_DETECT_HAS_STREAMDATA)) &&
829,949✔
256
        !(p->flags & PKT_NOPAYLOAD_INSPECTION))
829,949✔
257
    {
133,820✔
258
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_PAYLOAD);
133,820✔
259
        PrefilterEngine *engine = sgh->payload_engines;
133,820✔
260
        while (1) {
157,175✔
261
            PREFILTER_PROFILING_START(det_ctx);
157,175✔
262
            engine->cb.Prefilter(det_ctx, p, engine->pectx);
157,175✔
263
            PREFILTER_PROFILING_END(det_ctx, engine->gid);
157,175✔
264

265
            if (engine->is_last)
157,175✔
266
                break;
133,820✔
267
            engine++;
23,355✔
268
        }
23,355✔
269
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_PAYLOAD);
133,820✔
270
    }
133,820✔
271

272
    /* Sort the rule list to lets look at pmq.
273
     * NOTE due to merging of 'stream' pmqs we *MAY* have duplicate entries */
274
    if (likely(det_ctx->pmq.rule_id_array_cnt > 1)) {
829,949✔
275
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_SORT1);
75,762✔
276
        QuickSortSigIntId(det_ctx->pmq.rule_id_array, det_ctx->pmq.rule_id_array_cnt);
75,762✔
277
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_SORT1);
75,762✔
278
    }
75,762✔
279
    SCReturn;
829,949✔
280
}
829,949✔
281

282
int PrefilterAppendEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh, PrefilterPktFn PrefilterFunc,
283
        SignatureMask mask, enum SignatureHookPkt hook, void *pectx, void (*FreeFunc)(void *pectx),
284
        const char *name)
285
{
88,683✔
286
    if (sgh == NULL || PrefilterFunc == NULL || pectx == NULL)
88,683✔
287
        return -1;
×
288

289
    PrefilterEngineList *e = SCMallocAligned(sizeof(*e), CLS);
88,683✔
290
    if (e == NULL)
88,683✔
291
        return -1;
×
292
    memset(e, 0x00, sizeof(*e));
88,683✔
293

294
    // TODO right now we represent the hook in a u8 in the prefilter engine for space reasons.
295
    BUG_ON(hook >= 8);
88,683✔
296

297
    e->Prefilter = PrefilterFunc;
88,683✔
298
    e->pectx = pectx;
88,683✔
299
    e->Free = FreeFunc;
88,683✔
300
    e->pkt_mask = mask;
88,683✔
301
    e->pkt_hook = hook;
88,683✔
302

303
    if (sgh->init->pkt_engines == NULL) {
88,683✔
304
        sgh->init->pkt_engines = e;
59,318✔
305
    } else {
59,318✔
306
        PrefilterEngineList *t = sgh->init->pkt_engines;
29,365✔
307
        while (t->next != NULL) {
47,079✔
308
            t = t->next;
17,714✔
309
        }
17,714✔
310

311
        t->next = e;
29,365✔
312
        e->id = t->id + 1;
29,365✔
313
    }
29,365✔
314

315
    e->name = name;
88,683✔
316
    e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
88,683✔
317
    return 0;
88,683✔
318
}
88,683✔
319

320
int PrefilterAppendPayloadEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
321
        PrefilterPktFn PrefilterFunc, void *pectx, void (*FreeFunc)(void *pectx), const char *name)
322
{
38,652✔
323
    if (sgh == NULL || PrefilterFunc == NULL || pectx == NULL)
38,652✔
324
        return -1;
2,262✔
325

326
    PrefilterEngineList *e = SCMallocAligned(sizeof(*e), CLS);
36,390✔
327
    if (e == NULL)
36,390✔
328
        return -1;
×
329
    memset(e, 0x00, sizeof(*e));
36,390✔
330

331
    e->Prefilter = PrefilterFunc;
36,390✔
332
    e->pectx = pectx;
36,390✔
333
    e->Free = FreeFunc;
36,390✔
334

335
    if (sgh->init->payload_engines == NULL) {
36,390✔
336
        sgh->init->payload_engines = e;
28,828✔
337
    } else {
28,828✔
338
        PrefilterEngineList *t = sgh->init->payload_engines;
7,562✔
339
        while (t->next != NULL) {
7,562✔
340
            t = t->next;
×
341
        }
×
342

343
        t->next = e;
7,562✔
344
        e->id = t->id + 1;
7,562✔
345
    }
7,562✔
346

347
    e->name = name;
36,390✔
348
    e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
36,390✔
349
    return 0;
36,390✔
350
}
36,390✔
351

352
int PrefilterAppendTxEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
353
        PrefilterTxFn PrefilterTxFunc, AppProto alproto, int tx_min_progress, void *pectx,
354
        void (*FreeFunc)(void *pectx), const char *name)
355
{
226,555✔
356
    if (sgh == NULL || PrefilterTxFunc == NULL || pectx == NULL)
226,555✔
357
        return -1;
×
358

359
    PrefilterEngineList *e = SCMallocAligned(sizeof(*e), CLS);
226,555✔
360
    if (e == NULL)
226,555✔
361
        return -1;
×
362
    memset(e, 0x00, sizeof(*e));
226,555✔
363

364
    e->PrefilterTx = PrefilterTxFunc;
226,555✔
365
    e->pectx = pectx;
226,555✔
366
    e->alproto = alproto;
226,555✔
367
    // TODO change function prototype ?
368
    DEBUG_VALIDATE_BUG_ON(tx_min_progress > INT8_MAX);
226,555✔
369
    e->tx_min_progress = (uint8_t)tx_min_progress;
226,555✔
370
    e->Free = FreeFunc;
226,555✔
371

372
    if (sgh->init->tx_engines == NULL) {
226,555✔
373
        sgh->init->tx_engines = e;
44,286✔
374
    } else {
182,269✔
375
        PrefilterEngineList *t = sgh->init->tx_engines;
182,269✔
376
        while (t->next != NULL) {
1,258,000✔
377
            t = t->next;
1,075,731✔
378
        }
1,075,731✔
379

380
        t->next = e;
182,269✔
381
        e->id = t->id + 1;
182,269✔
382
    }
182,269✔
383

384
    e->name = name;
226,555✔
385
    e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
226,555✔
386
    return 0;
226,555✔
387
}
226,555✔
388

389
int PrefilterAppendFrameEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
390
        PrefilterFrameFn PrefilterFrameFunc, AppProto alproto, uint8_t frame_type, void *pectx,
391
        void (*FreeFunc)(void *pectx), const char *name)
392
{
7,582✔
393
    if (sgh == NULL || PrefilterFrameFunc == NULL || pectx == NULL)
7,582✔
394
        return -1;
×
395

396
    PrefilterEngineList *e = SCMallocAligned(sizeof(*e), CLS);
7,582✔
397
    if (e == NULL)
7,582✔
398
        return -1;
×
399
    memset(e, 0x00, sizeof(*e));
7,582✔
400

401
    e->frame_type = frame_type;
7,582✔
402
    e->alproto = alproto;
7,582✔
403
    e->PrefilterFrame = PrefilterFrameFunc;
7,582✔
404
    e->pectx = pectx;
7,582✔
405
    e->Free = FreeFunc;
7,582✔
406

407
    if (sgh->init->frame_engines == NULL) {
7,582✔
408
        sgh->init->frame_engines = e;
4,832✔
409
    } else {
4,832✔
410
        PrefilterEngineList *t = sgh->init->frame_engines;
2,750✔
411
        while (t->next != NULL) {
4,296✔
412
            t = t->next;
1,546✔
413
        }
1,546✔
414

415
        t->next = e;
2,750✔
416
        e->id = t->id + 1;
2,750✔
417
    }
2,750✔
418

419
    e->name = name;
7,582✔
420
    e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
7,582✔
421
    return 0;
7,582✔
422
}
7,582✔
423

424
int PrefilterAppendPostRuleEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
425
        void (*PrefilterPostRuleFunc)(
426
                DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f),
427
        void *pectx, void (*FreeFunc)(void *pectx), const char *name)
428
{
118✔
429
    if (sgh == NULL || PrefilterPostRuleFunc == NULL || pectx == NULL)
118✔
430
        return -1;
×
431

432
    PrefilterEngineList *e = SCMallocAligned(sizeof(*e), CLS);
118✔
433
    if (e == NULL)
118✔
434
        return -1;
×
435
    memset(e, 0x00, sizeof(*e));
118✔
436
    e->PrefilterPostRule = PrefilterPostRuleFunc;
118✔
437
    e->pectx = pectx;
118✔
438
    e->Free = FreeFunc;
118✔
439

440
    if (sgh->init->post_rule_match_engines == NULL) {
118✔
441
        sgh->init->post_rule_match_engines = e;
118✔
442
    } else {
118✔
443
        PrefilterEngineList *t = sgh->init->post_rule_match_engines;
×
444
        while (t->next != NULL) {
×
445
            t = t->next;
×
446
        }
×
447

448
        t->next = e;
×
449
        e->id = t->id + 1;
×
450
    }
×
451

452
    e->name = name;
118✔
453
    e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
118✔
454
    return 0;
118✔
455
}
118✔
456

457
static void PrefilterFreeEngineList(PrefilterEngineList *e)
458
{
359,328✔
459
    if (e->Free && e->pectx) {
359,328✔
460
        e->Free(e->pectx);
×
461
    }
×
462
    SCFreeAligned(e);
359,328✔
463
}
359,328✔
464

465
void PrefilterFreeEnginesList(PrefilterEngineList *list)
466
{
11,302,870✔
467
    PrefilterEngineList *t = list;
11,302,870✔
468

469
    while (t != NULL) {
11,662,198✔
470
        PrefilterEngineList *next = t->next;
359,328✔
471
        PrefilterFreeEngineList(t);
359,328✔
472
        t = next;
359,328✔
473
    }
359,328✔
474
}
11,302,870✔
475

476
static void PrefilterFreeEngines(const DetectEngineCtx *de_ctx, PrefilterEngine *list)
477
{
137,381✔
478
    PrefilterEngine *t = list;
137,381✔
479

480
    while (1) {
359,326✔
481
        const PrefilterStore *s = PrefilterStoreGetStore(de_ctx, t->gid);
359,326✔
482
        if (s && s->FreeFunc && t->pectx) {
359,326✔
483
            s->FreeFunc(t->pectx);
322,936✔
484
        }
322,936✔
485

486
        if (t->is_last)
359,326✔
487
            break;
137,381✔
488
        t++;
221,945✔
489
    }
221,945✔
490
    SCFreeAligned(list);
137,381✔
491
}
137,381✔
492

493
void PrefilterCleanupRuleGroup(const DetectEngineCtx *de_ctx, SigGroupHead *sgh)
494
{
2,260,573✔
495
    if (sgh->pkt_engines) {
2,260,573✔
496
        PrefilterFreeEngines(de_ctx, sgh->pkt_engines);
59,318✔
497
        sgh->pkt_engines = NULL;
59,318✔
498
    }
59,318✔
499
    if (sgh->payload_engines) {
2,260,573✔
500
        PrefilterFreeEngines(de_ctx, sgh->payload_engines);
28,828✔
501
        sgh->payload_engines = NULL;
28,828✔
502
    }
28,828✔
503
    if (sgh->tx_engines) {
2,260,573✔
504
        PrefilterFreeEngines(de_ctx, sgh->tx_engines);
44,285✔
505
        sgh->tx_engines = NULL;
44,285✔
506
    }
44,285✔
507
    if (sgh->frame_engines) {
2,260,573✔
508
        PrefilterFreeEngines(de_ctx, sgh->frame_engines);
4,832✔
509
        sgh->frame_engines = NULL;
4,832✔
510
    }
4,832✔
511
    if (sgh->post_rule_match_engines) {
2,260,573✔
512
        PrefilterFreeEngines(de_ctx, sgh->post_rule_match_engines);
118✔
513
        sgh->post_rule_match_engines = NULL;
118✔
514
    }
118✔
515
}
2,260,573✔
516

517
static int PrefilterSetupRuleGroupSortHelper(const void *a, const void *b)
518
{
629,651✔
519
    const PrefilterEngine *s0 = a;
629,651✔
520
    const PrefilterEngine *s1 = b;
629,651✔
521
    if (s1->ctx.tx_min_progress == s0->ctx.tx_min_progress) {
629,651✔
522
        if (s1->alproto == s0->alproto) {
406,179✔
523
            return s0->local_id > s1->local_id ? 1 : -1;
101,221✔
524
        } else {
304,958✔
525
            return s0->alproto > s1->alproto ? 1 : -1;
304,958✔
526
        }
304,958✔
527
    } else {
406,179✔
528
        return s0->ctx.tx_min_progress > s1->ctx.tx_min_progress ? 1 : -1;
223,472✔
529
    }
223,472✔
530
}
629,651✔
531

532
/** prefilter engine data for the non-prefilter engine for the prefilter API */
533
struct PrefilterNonPFDataSig {
534
    uint32_t sid : 30;
535
    uint32_t type : 2; /**< type for `value` field below: 0:alproto 1:dport 2:dsize */
536
    uint16_t value;
537
    /* since we have 2 more bytes available due to padding, we can add some additional
538
     * filters here. */
539
    union {
540
        struct {
541
            SignatureMask sig_mask;
542
        } pkt;
543
        struct {
544
            /* filter for frame type */
545
            uint8_t type;
546
        } frame;
547
        struct {
548
            uint8_t foo; // TODO unused
549
        } app;
550
    };
551
};
552

553
struct PrefilterNonPFData {
554
    uint32_t size;
555
    struct PrefilterNonPFDataSig array[];
556
};
557

558
struct PrefilterNonPFDataTx {
559
    uint32_t size;
560
    uint32_t array[];
561
};
562

563
/** \internal
564
 *  \brief wrapper for use in APIs */
565
static void PrefilterNonPFDataFree(void *data)
566
{
97,506✔
567
    SCFree(data);
97,506✔
568
}
97,506✔
569

570
static void PrefilterTxNonPF(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f,
571
        void *tx, const uint64_t tx_id, const AppLayerTxData *tx_data, const uint8_t flags)
572
{
55,881✔
573
    const struct PrefilterNonPFDataTx *data = (const struct PrefilterNonPFDataTx *)pectx;
55,881✔
574
    SCLogDebug("adding %u sids", data->size);
55,881✔
575
    PrefilterAddSids(&det_ctx->pmq, data->array, data->size);
55,881✔
576
}
55,881✔
577

578
#ifdef NONPF_PKT_STATS
579
static thread_local uint64_t prefilter_pkt_nonpf_called = 0;
580
static thread_local uint64_t prefilter_pkt_nonpf_mask_fail = 0;
581
static thread_local uint64_t prefilter_pkt_nonpf_alproto_fail = 0;
582
static thread_local uint64_t prefilter_pkt_nonpf_dsize_fail = 0;
583
static thread_local uint64_t prefilter_pkt_nonpf_dport_fail = 0;
584
static thread_local uint64_t prefilter_pkt_nonpf_sids = 0;
585
#define NONPF_PKT_STATS_INCR(s) (s)++
586
#else
587
#define NONPF_PKT_STATS_INCR(s)
588
#endif
589

590
void PrefilterPktNonPFStatsDump(void)
591
{
35,437✔
592
#ifdef NONPF_PKT_STATS
593
    SCLogDebug("prefilter non-pf: called:%" PRIu64 ", mask_fail:%" PRIu64 ", alproto fail:%" PRIu64
594
               ", dport fail:%" PRIu64 ", dsize fail:%" PRIu64 ", sids:%" PRIu64
595
               ", avg sids:%" PRIu64,
596
            prefilter_pkt_nonpf_called, prefilter_pkt_nonpf_mask_fail,
597
            prefilter_pkt_nonpf_alproto_fail, prefilter_pkt_nonpf_dport_fail,
598
            prefilter_pkt_nonpf_dsize_fail, prefilter_pkt_nonpf_sids,
599
            prefilter_pkt_nonpf_called ? prefilter_pkt_nonpf_sids / prefilter_pkt_nonpf_called : 0);
600
#endif
601
}
35,437✔
602

603
static void PrefilterPktNonPF(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
604
{
217,455✔
605
    const uint16_t alproto = p->flow ? p->flow->alproto : ALPROTO_UNKNOWN;
217,455✔
606
    const SignatureMask mask = p->sig_mask;
217,455✔
607
    const struct PrefilterNonPFData *data = (const struct PrefilterNonPFData *)pectx;
217,455✔
608
    SCLogDebug("adding %u sids", data->size);
217,455✔
609
    NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_called);
217,455✔
610
    for (uint32_t i = 0; i < data->size; i++) {
2,007,146✔
611
        const struct PrefilterNonPFDataSig *ds = &data->array[i];
1,789,691✔
612
        const SignatureMask rule_mask = ds->pkt.sig_mask;
1,789,691✔
613
        if ((rule_mask & mask) == rule_mask) {
1,789,691✔
614
            switch (ds->type) {
965,260✔
615
                case 0:
944,380✔
616
                    if (ds->value == ALPROTO_UNKNOWN || AppProtoEquals(ds->value, alproto)) {
944,380✔
617
                        const uint32_t sid = ds->sid;
442,334✔
618
                        PrefilterAddSids(&det_ctx->pmq, &sid, 1);
442,334✔
619
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_sids);
442,334✔
620
                    } else {
502,046✔
621
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_alproto_fail);
502,046✔
622
                    }
502,046✔
623
                    break;
944,380✔
624
                case 1:
19,230✔
625
                    if (ds->value == p->dp) {
19,230✔
626
                        const uint32_t sid = ds->sid;
3,486✔
627
                        PrefilterAddSids(&det_ctx->pmq, &sid, 1);
3,486✔
628
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_sids);
3,486✔
629
                    } else {
15,744✔
630
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_dport_fail);
15,744✔
631
                    }
15,744✔
632
                    break;
19,230✔
633
                case 2:
1,650✔
634
                    if (ds->value == p->payload_len) {
1,650✔
635
                        const uint32_t sid = ds->sid;
25✔
636
                        PrefilterAddSids(&det_ctx->pmq, &sid, 1);
25✔
637
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_sids);
25✔
638
                    } else {
1,625✔
639
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_dsize_fail);
1,625✔
640
                    }
1,625✔
641
                    break;
1,650✔
642
            }
965,260✔
643
        } else {
965,260✔
644
            NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_mask_fail);
824,431✔
645
        }
824,431✔
646
    }
1,789,691✔
647
}
217,455✔
648

649
static void PrefilterPktNonPFHookFlowStart(
650
        DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
UNCOV
651
{
×
UNCOV
652
    if (p->flowflags & (FLOW_PKT_TOSERVER_FIRST | FLOW_PKT_TOCLIENT_FIRST)) {
×
UNCOV
653
        PrefilterPktNonPF(det_ctx, p, pectx);
×
UNCOV
654
    }
×
UNCOV
655
}
×
656

657
/** \internal
658
 *  \brief engine to select the non-prefilter rules for frames
659
 *  Checks the alproto and type as well.
660
 *  Direction needs no checking as the rule groups are per direction. */
661
static void PrefilterFrameNonPF(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p,
662
        const Frames *frames, const Frame *frame)
663
{
82,673✔
664
    DEBUG_VALIDATE_BUG_ON(p->flow == NULL);
82,673✔
665
    const uint16_t alproto = p->flow->alproto;
82,673✔
666
    const struct PrefilterNonPFData *data = (const struct PrefilterNonPFData *)pectx;
82,673✔
667
    SCLogDebug("adding %u sids", data->size);
82,673✔
668
    for (uint32_t i = 0; i < data->size; i++) {
165,753✔
669
        const struct PrefilterNonPFDataSig *ds = &data->array[i];
83,080✔
670
        if (ds->frame.type == frame->type &&
83,080✔
671
                (ds->value == ALPROTO_UNKNOWN || AppProtoEquals(ds->value, alproto))) {
83,080✔
672
            const uint32_t sid = ds->sid;
27,346✔
673
            PrefilterAddSids(&det_ctx->pmq, &sid, 1);
27,346✔
674
        }
27,346✔
675
    }
83,080✔
676
}
82,673✔
677

678
/* helper funcs for the non prefilter names hash */
679

680
static uint32_t NonPFNamesHash(HashTable *h, void *data, uint16_t _len)
681
{
66,307✔
682
    const char *str = data;
66,307✔
683
    return StringHashDjb2((const uint8_t *)str, (uint16_t)strlen(str)) % h->array_size;
66,307✔
684
}
66,307✔
685

686
static char NonPFNamesCompare(void *data1, uint16_t _len1, void *data2, uint16_t len2)
687
{
×
688
    const char *s1 = data1;
×
689
    const char *s2 = data2;
×
690
    return StringHashCompareFunc(data1, (uint16_t)strlen(s1), data2, (uint16_t)strlen(s2));
×
691
}
×
692

693
static void NonPFNamesFree(void *data)
694
{
66,305✔
695
    SCFree(data);
66,305✔
696
}
66,305✔
697

698
/* helper funcs for assembling non-prefilter engines */
699

700
struct TxNonPFData {
701
    AppProto alproto;
702
    int dir;      /**< 0: toserver, 1: toclient */
703
    int progress; /**< progress state value to register at */
704
    int sig_list; /**< special handling: normally 0, but for special cases (app-layer-state,
705
                     app-layer-event) use the list id to create separate engines */
706
    uint32_t sigs_cnt;
707
    struct PrefilterNonPFDataSig *sigs;
708
    const char *engine_name; /**< pointer to name owned by DetectEngineCtx::non_pf_engine_names */
709
};
710

711
static uint32_t TxNonPFHash(HashListTable *h, void *data, uint16_t _len)
712
{
175,049✔
713
    struct TxNonPFData *d = data;
175,049✔
714
    return (d->alproto + d->progress + d->dir + d->sig_list) % h->array_size;
175,049✔
715
}
175,049✔
716

717
static char TxNonPFCompare(void *data1, uint16_t _len1, void *data2, uint16_t len2)
718
{
46,421✔
719
    struct TxNonPFData *d1 = data1;
46,421✔
720
    struct TxNonPFData *d2 = data2;
46,421✔
721
    return d1->alproto == d2->alproto && d1->progress == d2->progress && d1->dir == d2->dir &&
46,421✔
722
           d1->sig_list == d2->sig_list;
46,421✔
723
}
46,421✔
724

725
static void TxNonPFFree(void *data)
726
{
66,307✔
727
    struct TxNonPFData *d = data;
66,307✔
728
    SCFree(d->sigs);
66,307✔
729
    SCFree(d);
66,307✔
730
}
66,307✔
731

732
static int TxNonPFAddSig(DetectEngineCtx *de_ctx, HashListTable *tx_engines_hash,
733
        const AppProto alproto, const int dir, const int16_t progress, const int sig_list,
734
        const char *name, const Signature *s)
735
{
108,742✔
736
    const uint32_t max_sids = DetectEngineGetMaxSigId(de_ctx);
108,742✔
737

738
    struct TxNonPFData lookup = {
108,742✔
739
        .alproto = alproto,
108,742✔
740
        .dir = dir,
108,742✔
741
        .progress = progress,
108,742✔
742
        .sig_list = sig_list,
108,742✔
743
        .sigs_cnt = 0,
108,742✔
744
        .sigs = NULL,
108,742✔
745
        .engine_name = NULL,
108,742✔
746
    };
108,742✔
747
    struct TxNonPFData *e = HashListTableLookup(tx_engines_hash, &lookup, 0);
108,742✔
748
    if (e != NULL) {
108,742✔
749
        bool found = false;
42,435✔
750
        // avoid adding same sid multiple times
751
        for (uint32_t y = 0; y < e->sigs_cnt; y++) {
112,357✔
752
            if (e->sigs[y].sid == s->iid) {
76,356✔
753
                found = true;
6,434✔
754
                break;
6,434✔
755
            }
6,434✔
756
        }
76,356✔
757
        if (!found) {
42,435✔
758
            BUG_ON(e->sigs_cnt == max_sids);
36,001✔
759
            e->sigs[e->sigs_cnt].sid = s->iid;
36,001✔
760
            e->sigs[e->sigs_cnt].value = alproto;
36,001✔
761
            e->sigs_cnt++;
36,001✔
762
        }
36,001✔
763
        return 0;
42,435✔
764
    }
42,435✔
765

766
    struct TxNonPFData *add = SCCalloc(1, sizeof(*add));
66,307✔
767
    if (add == NULL) {
66,307✔
768
        return -1;
×
769
    }
×
770
    add->dir = dir;
66,307✔
771
    add->alproto = alproto;
66,307✔
772
    add->progress = progress;
66,307✔
773
    add->sig_list = sig_list;
66,307✔
774
    add->sigs = SCCalloc(max_sids, sizeof(struct PrefilterNonPFDataSig));
66,307✔
775
    if (add->sigs == NULL) {
66,307✔
776
        SCFree(add);
×
777
        return -1;
×
778
    }
×
779
    add->sigs_cnt = 0;
66,307✔
780
    add->sigs[add->sigs_cnt].sid = s->iid;
66,307✔
781
    add->sigs[add->sigs_cnt].value = alproto;
66,307✔
782
    add->sigs_cnt++;
66,307✔
783

784
    char engine_name[128];
66,307✔
785
    snprintf(engine_name, sizeof(engine_name), "%s:%s:non_pf:%s", AppProtoToString(alproto), name,
66,307✔
786
            dir == 0 ? "toserver" : "toclient");
66,307✔
787
    char *engine_name_heap = SCStrdup(engine_name);
66,307✔
788
    if (engine_name_heap == NULL) {
66,307✔
789
        SCFree(add->sigs);
×
790
        SCFree(add);
×
791
        return -1;
×
792
    }
×
793
    int result = HashTableAdd(
66,307✔
794
            de_ctx->non_pf_engine_names, engine_name_heap, (uint16_t)strlen(engine_name_heap));
66,307✔
795
    if (result != 0) {
66,307✔
796
        SCFree(add->sigs);
×
797
        SCFree(add);
×
798
        return -1;
×
799
    }
×
800

801
    add->engine_name = engine_name_heap;
66,307✔
802
    SCLogDebug("engine_name_heap %s", engine_name_heap);
66,307✔
803

804
    int ret = HashListTableAdd(tx_engines_hash, add, 0);
66,307✔
805
    if (ret != 0) {
66,307✔
806
        SCFree(add->sigs);
×
807
        SCFree(add);
×
808
        return -1;
×
809
    }
×
810

811
    return 0;
66,307✔
812
}
66,307✔
813

814
/** \internal
815
 *  \brief setup non-prefilter rules in special "non-prefilter" engines that are registered in the
816
 * prefilter logic.
817
 *
818
 *  \retval 0 ok
819
 *  \retval -1 error
820
 */
821
static int SetupNonPrefilter(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
822
{
90,293✔
823
    const uint32_t max_sids = DetectEngineGetMaxSigId(de_ctx);
90,293✔
824
    SCLogDebug("max_sids %u", max_sids);
90,293✔
825
    struct PrefilterNonPFDataSig *pkt_non_pf_array = SCCalloc(max_sids, sizeof(*pkt_non_pf_array));
90,293✔
826
    if (pkt_non_pf_array == NULL) {
90,293✔
827
        return -1;
×
828
    }
×
829
    uint32_t pkt_non_pf_array_size = 0;
90,293✔
830
    struct PrefilterNonPFDataSig *frame_non_pf_array =
90,293✔
831
            SCCalloc(max_sids, sizeof(*frame_non_pf_array));
90,293✔
832
    if (frame_non_pf_array == NULL) {
90,293✔
833
        SCFree(pkt_non_pf_array);
×
834
        return -1;
×
835
    }
×
836
    uint32_t frame_non_pf_array_size = 0;
90,293✔
837

838
    struct PrefilterNonPFDataSig *pkt_hook_flow_start_non_pf_array =
90,293✔
839
            SCCalloc(max_sids, sizeof(*pkt_hook_flow_start_non_pf_array));
90,293✔
840
    if (pkt_hook_flow_start_non_pf_array == NULL) {
90,293✔
841
        SCFree(pkt_non_pf_array);
×
842
        SCFree(frame_non_pf_array);
×
843
        return -1;
×
844
    }
×
845
    uint32_t pkt_hook_flow_start_non_pf_array_size = 0;
90,293✔
846
    SignatureMask pkt_hook_flow_start_mask = 0;
90,293✔
847
    bool pkt_hook_flow_start_mask_init = false;
90,293✔
848

849
    HashListTable *tx_engines_hash =
90,293✔
850
            HashListTableInit(256, TxNonPFHash, TxNonPFCompare, TxNonPFFree);
90,293✔
851
    if (tx_engines_hash == NULL) {
90,293✔
852
        SCFree(pkt_non_pf_array);
×
853
        SCFree(pkt_hook_flow_start_non_pf_array);
×
854
        SCFree(frame_non_pf_array);
×
855
        return -1;
×
856
    }
×
857

858
    if (de_ctx->non_pf_engine_names == NULL) {
90,293✔
859
        de_ctx->non_pf_engine_names =
19,748✔
860
                HashTableInit(512, NonPFNamesHash, NonPFNamesCompare, NonPFNamesFree);
19,748✔
861
        if (de_ctx->non_pf_engine_names == NULL) {
19,748✔
862
            SCFree(pkt_non_pf_array);
×
863
            SCFree(pkt_hook_flow_start_non_pf_array);
×
864
            SCFree(frame_non_pf_array);
×
865
            HashListTableFree(tx_engines_hash);
×
866
            return -1;
×
867
        }
×
868
    }
19,748✔
869

870
    SignatureMask pkt_mask = 0;
90,293✔
871
    bool pkt_mask_init = false;
90,293✔
872
#ifdef NONPF_PKT_STATS
873
    uint32_t nonpf_pkt_alproto = 0;
874
    uint32_t nonpf_pkt_dsize = 0;
875
    uint32_t nonpf_pkt_dport = 0;
876
#endif
877
    const int app_events_list_id = DetectBufferTypeGetByName("app-layer-events");
90,293✔
878
    SCLogDebug("app_events_list_id %d", app_events_list_id);
90,293✔
879
    const int app_state_list_id = DetectBufferTypeGetByName("app-layer-state");
90,293✔
880
    SCLogDebug("app_state_list_id %d", app_state_list_id);
90,293✔
881
    for (uint32_t sig = 0; sig < sgh->init->sig_cnt; sig++) {
613,933✔
882
        Signature *s = sgh->init->match_array[sig];
523,640✔
883
        if (s == NULL)
523,640✔
884
            continue;
×
885
        SCLogDebug("checking sid %u for non-prefilter", s->id);
523,640✔
886
        if (s->init_data->mpm_sm != NULL && (s->flags & SIG_FLAG_MPM_NEG) == 0)
523,640✔
887
            continue;
167,185✔
888
        if (s->init_data->prefilter_sm != NULL)
356,455✔
889
            continue;
41,133✔
890
        if ((s->flags & (SIG_FLAG_PREFILTER | SIG_FLAG_MPM_NEG)) == SIG_FLAG_PREFILTER)
315,322✔
891
            continue;
×
892
        SCLogDebug("setting up sid %u for non-prefilter", s->id);
315,322✔
893

894
        uint8_t frame_type = 0; /**< only a single type per rule */
315,322✔
895
        bool tx_non_pf = false;
315,322✔
896
        bool frame_non_pf = false;
315,322✔
897
        bool pkt_non_pf = false;
315,322✔
898

899
        if (s->init_data->hook.type == SIGNATURE_HOOK_TYPE_PKT &&
315,322✔
900
                s->init_data->hook.t.pkt.ph == SIGNATURE_HOOK_PKT_FLOW_START) {
315,322✔
901
            // TODO code duplication with regular pkt case below
902

903
            /* for pkt non prefilter, we have some space in the structure,
904
             * so we can squeeze another filter */
UNCOV
905
            uint8_t type;
×
UNCOV
906
            uint16_t value;
×
UNCOV
907
            if ((s->flags & SIG_FLAG_DSIZE) && s->dsize_mode == DETECT_UINT_EQ) {
×
UNCOV
908
                SCLogDebug("dsize extra match");
×
UNCOV
909
                type = 2;
×
UNCOV
910
                value = s->dsize_low;
×
UNCOV
911
            } else if (s->dp != NULL && s->dp->next == NULL && s->dp->port == s->dp->port2) {
×
UNCOV
912
                type = 1;
×
UNCOV
913
                value = s->dp->port;
×
UNCOV
914
            } else {
×
UNCOV
915
                type = 0;
×
UNCOV
916
                value = s->alproto;
×
UNCOV
917
            }
×
UNCOV
918
            pkt_hook_flow_start_non_pf_array[pkt_hook_flow_start_non_pf_array_size].sid = s->iid;
×
UNCOV
919
            pkt_hook_flow_start_non_pf_array[pkt_hook_flow_start_non_pf_array_size].value = value;
×
UNCOV
920
            pkt_hook_flow_start_non_pf_array[pkt_hook_flow_start_non_pf_array_size].type = type;
×
UNCOV
921
            pkt_hook_flow_start_non_pf_array[pkt_hook_flow_start_non_pf_array_size].pkt.sig_mask =
×
UNCOV
922
                    s->mask;
×
UNCOV
923
            pkt_hook_flow_start_non_pf_array_size++;
×
924

UNCOV
925
            if (pkt_hook_flow_start_mask_init) {
×
UNCOV
926
                pkt_hook_flow_start_mask &= s->mask;
×
UNCOV
927
            } else {
×
UNCOV
928
                pkt_hook_flow_start_mask = s->mask;
×
UNCOV
929
                pkt_hook_flow_start_mask_init = true;
×
UNCOV
930
            }
×
931

UNCOV
932
            SCLogDebug("flow_start hook");
×
UNCOV
933
            continue; // done for this sig
×
UNCOV
934
        }
×
935

936
        for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
411,261✔
937
            const int list_id = s->init_data->buffers[x].id;
95,939✔
938
            const DetectBufferType *buf = DetectEngineBufferTypeGetById(de_ctx, list_id);
95,939✔
939
            if (buf == NULL)
95,939✔
940
                continue;
×
941
            /* for now, exclude app-layer-events, as they are not tied to a specific
942
             * progress value like other keywords. */
943
            SCLogDebug("list_id %d buf %p", list_id, buf);
95,939✔
944
            if (list_id == app_events_list_id)
95,939✔
945
                continue;
36,168✔
946
            if (buf->packet) {
59,771✔
947
                SCLogDebug("packet buf");
1,527✔
948
                /* packet is handled below */
949
                pkt_non_pf = true;
1,527✔
950
            } else if (buf->frame) {
58,244✔
951
                for (DetectEngineFrameInspectionEngine *f = de_ctx->frame_inspect_engines;
390✔
952
                        f != NULL; f = f->next) {
1,408✔
953
                    if (!((((s->flags & SIG_FLAG_TOSERVER) != 0 && f->dir == 0) ||
1,408✔
954
                                  ((s->flags & SIG_FLAG_TOCLIENT) != 0 && f->dir == 1)) &&
1,408✔
955
                                list_id == (int)f->sm_list &&
1,408✔
956
                                AppProtoEquals(s->alproto, f->alproto)))
1,408✔
957
                        continue;
1,018✔
958

959
                    SCLogDebug("frame '%s' type %u", buf->name, f->type);
390✔
960
                    frame_type = f->type;
390✔
961
                    frame_non_pf = true;
390✔
962

963
                    frame_non_pf_array[frame_non_pf_array_size].sid = s->iid;
390✔
964
                    frame_non_pf_array[frame_non_pf_array_size].value = s->alproto;
390✔
965
                    frame_non_pf_array[frame_non_pf_array_size].frame.type = frame_type;
390✔
966
                    frame_non_pf_array_size++;
390✔
967
                    break;
390✔
968
                }
1,408✔
969

970
            } else {
57,854✔
971
                SCLogDebug("x %u list_id %d", x, list_id);
57,854✔
972
                for (DetectEngineAppInspectionEngine *app = de_ctx->app_inspect_engines;
57,854✔
973
                        app != NULL; app = app->next) {
44,156,311✔
974
                    SCLogDebug("app %p proto %s list_d %d sig dir %0x", app,
44,098,457✔
975
                            AppProtoToString(app->alproto), app->sm_list,
44,098,457✔
976
                            s->flags & (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT));
44,098,457✔
977

978
                    /* skip if:
979
                     * - not in our dir
980
                     * - not our list
981
                     * - app proto mismatch. Both sig and app can have proto or unknown */
982
                    if (!((((s->flags & SIG_FLAG_TOSERVER) != 0 && app->dir == 0) ||
44,098,457✔
983
                                  ((s->flags & SIG_FLAG_TOCLIENT) != 0 && app->dir == 1)) &&
44,098,457✔
984
                                list_id == (int)app->sm_list &&
44,098,457✔
985
                                (s->alproto == ALPROTO_UNKNOWN || app->alproto == ALPROTO_UNKNOWN ||
44,098,457✔
986
                                        AppProtoEquals(s->alproto, app->alproto))))
165,569✔
987
                        continue;
43,989,722✔
988

989
                    int sig_list = 0;
108,735✔
990
                    if (list_id == app_state_list_id)
108,735✔
991
                        sig_list = app_state_list_id;
3✔
992
                    if (TxNonPFAddSig(de_ctx, tx_engines_hash, app->alproto, app->dir,
108,735✔
993
                                app->progress, sig_list, buf->name, s) != 0) {
108,735✔
994
                        goto error;
×
995
                    }
×
996
                    tx_non_pf = true;
108,735✔
997
                }
108,735✔
998
            }
57,854✔
999
        }
59,771✔
1000
        /* handle hook only rules */
1001
        if (!tx_non_pf && s->init_data->hook.type == SIGNATURE_HOOK_TYPE_APP) {
315,322✔
1002
            const int dir = (s->flags & SIG_FLAG_TOSERVER) ? 0 : 1;
7✔
1003
            const char *pname = AppLayerParserGetStateNameById(IPPROTO_TCP, // TODO
7✔
1004
                    s->alproto, s->init_data->hook.t.app.app_progress,
7✔
1005
                    dir == 0 ? STREAM_TOSERVER : STREAM_TOCLIENT);
7✔
1006

1007
            if (TxNonPFAddSig(de_ctx, tx_engines_hash, s->alproto, dir,
7✔
1008
                        (int16_t)s->init_data->hook.t.app.app_progress, s->init_data->hook.sm_list,
7✔
1009
                        pname, s) != 0) {
7✔
1010
                goto error;
×
1011
            }
×
1012
            tx_non_pf = true;
7✔
1013
        }
7✔
1014
        /* mark as prefiltered as the sig is now part of a engine */
1015
        // s->flags |= SIG_FLAG_PREFILTER;
1016
        //  TODO doesn't work for sigs that are in multiple sgh's
1017

1018
        /* default to pkt if there was no tx or frame match */
1019
        if (!(tx_non_pf || frame_non_pf)) {
315,322✔
1020
            if (!pkt_non_pf) {
259,359✔
1021
                SCLogDebug("not frame, not tx, so pkt");
258,183✔
1022
            }
258,183✔
1023
            pkt_non_pf = true;
259,359✔
1024
        }
259,359✔
1025

1026
        SCLogDebug("setting up sid %u for non-prefilter: %s", s->id,
315,322✔
1027
                tx_non_pf ? "tx engine" : (frame_non_pf ? "frame engine" : "pkt engine"));
315,322✔
1028

1029
        if (pkt_non_pf) {
315,322✔
1030
            /* for pkt non prefilter, we have some space in the structure,
1031
             * so we can squeeze another filter */
1032
            uint8_t type;
259,364✔
1033
            uint16_t value;
259,364✔
1034
            if ((s->flags & SIG_FLAG_DSIZE) && s->dsize_mode == DETECT_UINT_EQ) {
259,364✔
1035
                SCLogDebug("dsize extra match");
562✔
1036
                type = 2;
562✔
1037
                value = s->dsize_low;
562✔
1038
#ifdef NONPF_PKT_STATS
1039
                nonpf_pkt_dsize++;
1040
#endif
1041
            } else if (s->dp != NULL && s->dp->next == NULL && s->dp->port == s->dp->port2) {
258,802✔
1042
                type = 1;
38,999✔
1043
                value = s->dp->port;
38,999✔
1044
#ifdef NONPF_PKT_STATS
1045
                nonpf_pkt_dport++;
1046
#endif
1047
            } else {
219,803✔
1048
                type = 0;
219,803✔
1049
                value = s->alproto;
219,803✔
1050
#ifdef NONPF_PKT_STATS
1051
                nonpf_pkt_alproto++;
1052
#endif
1053
            }
219,803✔
1054

1055
            pkt_non_pf_array[pkt_non_pf_array_size].sid = s->iid;
259,364✔
1056
            pkt_non_pf_array[pkt_non_pf_array_size].value = value;
259,364✔
1057
            pkt_non_pf_array[pkt_non_pf_array_size].type = type;
259,364✔
1058
            pkt_non_pf_array[pkt_non_pf_array_size].pkt.sig_mask = s->mask;
259,364✔
1059
            pkt_non_pf_array_size++;
259,364✔
1060

1061
            if (pkt_mask_init) {
259,364✔
1062
                pkt_mask &= s->mask;
207,469✔
1063
            } else {
207,469✔
1064
                pkt_mask = s->mask;
51,895✔
1065
                pkt_mask_init = true;
51,895✔
1066
            }
51,895✔
1067
        }
259,364✔
1068
    }
315,322✔
1069

1070
    /* for each unique sig set, add an engine */
1071
    for (HashListTableBucket *b = HashListTableGetListHead(tx_engines_hash); b != NULL;
156,600✔
1072
            b = HashListTableGetListNext(b)) {
90,293✔
1073
        struct TxNonPFData *t = HashListTableGetListData(b);
66,307✔
1074
        SCLogDebug("%s engine for %s hook %d has %u non-pf sigs",
66,307✔
1075
                t->dir == 0 ? "toserver" : "toclient", AppProtoToString(t->alproto), t->progress,
66,307✔
1076
                t->sigs_cnt);
66,307✔
1077

1078
        if (((sgh->init->direction & SIG_FLAG_TOSERVER) && t->dir == 1) ||
66,307✔
1079
                ((sgh->init->direction & SIG_FLAG_TOCLIENT) && t->dir == 0)) {
66,307✔
1080
            SCLogDebug("skipped");
21,057✔
1081
            continue;
21,057✔
1082
        }
21,057✔
1083

1084
        /* register special progress value to indicate we need to run it all the time */
1085
        int engine_progress = t->progress;
45,250✔
1086
        if (t->sig_list == app_state_list_id) {
45,250✔
1087
            SCLogDebug("engine %s for state list", t->engine_name);
3✔
1088
            engine_progress = -1;
3✔
1089
        }
3✔
1090

1091
        struct PrefilterNonPFDataTx *data =
45,250✔
1092
                SCCalloc(1, sizeof(*data) + t->sigs_cnt * sizeof(data->array[0]));
45,250✔
1093
        if (data == NULL)
45,250✔
1094
            goto error;
×
1095
        data->size = t->sigs_cnt;
45,250✔
1096
        for (uint32_t i = 0; i < t->sigs_cnt; i++) {
116,494✔
1097
            data->array[i] = t->sigs[i].sid;
71,244✔
1098
        }
71,244✔
1099
        if (PrefilterAppendTxEngine(de_ctx, sgh, PrefilterTxNonPF, t->alproto, engine_progress,
45,250✔
1100
                    (void *)data, PrefilterNonPFDataFree, t->engine_name) < 0) {
45,250✔
1101
            SCFree(data);
×
1102
            goto error;
×
1103
        }
×
1104
    }
45,250✔
1105
    HashListTableFree(tx_engines_hash);
90,293✔
1106
    tx_engines_hash = NULL;
90,293✔
1107

1108
    if (pkt_non_pf_array_size) {
90,293✔
1109
        struct PrefilterNonPFData *data =
51,895✔
1110
                SCCalloc(1, sizeof(*data) + pkt_non_pf_array_size * sizeof(data->array[0]));
51,895✔
1111
        if (data == NULL)
51,895✔
1112
            goto error;
×
1113
        data->size = pkt_non_pf_array_size;
51,895✔
1114
        memcpy((uint8_t *)&data->array, pkt_non_pf_array,
51,895✔
1115
                pkt_non_pf_array_size * sizeof(data->array[0]));
51,895✔
1116
        enum SignatureHookPkt hook = SIGNATURE_HOOK_PKT_NOT_SET; // TODO review
51,895✔
1117
        if (PrefilterAppendEngine(de_ctx, sgh, PrefilterPktNonPF, pkt_mask, hook, (void *)data,
51,895✔
1118
                    PrefilterNonPFDataFree, "packet:non_pf") < 0) {
51,895✔
1119
            SCFree(data);
×
1120
            goto error;
×
1121
        }
×
1122
    }
51,895✔
1123
    if (pkt_hook_flow_start_non_pf_array_size) {
90,293✔
UNCOV
1124
        struct PrefilterNonPFData *data = SCCalloc(
×
UNCOV
1125
                1, sizeof(*data) + pkt_hook_flow_start_non_pf_array_size * sizeof(data->array[0]));
×
UNCOV
1126
        if (data == NULL)
×
1127
            goto error;
×
UNCOV
1128
        data->size = pkt_hook_flow_start_non_pf_array_size;
×
UNCOV
1129
        memcpy((uint8_t *)&data->array, pkt_hook_flow_start_non_pf_array,
×
UNCOV
1130
                pkt_hook_flow_start_non_pf_array_size * sizeof(data->array[0]));
×
UNCOV
1131
        SCLogDebug("packet:flow_start:non_pf added with %u rules", data->size);
×
UNCOV
1132
        enum SignatureHookPkt hook = SIGNATURE_HOOK_PKT_FLOW_START;
×
UNCOV
1133
        if (PrefilterAppendEngine(de_ctx, sgh,
×
UNCOV
1134
                    PrefilterPktNonPFHookFlowStart, // TODO no longer needed to have a dedicated
×
1135
                                                    // callback
UNCOV
1136
                    pkt_hook_flow_start_mask, hook, (void *)data, PrefilterNonPFDataFree,
×
UNCOV
1137
                    "packet:flow_start:non_pf") < 0) {
×
1138
            SCFree(data);
×
1139
            goto error;
×
1140
        }
×
UNCOV
1141
    }
×
1142
    if (frame_non_pf_array_size) {
90,293✔
1143
        SCLogDebug("%u frame non-pf sigs", frame_non_pf_array_size);
363✔
1144
        struct PrefilterNonPFData *data =
363✔
1145
                SCCalloc(1, sizeof(*data) + frame_non_pf_array_size * sizeof(data->array[0]));
363✔
1146
        if (data == NULL)
363✔
1147
            goto error;
×
1148
        data->size = frame_non_pf_array_size;
363✔
1149
        memcpy((uint8_t *)&data->array, frame_non_pf_array,
363✔
1150
                frame_non_pf_array_size * sizeof(data->array[0]));
363✔
1151
        if (PrefilterAppendFrameEngine(de_ctx, sgh, PrefilterFrameNonPF, ALPROTO_UNKNOWN,
363✔
1152
                    FRAME_ANY_TYPE, (void *)data, PrefilterNonPFDataFree, "frame:non_pf") < 0) {
363✔
1153
            SCFree(data);
×
1154
            goto error;
×
1155
        }
×
1156
    }
363✔
1157

1158
    SCFree(pkt_hook_flow_start_non_pf_array);
90,293✔
1159
    pkt_hook_flow_start_non_pf_array = NULL;
90,293✔
1160
    SCFree(pkt_non_pf_array);
90,293✔
1161
    pkt_non_pf_array = NULL;
90,293✔
1162
    SCFree(frame_non_pf_array);
90,293✔
1163
    frame_non_pf_array = NULL;
90,293✔
1164
    return 0;
90,293✔
1165

1166
error:
×
1167
    if (tx_engines_hash) {
×
1168
        HashListTableFree(tx_engines_hash);
×
1169
    }
×
1170
    SCFree(pkt_hook_flow_start_non_pf_array);
×
1171
    SCFree(pkt_non_pf_array);
×
1172
    SCFree(frame_non_pf_array);
×
1173
    return -1;
×
1174
}
90,293✔
1175

1176
int PrefilterSetupRuleGroup(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
1177
{
90,293✔
1178
    int r = PatternMatchPrepareGroup(de_ctx, sgh);
90,293✔
1179
    if (r != 0) {
90,293✔
1180
        FatalError("failed to set up pattern matching");
×
1181
    }
×
1182

1183
    /* set up engines if needed - when prefilter is set to auto we run
1184
     * all engines, otherwise only those that have been forced by the
1185
     * prefilter keyword. */
1186
    const enum DetectEnginePrefilterSetting setting = de_ctx->prefilter_setting;
90,293✔
1187
    for (int i = 0; i < DETECT_TBLSIZE; i++) {
44,333,863✔
1188
        if (sigmatch_table[i].SetupPrefilter != NULL &&
44,243,570✔
1189
                (setting == DETECT_PREFILTER_AUTO || de_ctx->sm_types_prefilter[i])) {
44,243,570✔
1190
            sigmatch_table[i].SetupPrefilter(de_ctx, sgh);
45,157✔
1191
        }
45,157✔
1192
    }
44,243,570✔
1193

1194
    if (SetupNonPrefilter(de_ctx, sgh) != 0) {
90,293✔
1195
        return -1;
×
1196
    }
×
1197

1198
    /* we have lists of engines in sgh->init now. Lets setup the
1199
     * match arrays */
1200
    PrefilterEngineList *el;
90,293✔
1201
    if (sgh->init->pkt_engines != NULL) {
90,293✔
1202
        uint32_t cnt = 0;
59,318✔
1203
        for (el = sgh->init->pkt_engines ; el != NULL; el = el->next) {
148,001✔
1204
            cnt++;
88,683✔
1205
        }
88,683✔
1206
        sgh->pkt_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
59,318✔
1207
        if (sgh->pkt_engines == NULL) {
59,318✔
1208
            return -1;
×
1209
        }
×
1210
        memset(sgh->pkt_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
59,318✔
1211

1212
        PrefilterEngine *e = sgh->pkt_engines;
59,318✔
1213
        for (el = sgh->init->pkt_engines ; el != NULL; el = el->next) {
148,001✔
1214
            e->local_id = el->id;
88,683✔
1215
            e->cb.Prefilter = el->Prefilter;
88,683✔
1216
            e->ctx.pkt.mask = el->pkt_mask;
88,683✔
1217
            // TODO right now we represent the hook in a u8 in the prefilter engine for space
1218
            // reasons.
1219
            BUG_ON(el->pkt_hook >= 8);
88,683✔
1220
            e->ctx.pkt.hook = (uint8_t)el->pkt_hook;
88,683✔
1221
            e->pectx = el->pectx;
88,683✔
1222
            el->pectx = NULL; // e now owns the ctx
88,683✔
1223
            e->gid = el->gid;
88,683✔
1224
            if (el->next == NULL) {
88,683✔
1225
                e->is_last = true;
59,318✔
1226
            }
59,318✔
1227
            e++;
88,683✔
1228
        }
88,683✔
1229
    }
59,318✔
1230
    if (sgh->init->payload_engines != NULL) {
90,293✔
1231
        uint32_t cnt = 0;
28,828✔
1232
        for (el = sgh->init->payload_engines ; el != NULL; el = el->next) {
65,218✔
1233
            cnt++;
36,390✔
1234
        }
36,390✔
1235
        sgh->payload_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
28,828✔
1236
        if (sgh->payload_engines == NULL) {
28,828✔
1237
            return -1;
×
1238
        }
×
1239
        memset(sgh->payload_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
28,828✔
1240

1241
        PrefilterEngine *e = sgh->payload_engines;
28,828✔
1242
        for (el = sgh->init->payload_engines ; el != NULL; el = el->next) {
65,218✔
1243
            e->local_id = el->id;
36,390✔
1244
            e->cb.Prefilter = el->Prefilter;
36,390✔
1245
            e->ctx.pkt.mask = el->pkt_mask;
36,390✔
1246
            // TODO right now we represent the hook in a u8 in the prefilter engine for space
1247
            // reasons.
1248
            BUG_ON(el->pkt_hook >= 8);
36,390✔
1249
            e->ctx.pkt.hook = (uint8_t)el->pkt_hook;
36,390✔
1250
            e->pectx = el->pectx;
36,390✔
1251
            el->pectx = NULL; // e now owns the ctx
36,390✔
1252
            e->gid = el->gid;
36,390✔
1253
            if (el->next == NULL) {
36,390✔
1254
                e->is_last = true;
28,828✔
1255
            }
28,828✔
1256
            e++;
36,390✔
1257
        }
36,390✔
1258
    }
28,828✔
1259
    if (sgh->init->tx_engines != NULL) {
90,293✔
1260
        uint32_t cnt = 0;
44,286✔
1261
        for (el = sgh->init->tx_engines ; el != NULL; el = el->next) {
270,841✔
1262
            cnt++;
226,555✔
1263
        }
226,555✔
1264
        sgh->tx_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
44,286✔
1265
        if (sgh->tx_engines == NULL) {
44,286✔
1266
            return -1;
×
1267
        }
×
1268
        memset(sgh->tx_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
44,286✔
1269

1270
        uint16_t local_id = 0;
44,286✔
1271
        PrefilterEngine *e = sgh->tx_engines;
44,286✔
1272
        for (el = sgh->init->tx_engines ; el != NULL; el = el->next) {
270,841✔
1273
            e->local_id = local_id++;
226,555✔
1274
            e->alproto = el->alproto;
226,555✔
1275
            e->ctx.tx_min_progress = el->tx_min_progress;
226,555✔
1276
            e->cb.PrefilterTx = el->PrefilterTx;
226,555✔
1277
            e->pectx = el->pectx;
226,555✔
1278
            el->pectx = NULL; // e now owns the ctx
226,555✔
1279
            e->gid = el->gid;
226,555✔
1280
            e++;
226,555✔
1281
        }
226,555✔
1282

1283
        /* sort by tx_min_progress, then alproto, then local_id */
1284
        qsort(sgh->tx_engines, local_id, sizeof(PrefilterEngine),
44,286✔
1285
                PrefilterSetupRuleGroupSortHelper);
44,286✔
1286
        sgh->tx_engines[local_id - 1].is_last = true;
44,286✔
1287
        sgh->tx_engines[local_id - 1].is_last_for_progress = true;
44,286✔
1288

1289
        PrefilterEngine *engine;
44,286✔
1290

1291
        /* per alproto to set is_last_for_progress per alproto because the inspect
1292
         * loop skips over engines that are not the correct alproto */
1293
        for (AppProto a = ALPROTO_FAILED + 1; a < g_alproto_max; a++) {
1,727,154✔
1294
            int last_tx_progress = 0;
1,682,868✔
1295
            bool last_tx_progress_set = false;
1,682,868✔
1296
            PrefilterEngine *prev_engine = NULL;
1,682,868✔
1297
            engine = sgh->tx_engines;
1,682,868✔
1298
            do {
8,609,090✔
1299
                if (engine->ctx.tx_min_progress != -1)
8,609,090✔
1300
                    BUG_ON(engine->ctx.tx_min_progress < last_tx_progress);
8,609,090✔
1301
                if (engine->alproto == a) {
8,609,090✔
1302
                    if (last_tx_progress_set && engine->ctx.tx_min_progress > last_tx_progress) {
226,552✔
1303
                        if (prev_engine) {
10,972✔
1304
                            prev_engine->is_last_for_progress = true;
10,972✔
1305
                        }
10,972✔
1306
                    }
10,972✔
1307

1308
                    last_tx_progress_set = true;
226,552✔
1309
                    prev_engine = engine;
226,552✔
1310
                } else {
8,382,538✔
1311
                    if (prev_engine) {
8,382,538✔
1312
                        prev_engine->is_last_for_progress = true;
668,967✔
1313
                    }
668,967✔
1314
                }
8,382,538✔
1315
                last_tx_progress = engine->ctx.tx_min_progress;
8,609,090✔
1316
                if (engine->is_last)
8,609,090✔
1317
                    break;
1,682,868✔
1318
                engine++;
6,926,222✔
1319
            } while (1);
6,926,222✔
1320
        }
1,682,868✔
1321
#ifdef DEBUG
1322
        SCLogDebug("sgh %p", sgh);
1323
        engine = sgh->tx_engines;
1324
        do {
1325
            SCLogDebug("engine: gid %u alproto %s tx_min_progress %d is_last %s "
1326
                       "is_last_for_progress %s",
1327
                    engine->gid, AppProtoToString(engine->alproto), engine->ctx.tx_min_progress,
1328
                    engine->is_last ? "true" : "false",
1329
                    engine->is_last_for_progress ? "true" : "false");
1330
            if (engine->is_last)
1331
                break;
1332
            engine++;
1333
        } while (1);
1334
#endif
1335
    }
44,286✔
1336
    if (sgh->init->frame_engines != NULL) {
90,293✔
1337
        uint32_t cnt = 0;
4,832✔
1338
        for (el = sgh->init->frame_engines; el != NULL; el = el->next) {
12,414✔
1339
            cnt++;
7,582✔
1340
        }
7,582✔
1341
        sgh->frame_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
4,832✔
1342
        if (sgh->frame_engines == NULL) {
4,832✔
1343
            return -1;
×
1344
        }
×
1345
        memset(sgh->frame_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
4,832✔
1346

1347
        PrefilterEngine *e = sgh->frame_engines;
4,832✔
1348
        for (el = sgh->init->frame_engines; el != NULL; el = el->next) {
12,414✔
1349
            e->local_id = el->id;
7,582✔
1350
            e->ctx.frame_type = el->frame_type;
7,582✔
1351
            e->cb.PrefilterFrame = el->PrefilterFrame;
7,582✔
1352
            e->alproto = el->alproto;
7,582✔
1353
            e->pectx = el->pectx;
7,582✔
1354
            el->pectx = NULL; // e now owns the ctx
7,582✔
1355
            e->gid = el->gid;
7,582✔
1356
            if (el->next == NULL) {
7,582✔
1357
                e->is_last = true;
4,832✔
1358
            }
4,832✔
1359
            e++;
7,582✔
1360
        }
7,582✔
1361
    }
4,832✔
1362

1363
    if (sgh->init->post_rule_match_engines != NULL) {
90,293✔
1364
        uint32_t cnt = 0;
118✔
1365
        for (el = sgh->init->post_rule_match_engines; el != NULL; el = el->next) {
236✔
1366
            cnt++;
118✔
1367
        }
118✔
1368
        sgh->post_rule_match_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
118✔
1369
        if (sgh->post_rule_match_engines == NULL) {
118✔
1370
            return -1;
×
1371
        }
×
1372
        memset(sgh->post_rule_match_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
118✔
1373

1374
        uint16_t local_id = 0;
118✔
1375
        PrefilterEngine *e = sgh->post_rule_match_engines;
118✔
1376
        for (el = sgh->init->post_rule_match_engines; el != NULL; el = el->next) {
236✔
1377
            e->local_id = local_id++;
118✔
1378
            e->cb.PrefilterPostRule = el->PrefilterPostRule;
118✔
1379
            e->pectx = el->pectx;
118✔
1380
            el->pectx = NULL; // e now owns the ctx
118✔
1381
            e->gid = el->gid;
118✔
1382
            e->is_last = (el->next == NULL);
118✔
1383
            e++;
118✔
1384
        }
118✔
1385
        SCLogDebug("sgh %p max local_id %u", sgh, local_id);
118✔
1386
    }
118✔
1387

1388
    return 0;
90,293✔
1389
}
90,293✔
1390

1391
/* hash table for assigning a unique id to each engine type. */
1392

1393
static uint32_t PrefilterStoreHashFunc(HashListTable *ht, void *data, uint16_t datalen)
1394
{
412,252✔
1395
    PrefilterStore *ctx = data;
412,252✔
1396

1397
    uint32_t hash = (uint32_t)strlen(ctx->name);
412,252✔
1398

1399
    for (size_t u = 0; u < strlen(ctx->name); u++) {
7,339,852✔
1400
        hash += ctx->name[u];
6,927,600✔
1401
    }
6,927,600✔
1402

1403
    hash %= ht->array_size;
412,252✔
1404
    return hash;
412,252✔
1405
}
412,252✔
1406

1407
static char PrefilterStoreCompareFunc(void *data1, uint16_t len1,
1408
                                      void *data2, uint16_t len2)
1409
{
311,018✔
1410
    PrefilterStore *ctx1 = data1;
311,018✔
1411
    PrefilterStore *ctx2 = data2;
311,018✔
1412
    return (strcmp(ctx1->name, ctx2->name) == 0);
311,018✔
1413
}
311,018✔
1414

1415
static void PrefilterStoreFreeFunc(void *ptr)
1416
{
52,922✔
1417
    SCFree(ptr);
52,922✔
1418
}
52,922✔
1419

1420
void PrefilterDeinit(DetectEngineCtx *de_ctx)
1421
{
35,466✔
1422
    if (de_ctx->prefilter_hash_table != NULL) {
35,466✔
1423
        HashListTableFree(de_ctx->prefilter_hash_table);
35,466✔
1424
    }
35,466✔
1425
}
35,466✔
1426

1427
void PrefilterInit(DetectEngineCtx *de_ctx)
1428
{
35,469✔
1429
    BUG_ON(de_ctx->prefilter_hash_table != NULL);
35,469✔
1430

1431
    de_ctx->prefilter_hash_table = HashListTableInit(256,
35,469✔
1432
            PrefilterStoreHashFunc,
35,469✔
1433
            PrefilterStoreCompareFunc,
35,469✔
1434
            PrefilterStoreFreeFunc);
35,469✔
1435
    BUG_ON(de_ctx->prefilter_hash_table == NULL);
35,469✔
1436
}
35,469✔
1437

1438
static int PrefilterStoreGetId(DetectEngineCtx *de_ctx,
1439
        const char *name, void (*FreeFunc)(void *))
1440
{
359,328✔
1441
    PrefilterStore ctx = { name, FreeFunc, 0 };
359,328✔
1442

1443
    BUG_ON(de_ctx->prefilter_hash_table == NULL);
359,328✔
1444

1445
    SCLogDebug("looking up %s", name);
359,328✔
1446

1447
    PrefilterStore *rctx = HashListTableLookup(de_ctx->prefilter_hash_table, (void *)&ctx, 0);
359,328✔
1448
    if (rctx != NULL) {
359,328✔
1449
        return rctx->id;
306,404✔
1450
    }
306,404✔
1451

1452
    PrefilterStore *actx = SCCalloc(1, sizeof(*actx));
52,924✔
1453
    if (actx == NULL) {
52,924✔
1454
        return -1;
×
1455
    }
×
1456

1457
    actx->name = name;
52,924✔
1458
    actx->FreeFunc = FreeFunc;
52,924✔
1459
    actx->id = de_ctx->prefilter_id++;
52,924✔
1460
    SCLogDebug("prefilter engine %s has profile id %u", actx->name, actx->id);
52,924✔
1461

1462
    int ret = HashListTableAdd(de_ctx->prefilter_hash_table, actx, 0);
52,924✔
1463
    if (ret != 0) {
52,924✔
1464
        SCFree(actx);
×
1465
        return -1;
×
1466
    }
×
1467

1468
    int r = actx->id;
52,924✔
1469
    return r;
52,924✔
1470
}
52,924✔
1471

1472
/** \warning slow */
1473
static const PrefilterStore *PrefilterStoreGetStore(const DetectEngineCtx *de_ctx,
1474
        const uint32_t id)
1475
{
359,326✔
1476

1477
    const PrefilterStore *store = NULL;
359,326✔
1478
    if (de_ctx->prefilter_hash_table != NULL) {
359,326✔
1479
        HashListTableBucket *hb = HashListTableGetListHead(de_ctx->prefilter_hash_table);
359,326✔
1480
        for ( ; hb != NULL; hb = HashListTableGetListNext(hb)) {
1,466,754✔
1481
            PrefilterStore *ctx = HashListTableGetListData(hb);
1,466,754✔
1482
            if (ctx->id == id) {
1,466,754✔
1483
                store = ctx;
359,326✔
1484
                break;
359,326✔
1485
            }
359,326✔
1486
        }
1,466,754✔
1487
    }
359,326✔
1488
    return store;
359,326✔
1489
}
359,326✔
1490

1491
#include "util-print.h"
1492

1493
typedef struct PrefilterMpmCtx {
1494
    int list_id;
1495
    union {
1496
        InspectionBufferGetDataPtr GetData;
1497
        InspectionSingleBufferGetDataPtr GetDataSingle;
1498
    };
1499
    const MpmCtx *mpm_ctx;
1500
    const DetectEngineTransforms *transforms;
1501
} PrefilterMpmCtx;
1502

1503
/** \brief Generic Mpm prefilter callback for simple InspectionSingleBufferGetDataPtr
1504
 *
1505
 *  \param det_ctx detection engine thread ctx
1506
 *  \param p packet to inspect
1507
 *  \param f flow to inspect
1508
 *  \param txv tx to inspect
1509
 *  \param pectx inspection context
1510
 */
1511
static void PrefilterMpmTxSingle(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p,
1512
        Flow *f, void *txv, const uint64_t idx, const AppLayerTxData *_txd, const uint8_t flags)
1513
{
4,476✔
1514
    SCEnter();
4,476✔
1515

1516
    const PrefilterMpmCtx *ctx = (const PrefilterMpmCtx *)pectx;
4,476✔
1517
    const MpmCtx *mpm_ctx = ctx->mpm_ctx;
4,476✔
1518
    SCLogDebug("running on list %d", ctx->list_id);
4,476✔
1519

1520
    InspectionBuffer *buffer = DetectGetSingleData(
4,476✔
1521
            det_ctx, ctx->transforms, f, flags, txv, ctx->list_id, ctx->GetDataSingle);
4,476✔
1522
    if (buffer == NULL)
4,476✔
1523
        return;
2,459✔
1524

1525
    const uint32_t data_len = buffer->inspect_len;
2,017✔
1526
    const uint8_t *data = buffer->inspect;
2,017✔
1527

1528
    SCLogDebug("mpm'ing buffer:");
2,017✔
1529
    // PrintRawDataFp(stdout, data, data_len);
1530

1531
    if (data != NULL && data_len >= mpm_ctx->minlen) {
2,017✔
1532
        (void)mpm_table[mpm_ctx->mpm_type].Search(
1,545✔
1533
                mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, data, data_len);
1,545✔
1534
        PREFILTER_PROFILING_ADD_BYTES(det_ctx, data_len);
1,545✔
1535
    }
1,545✔
1536
}
2,017✔
1537

1538
/** \brief Generic Mpm prefilter callback
1539
 *
1540
 *  \param det_ctx detection engine thread ctx
1541
 *  \param p packet to inspect
1542
 *  \param f flow to inspect
1543
 *  \param txv tx to inspect
1544
 *  \param pectx inspection context
1545
 */
1546
static void PrefilterMpm(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f,
1547
        void *txv, const uint64_t idx, const AppLayerTxData *_txd, const uint8_t flags)
1548
{
18,513✔
1549
    SCEnter();
18,513✔
1550

1551
    const PrefilterMpmCtx *ctx = (const PrefilterMpmCtx *)pectx;
18,513✔
1552
    const MpmCtx *mpm_ctx = ctx->mpm_ctx;
18,513✔
1553
    SCLogDebug("running on list %d", ctx->list_id);
18,513✔
1554

1555
    InspectionBuffer *buffer = ctx->GetData(det_ctx, ctx->transforms, f, flags, txv, ctx->list_id);
18,513✔
1556
    if (buffer == NULL)
18,513✔
1557
        return;
9,702✔
1558

1559
    const uint32_t data_len = buffer->inspect_len;
8,811✔
1560
    const uint8_t *data = buffer->inspect;
8,811✔
1561

1562
    SCLogDebug("mpm'ing buffer:");
8,811✔
1563
    //PrintRawDataFp(stdout, data, data_len);
1564

1565
    if (data != NULL && data_len >= mpm_ctx->minlen) {
8,811✔
1566
        (void)mpm_table[mpm_ctx->mpm_type].Search(
7,099✔
1567
                mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, data, data_len);
7,099✔
1568
        PREFILTER_PROFILING_ADD_BYTES(det_ctx, data_len);
7,099✔
1569
    }
7,099✔
1570
}
8,811✔
1571

1572
static void PrefilterGenericMpmFree(void *ptr)
1573
{
35,043✔
1574
    SCFree(ptr);
35,043✔
1575
}
35,043✔
1576

1577
int PrefilterGenericMpmRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
1578
        const DetectBufferMpmRegistry *mpm_reg, int list_id)
1579
{
30,021✔
1580
    SCEnter();
30,021✔
1581
    PrefilterMpmCtx *pectx = SCCalloc(1, sizeof(*pectx));
30,021✔
1582
    if (pectx == NULL)
30,021✔
1583
        return -1;
×
1584
    pectx->list_id = list_id;
30,021✔
1585
    pectx->GetData = mpm_reg->app_v2.GetData;
30,021✔
1586
    pectx->mpm_ctx = mpm_ctx;
30,021✔
1587
    pectx->transforms = &mpm_reg->transforms;
30,021✔
1588

1589
    int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpm,
30,021✔
1590
        mpm_reg->app_v2.alproto, mpm_reg->app_v2.tx_min_progress,
30,021✔
1591
        pectx, PrefilterGenericMpmFree, mpm_reg->pname);
30,021✔
1592
    if (r != 0) {
30,021✔
1593
        SCFree(pectx);
×
1594
    }
×
1595
    return r;
30,021✔
1596
}
30,021✔
1597

1598
int PrefilterSingleMpmRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
1599
        const DetectBufferMpmRegistry *mpm_reg, int list_id)
1600
{
5,814✔
1601
    SCEnter();
5,814✔
1602
    PrefilterMpmCtx *pectx = SCCalloc(1, sizeof(*pectx));
5,814✔
1603
    if (pectx == NULL)
5,814✔
1604
        return -1;
×
1605
    pectx->list_id = list_id;
5,814✔
1606
    pectx->GetDataSingle = mpm_reg->app_v2.GetDataSingle;
5,814✔
1607
    pectx->mpm_ctx = mpm_ctx;
5,814✔
1608
    pectx->transforms = &mpm_reg->transforms;
5,814✔
1609

1610
    int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMpmTxSingle, mpm_reg->app_v2.alproto,
5,814✔
1611
            mpm_reg->app_v2.tx_min_progress, pectx, PrefilterGenericMpmFree, mpm_reg->pname);
5,814✔
1612
    if (r != 0) {
5,814✔
1613
        SCFree(pectx);
×
1614
    }
×
1615
    return r;
5,814✔
1616
}
5,814✔
1617

1618
static void PrefilterMultiGenericMpmFree(void *ptr)
1619
{
7,751✔
1620
    // PrefilterMpmListId
1621
    SCFree(ptr);
7,751✔
1622
}
7,751✔
1623

1624
static void PrefilterMultiMpm(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f,
1625
        void *txv, const uint64_t idx, const AppLayerTxData *_txd, const uint8_t flags)
1626
{
2,430✔
1627
    SCEnter();
2,430✔
1628

1629
    const PrefilterMpmListId *ctx = (const PrefilterMpmListId *)pectx;
2,430✔
1630
    const MpmCtx *mpm_ctx = ctx->mpm_ctx;
2,430✔
1631
    SCLogDebug("running on list %d", ctx->list_id);
2,430✔
1632
    uint32_t local_id = 0;
2,430✔
1633

1634
    do {
8,172✔
1635
        // loop until we get a NULL
1636
        InspectionBuffer *buffer = DetectGetMultiData(
8,172✔
1637
                det_ctx, ctx->transforms, f, flags, txv, ctx->list_id, local_id, ctx->GetData);
8,172✔
1638
        if (buffer == NULL)
8,172✔
1639
            break;
2,430✔
1640

1641
        if (buffer->inspect_len >= mpm_ctx->minlen) {
5,742✔
1642
            (void)mpm_table[mpm_ctx->mpm_type].Search(
4,900✔
1643
                    mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, buffer->inspect, buffer->inspect_len);
4,900✔
1644
            PREFILTER_PROFILING_ADD_BYTES(det_ctx, buffer->inspect_len);
4,900✔
1645
        }
4,900✔
1646

1647
        local_id++;
5,742✔
1648
    } while (1);
5,742✔
1649
}
2,430✔
1650

1651
int PrefilterMultiGenericMpmRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
1652
        const DetectBufferMpmRegistry *mpm_reg, int list_id)
1653
{
7,751✔
1654
    SCEnter();
7,751✔
1655
    PrefilterMpmListId *pectx = SCCalloc(1, sizeof(*pectx));
7,751✔
1656
    if (pectx == NULL)
7,751✔
1657
        return -1;
×
1658
    pectx->list_id = list_id;
7,751✔
1659
    pectx->GetData = mpm_reg->app_v2.GetMultiData;
7,751✔
1660
    pectx->mpm_ctx = mpm_ctx;
7,751✔
1661
    pectx->transforms = &mpm_reg->transforms;
7,751✔
1662

1663
    int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMultiMpm, mpm_reg->app_v2.alproto,
7,751✔
1664
            mpm_reg->app_v2.tx_min_progress, pectx, PrefilterMultiGenericMpmFree, mpm_reg->pname);
7,751✔
1665
    if (r != 0) {
7,751✔
1666
        SCFree(pectx);
×
1667
    }
×
1668
    return r;
7,751✔
1669
}
7,751✔
1670

1671
/* generic mpm for pkt engines */
1672

1673
typedef struct PrefilterMpmPktCtx {
1674
    int list_id;
1675
    InspectionBufferGetPktDataPtr GetData;
1676
    const MpmCtx *mpm_ctx;
1677
    const DetectEngineTransforms *transforms;
1678
} PrefilterMpmPktCtx;
1679

1680
/** \brief Generic Mpm prefilter callback
1681
 *
1682
 *  \param det_ctx detection engine thread ctx
1683
 *  \param p packet to inspect
1684
 *  \param f flow to inspect
1685
 *  \param txv tx to inspect
1686
 *  \param pectx inspection context
1687
 */
1688
static void PrefilterMpmPkt(DetectEngineThreadCtx *det_ctx,
1689
        Packet *p, const void *pectx)
1690
{
5,611✔
1691
    SCEnter();
5,611✔
1692

1693
    const PrefilterMpmPktCtx *ctx = (const PrefilterMpmPktCtx *)pectx;
5,611✔
1694
    const MpmCtx *mpm_ctx = ctx->mpm_ctx;
5,611✔
1695
    SCLogDebug("running on list %d", ctx->list_id);
5,611✔
1696

1697
    InspectionBuffer *buffer = ctx->GetData(det_ctx, ctx->transforms,
5,611✔
1698
            p, ctx->list_id);
5,611✔
1699
    if (buffer == NULL)
5,611✔
1700
        return;
1,111✔
1701

1702
    const uint32_t data_len = buffer->inspect_len;
4,500✔
1703
    const uint8_t *data = buffer->inspect;
4,500✔
1704

1705
    SCLogDebug("mpm'ing buffer:");
4,500✔
1706
    //PrintRawDataFp(stdout, data, data_len);
1707

1708
    if (data != NULL && data_len >= mpm_ctx->minlen) {
4,500✔
1709
        (void)mpm_table[mpm_ctx->mpm_type].Search(
4,359✔
1710
                mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, data, data_len);
4,359✔
1711
        PREFILTER_PROFILING_ADD_BYTES(det_ctx, data_len);
4,359✔
1712
    }
4,359✔
1713
}
4,500✔
1714

1715
static void PrefilterMpmPktFree(void *ptr)
1716
{
1,245✔
1717
    SCFree(ptr);
1,245✔
1718
}
1,245✔
1719

1720
int PrefilterGenericMpmPktRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
1721
        const DetectBufferMpmRegistry *mpm_reg, int list_id)
1722
{
1,245✔
1723
    SCEnter();
1,245✔
1724
    PrefilterMpmPktCtx *pectx = SCCalloc(1, sizeof(*pectx));
1,245✔
1725
    if (pectx == NULL)
1,245✔
1726
        return -1;
×
1727
    pectx->list_id = list_id;
1,245✔
1728
    pectx->GetData = mpm_reg->pkt_v1.GetData;
1,245✔
1729
    pectx->mpm_ctx = mpm_ctx;
1,245✔
1730
    pectx->transforms = &mpm_reg->transforms;
1,245✔
1731

1732
    enum SignatureHookPkt hook = SIGNATURE_HOOK_PKT_NOT_SET; // TODO review
1,245✔
1733
    int r = PrefilterAppendEngine(
1,245✔
1734
            de_ctx, sgh, PrefilterMpmPkt, 0, hook, pectx, PrefilterMpmPktFree, mpm_reg->pname);
1,245✔
1735
    if (r != 0) {
1,245✔
1736
        SCFree(pectx);
×
1737
    }
×
1738
    return r;
1,245✔
1739
}
1,245✔
1740

1741
#define QUEUE_STEP 16
13✔
1742

1743
void PostRuleMatchWorkQueueAppend(
1744
        DetectEngineThreadCtx *det_ctx, const Signature *s, const int type, const uint32_t value)
1745
{
74✔
1746
    if (det_ctx->post_rule_work_queue.q == NULL) {
74✔
1747
        det_ctx->post_rule_work_queue.q =
13✔
1748
                SCCalloc(1, sizeof(PostRuleMatchWorkQueueItem) * QUEUE_STEP);
13✔
1749
        if (det_ctx->post_rule_work_queue.q == NULL) {
13✔
1750
            DetectEngineSetEvent(det_ctx, DETECT_EVENT_POST_MATCH_QUEUE_FAILED);
×
1751
            return;
×
1752
        }
×
1753
        det_ctx->post_rule_work_queue.size = QUEUE_STEP;
13✔
1754
    } else if (det_ctx->post_rule_work_queue.len == det_ctx->post_rule_work_queue.size) {
61✔
1755
        void *ptr = SCRealloc(
×
1756
                det_ctx->post_rule_work_queue.q, (det_ctx->post_rule_work_queue.size + QUEUE_STEP) *
×
1757
                                                         sizeof(PostRuleMatchWorkQueueItem));
×
1758
        if (ptr == NULL) {
×
1759
            DetectEngineSetEvent(det_ctx, DETECT_EVENT_POST_MATCH_QUEUE_FAILED);
×
1760
            return;
×
1761
        }
×
1762
        det_ctx->post_rule_work_queue.q = ptr;
×
1763
        det_ctx->post_rule_work_queue.size += QUEUE_STEP;
×
1764
    }
×
1765
    det_ctx->post_rule_work_queue.q[det_ctx->post_rule_work_queue.len].sm_type = type;
74✔
1766
    det_ctx->post_rule_work_queue.q[det_ctx->post_rule_work_queue.len].value = value;
74✔
1767
#ifdef DEBUG
1768
    det_ctx->post_rule_work_queue.q[det_ctx->post_rule_work_queue.len].id = s->iid;
1769
#endif
1770
    det_ctx->post_rule_work_queue.len++;
74✔
1771
    SCLogDebug("det_ctx->post_rule_work_queue.len %u", det_ctx->post_rule_work_queue.len);
74✔
1772
}
74✔
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