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

OISF / suricata / 22550902417

01 Mar 2026 07:32PM UTC coverage: 68.401% (-5.3%) from 73.687%
22550902417

Pull #14922

github

web-flow
github-actions: bump actions/upload-artifact from 6.0.0 to 7.0.0

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #14922: github-actions: bump actions/upload-artifact from 6.0.0 to 7.0.0

218243 of 319063 relevant lines covered (68.4%)

3284926.58 hits per line

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

66.11
/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
{
429,151✔
70
    if (n < 2)
429,151✔
71
        return;
231,648✔
72
    SigIntId p = sids[n / 2];
197,503✔
73
    SigIntId *l = sids;
197,503✔
74
    SigIntId *r = sids + n - 1;
197,503✔
75
    while (l <= r) {
1,156,921✔
76
        if (*l < p)
959,418✔
77
            l++;
371,914✔
78
        else if (*r > p)
587,504✔
79
            r--;
262,712✔
80
        else {
324,792✔
81
            SigIntId t = *l;
324,792✔
82
            *l = *r;
324,792✔
83
            *r = t;
324,792✔
84
            l++;
324,792✔
85
            r--;
324,792✔
86
        }
324,792✔
87
    }
959,418✔
88
    QuickSortSigIntId(sids, (uint32_t)(r - sids) + 1);
197,503✔
89
    QuickSortSigIntId(l, (uint32_t)(sids + n - l));
197,503✔
90
}
197,503✔
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
{
66,834✔
104
    /* reset rule store */
105
    det_ctx->pmq.rule_id_array_cnt = 0;
66,834✔
106

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

110
    PrefilterEngine *engine = sgh->tx_engines;
66,834✔
111
    do {
2,149,726✔
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);
2,149,726✔
114
        if (tx_ptr == NULL) {
2,149,726✔
115
            // incompatible engine->alproto with flow alproto
116
            goto next;
1,910,411✔
117
        }
1,910,411✔
118

119
        if (engine->ctx.tx_min_progress != -1) {
239,315✔
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)
239,070✔
128
                break;
7,202✔
129
            if (tx->tx_progress > engine->ctx.tx_min_progress) {
231,868✔
130
                SCLogDebug("tx->tx_progress %u > engine->ctx.tx_min_progress %d", tx->tx_progress,
205,804✔
131
                        engine->ctx.tx_min_progress);
205,804✔
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) {
205,804✔
136
                    SCLogDebug("tx already marked progress as beyond engine: %u > %u",
112,633✔
137
                            tx->detect_progress, engine->ctx.tx_min_progress);
112,633✔
138
                    goto next;
112,633✔
139
                } else {
113,188✔
140
                    SCLogDebug("tx->tx_progress %u > engine->ctx.tx_min_progress %d: "
93,171✔
141
                               "tx->detect_progress %u",
93,171✔
142
                            tx->tx_progress, engine->ctx.tx_min_progress, tx->detect_progress);
93,171✔
143
                }
93,171✔
144
            }
205,804✔
145
#ifdef DEBUG
146
            uint32_t old = det_ctx->pmq.rule_id_array_cnt;
147
#endif
148
            PREFILTER_PROFILING_START(det_ctx);
119,235✔
149
            engine->cb.PrefilterTx(det_ctx, engine->pectx, p, p->flow, tx_ptr, tx->tx_id,
119,235✔
150
                    tx->tx_data_ptr, flow_flags);
119,235✔
151
            PREFILTER_PROFILING_END(det_ctx, engine->gid);
119,235✔
152
            SCLogDebug("engine %p min_progress %d %s:%s: results %u", engine,
119,235✔
153
                    engine->ctx.tx_min_progress, AppProtoToString(engine->alproto), pname,
119,235✔
154
                    det_ctx->pmq.rule_id_array_cnt - old);
119,235✔
155

156
            if (tx->tx_progress > engine->ctx.tx_min_progress && engine->is_last_for_progress) {
119,235✔
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;
26,253✔
161
                SCLogDebug("tx->tx_progress %d engine->ctx.tx_min_progress %d "
26,253✔
162
                           "engine->is_last_for_progress %d => tx->detect_progress updated to %02x",
26,253✔
163
                        tx->tx_progress, engine->ctx.tx_min_progress, engine->is_last_for_progress,
26,253✔
164
                        tx->detect_progress);
26,253✔
165
            }
26,253✔
166
        } else {
119,235✔
167
            PREFILTER_PROFILING_START(det_ctx);
245✔
168
            engine->cb.PrefilterTx(det_ctx, engine->pectx, p, p->flow, tx_ptr, tx->tx_id,
245✔
169
                    tx->tx_data_ptr, flow_flags);
245✔
170
            PREFILTER_PROFILING_END(det_ctx, engine->gid);
245✔
171
        }
245✔
172
    next:
2,141,738✔
173
        if (engine->is_last)
2,141,738✔
174
            break;
59,667✔
175
        engine++;
2,082,071✔
176
    } while (1);
2,082,071✔
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)) {
66,048✔
181
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_SORT1);
3,628✔
182
        QuickSortSigIntId(det_ctx->pmq.rule_id_array, det_ctx->pmq.rule_id_array_cnt);
3,628✔
183
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_SORT1);
3,628✔
184
    }
3,628✔
185
}
66,048✔
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
{
×
196
    SCLogDebug("post-rule-match engines %p", sgh->post_rule_match_engines);
×
197
    if (sgh->post_rule_match_engines) {
×
198
        PrefilterEngine *engine = sgh->post_rule_match_engines;
×
199
        do {
×
200
            SCLogDebug("running post-rule-match engine");
×
201
            PREFILTER_PROFILING_START(det_ctx);
×
202
            engine->cb.PrefilterPostRule(det_ctx, engine->pectx, p, f);
×
203
            PREFILTER_PROFILING_END(det_ctx, engine->gid);
×
204

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

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

216
void Prefilter(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p,
217
        const uint8_t flags, const SignatureMask mask)
218
{
1,182,878✔
219
    SCEnter();
1,182,878✔
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) {
1,182,878✔
231
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_PKT);
688,183✔
232
        /* run packet engines */
233
        PrefilterEngine *engine = sgh->pkt_engines;
688,183✔
234
        do {
688,183✔
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) &&
688,183✔
240
                    (engine->ctx.pkt.hook == 0 || (p->pkt_hooks & BIT_U16(engine->ctx.pkt.hook)))) {
688,183✔
241
                PREFILTER_PROFILING_START(det_ctx);
687,189✔
242
                engine->cb.Prefilter(det_ctx, p, engine->pectx);
687,189✔
243
                PREFILTER_PROFILING_END(det_ctx, engine->gid);
687,189✔
244
            }
687,189✔
245

246
            if (engine->is_last)
688,183✔
247
                break;
688,153✔
248
            engine++;
30✔
249
        } while (1);
30✔
250
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_PKT);
×
251
    }
688,183✔
252

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

265
            if (engine->is_last)
1,114,876✔
266
                break;
782,936✔
267
            engine++;
331,940✔
268
        }
331,940✔
269
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_PAYLOAD);
782,910✔
270
    }
782,910✔
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)) {
1,182,878✔
275
        PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_SORT1);
30,386✔
276
        QuickSortSigIntId(det_ctx->pmq.rule_id_array, det_ctx->pmq.rule_id_array_cnt);
30,386✔
277
        PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_SORT1);
30,386✔
278
    }
30,386✔
279
    SCReturn;
1,182,878✔
280
}
1,182,878✔
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
{
329✔
286
    if (sgh == NULL || PrefilterFunc == NULL || pectx == NULL)
329✔
287
        return -1;
×
288

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

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

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

303
    if (sgh->init->pkt_engines == NULL) {
329✔
304
        sgh->init->pkt_engines = e;
329✔
305
    } else {
329✔
306
        PrefilterEngineList *t = sgh->init->pkt_engines;
×
307
        while (t->next != NULL) {
×
308
            t = t->next;
×
309
        }
×
310

311
        t->next = e;
×
312
        e->id = t->id + 1;
×
313
    }
×
314

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

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

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

331
    e->Prefilter = PrefilterFunc;
998✔
332
    e->pectx = pectx;
998✔
333
    e->Free = FreeFunc;
998✔
334

335
    if (sgh->init->payload_engines == NULL) {
998✔
336
        sgh->init->payload_engines = e;
810✔
337
    } else {
810✔
338
        PrefilterEngineList *t = sgh->init->payload_engines;
188✔
339
        while (t->next != NULL) {
188✔
340
            t = t->next;
×
341
        }
×
342

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

347
    e->name = name;
998✔
348
    e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
998✔
349
    return 0;
998✔
350
}
998✔
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
{
3,635✔
356
    if (sgh == NULL || PrefilterTxFunc == NULL || pectx == NULL)
3,635✔
357
        return -1;
×
358

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

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

372
    if (sgh->init->tx_engines == NULL) {
3,635✔
373
        sgh->init->tx_engines = e;
656✔
374
    } else {
3,246✔
375
        PrefilterEngineList *t = sgh->init->tx_engines;
2,979✔
376
        while (t->next != NULL) {
60,522✔
377
            t = t->next;
57,543✔
378
        }
57,543✔
379

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

384
    e->name = name;
3,635✔
385
    e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
3,635✔
386
    return 0;
3,635✔
387
}
3,635✔
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
{
×
393
    if (sgh == NULL || PrefilterFrameFunc == NULL || pectx == NULL)
×
394
        return -1;
×
395

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

401
    e->frame_type = frame_type;
×
402
    e->alproto = alproto;
×
403
    e->PrefilterFrame = PrefilterFrameFunc;
×
404
    e->pectx = pectx;
×
405
    e->Free = FreeFunc;
×
406

407
    if (sgh->init->frame_engines == NULL) {
×
408
        sgh->init->frame_engines = e;
×
409
    } else {
×
410
        PrefilterEngineList *t = sgh->init->frame_engines;
×
411
        while (t->next != NULL) {
×
412
            t = t->next;
×
413
        }
×
414

415
        t->next = e;
×
416
        e->id = t->id + 1;
×
417
    }
×
418

419
    e->name = name;
×
420
    e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
×
421
    return 0;
×
422
}
×
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
{
×
429
    if (sgh == NULL || PrefilterPostRuleFunc == NULL || pectx == NULL)
×
430
        return -1;
×
431

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

440
    if (sgh->init->post_rule_match_engines == NULL) {
×
441
        sgh->init->post_rule_match_engines = e;
×
442
    } else {
×
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;
×
453
    e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free);
×
454
    return 0;
×
455
}
×
456

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

465
void PrefilterFreeEnginesList(PrefilterEngineList *list)
466
{
106,645✔
467
    PrefilterEngineList *t = list;
106,645✔
468

469
    while (t != NULL) {
111,607✔
470
        PrefilterEngineList *next = t->next;
4,962✔
471
        PrefilterFreeEngineList(t);
4,962✔
472
        t = next;
4,962✔
473
    }
4,962✔
474
}
106,645✔
475

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

480
    while (1) {
4,962✔
481
        const PrefilterStore *s = PrefilterStoreGetStore(de_ctx, t->gid);
4,962✔
482
        if (s && s->FreeFunc && t->pectx) {
4,962✔
483
            s->FreeFunc(t->pectx);
3,964✔
484
        }
3,964✔
485

486
        if (t->is_last)
4,962✔
487
            break;
1,795✔
488
        t++;
3,167✔
489
    }
3,167✔
490
    SCFreeAligned(list);
1,795✔
491
}
1,795✔
492

493
void PrefilterCleanupRuleGroup(const DetectEngineCtx *de_ctx, SigGroupHead *sgh)
494
{
21,329✔
495
    if (sgh->pkt_engines) {
21,329✔
496
        PrefilterFreeEngines(de_ctx, sgh->pkt_engines);
329✔
497
        sgh->pkt_engines = NULL;
329✔
498
    }
329✔
499
    if (sgh->payload_engines) {
21,329✔
500
        PrefilterFreeEngines(de_ctx, sgh->payload_engines);
810✔
501
        sgh->payload_engines = NULL;
810✔
502
    }
810✔
503
    if (sgh->tx_engines) {
21,329✔
504
        PrefilterFreeEngines(de_ctx, sgh->tx_engines);
656✔
505
        sgh->tx_engines = NULL;
656✔
506
    }
656✔
507
    if (sgh->frame_engines) {
21,329✔
508
        PrefilterFreeEngines(de_ctx, sgh->frame_engines);
×
509
        sgh->frame_engines = NULL;
×
510
    }
×
511
    if (sgh->post_rule_match_engines) {
21,329✔
512
        PrefilterFreeEngines(de_ctx, sgh->post_rule_match_engines);
×
513
        sgh->post_rule_match_engines = NULL;
×
514
    }
×
515
}
21,329✔
516

517
static int PrefilterSetupRuleGroupSortHelper(const void *a, const void *b)
518
{
11,804✔
519
    const PrefilterEngine *s0 = a;
11,804✔
520
    const PrefilterEngine *s1 = b;
11,804✔
521
    if (s1->ctx.tx_min_progress == s0->ctx.tx_min_progress) {
11,804✔
522
        if (s1->alproto == s0->alproto) {
5,448✔
523
            return s0->local_id > s1->local_id ? 1 : -1;
4,069✔
524
        } else {
4,179✔
525
            return s0->alproto > s1->alproto ? 1 : -1;
1,379✔
526
        }
1,379✔
527
    } else {
6,358✔
528
        return s0->ctx.tx_min_progress > s1->ctx.tx_min_progress ? 1 : -1;
6,356✔
529
    }
6,356✔
530
}
11,804✔
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
{
458✔
567
    SCFree(data);
458✔
568
}
458✔
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
{
127✔
573
    const struct PrefilterNonPFDataTx *data = (const struct PrefilterNonPFDataTx *)pectx;
127✔
574
    SCLogDebug("adding %u sids", data->size);
127✔
575
    PrefilterAddSids(&det_ctx->pmq, data->array, data->size);
127✔
576
}
127✔
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
{
6,600✔
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
}
6,600✔
602

603
static void PrefilterPktNonPF(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
604
{
687,167✔
605
    const uint16_t alproto = p->flow ? p->flow->alproto : ALPROTO_UNKNOWN;
687,167✔
606
    const SignatureMask mask = p->sig_mask;
687,167✔
607
    const struct PrefilterNonPFData *data = (const struct PrefilterNonPFData *)pectx;
687,167✔
608
    SCLogDebug("adding %u sids", data->size);
687,167✔
609
    NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_called);
687,167✔
610
    for (uint32_t i = 0; i < data->size; i++) {
5,423,686✔
611
        const struct PrefilterNonPFDataSig *ds = &data->array[i];
4,736,631✔
612
        const SignatureMask rule_mask = ds->pkt.sig_mask;
4,736,631✔
613
        if ((rule_mask & mask) == rule_mask) {
4,736,631✔
614
            switch (ds->type) {
790,962✔
615
                case 0:
690,032✔
616
                    if (ds->value == ALPROTO_UNKNOWN || AppProtoEquals(ds->value, alproto)) {
690,033✔
617
                        const uint32_t sid = ds->sid;
25,602✔
618
                        PrefilterAddSids(&det_ctx->pmq, &sid, 1);
25,602✔
619
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_sids);
25,602✔
620
                    } else {
2,148,149,309✔
621
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_alproto_fail);
2,148,148,078✔
622
                    }
2,148,148,078✔
623
                    break;
690,032✔
624
                case 1:
19,456✔
625
                    if (ds->value == p->dp) {
19,456✔
626
                        const uint32_t sid = ds->sid;
5,099✔
627
                        PrefilterAddSids(&det_ctx->pmq, &sid, 1);
5,099✔
628
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_sids);
5,099✔
629
                    } else {
14,357✔
630
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_dport_fail);
14,357✔
631
                    }
14,357✔
632
                    break;
19,456✔
633
                case 2:
81,469✔
634
                    if (ds->value == p->payload_len) {
81,469✔
635
                        const uint32_t sid = ds->sid;
203✔
636
                        PrefilterAddSids(&det_ctx->pmq, &sid, 1);
203✔
637
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_sids);
203✔
638
                    } else {
81,266✔
639
                        NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_dsize_fail);
81,266✔
640
                    }
81,266✔
641
                    break;
81,469✔
642
            }
790,962✔
643
        } else {
3,965,861✔
644
            NONPF_PKT_STATS_INCR(prefilter_pkt_nonpf_mask_fail);
3,945,669✔
645
        }
3,945,669✔
646
    }
4,736,631✔
647
}
687,167✔
648

649
static void PrefilterPktNonPFHookFlowStart(
650
        DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
651
{
×
652
    if (p->flowflags & (FLOW_PKT_TOSERVER_FIRST | FLOW_PKT_TOCLIENT_FIRST)) {
×
653
        PrefilterPktNonPF(det_ctx, p, pectx);
×
654
    }
×
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
{
×
664
    DEBUG_VALIDATE_BUG_ON(p->flow == NULL);
×
665
    const uint16_t alproto = p->flow->alproto;
×
666
    const struct PrefilterNonPFData *data = (const struct PrefilterNonPFData *)pectx;
×
667
    SCLogDebug("adding %u sids", data->size);
×
668
    for (uint32_t i = 0; i < data->size; i++) {
×
669
        const struct PrefilterNonPFDataSig *ds = &data->array[i];
×
670
        if (ds->frame.type == frame->type &&
×
671
                (ds->value == ALPROTO_UNKNOWN || AppProtoEquals(ds->value, alproto))) {
×
672
            const uint32_t sid = ds->sid;
×
673
            PrefilterAddSids(&det_ctx->pmq, &sid, 1);
×
674
        }
×
675
    }
×
676
}
×
677

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

680
static uint32_t NonPFNamesHash(HashTable *h, void *data, uint16_t _len)
681
{
191✔
682
    const char *str = data;
191✔
683
    return StringHashDjb2((const uint8_t *)str, (uint16_t)strlen(str)) % h->array_size;
191✔
684
}
191✔
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
{
191✔
695
    SCFree(data);
191✔
696
}
191✔
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
{
385✔
713
    struct TxNonPFData *d = data;
385✔
714
    return (d->alproto + d->progress + d->dir + d->sig_list) % h->array_size;
385✔
715
}
385✔
716

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

725
static void TxNonPFFree(void *data)
726
{
191✔
727
    struct TxNonPFData *d = data;
191✔
728
    SCFree(d->sigs);
191✔
729
    SCFree(d);
191✔
730
}
191✔
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
{
194✔
736
    const uint32_t max_sids = DetectEngineGetMaxSigId(de_ctx);
194✔
737

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

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

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

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

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

811
    return 0;
191✔
812
}
191✔
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
{
1,558✔
823
    const uint32_t max_sids = DetectEngineGetMaxSigId(de_ctx);
1,558✔
824
    SCLogDebug("max_sids %u", max_sids);
1,558✔
825
    struct PrefilterNonPFDataSig *pkt_non_pf_array = SCCalloc(max_sids, sizeof(*pkt_non_pf_array));
1,558✔
826
    if (pkt_non_pf_array == NULL) {
1,558✔
827
        return -1;
×
828
    }
×
829
    uint32_t pkt_non_pf_array_size = 0;
1,558✔
830
    struct PrefilterNonPFDataSig *frame_non_pf_array =
1,558✔
831
            SCCalloc(max_sids, sizeof(*frame_non_pf_array));
1,558✔
832
    if (frame_non_pf_array == NULL) {
1,558✔
833
        SCFree(pkt_non_pf_array);
×
834
        return -1;
×
835
    }
×
836
    uint32_t frame_non_pf_array_size = 0;
1,558✔
837

838
    struct PrefilterNonPFDataSig *pkt_hook_flow_start_non_pf_array =
1,558✔
839
            SCCalloc(max_sids, sizeof(*pkt_hook_flow_start_non_pf_array));
1,558✔
840
    if (pkt_hook_flow_start_non_pf_array == NULL) {
1,558✔
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;
1,558✔
846
    SignatureMask pkt_hook_flow_start_mask = 0;
1,558✔
847
    bool pkt_hook_flow_start_mask_init = false;
1,558✔
848

849
    HashListTable *tx_engines_hash =
1,558✔
850
            HashListTableInit(256, TxNonPFHash, TxNonPFCompare, TxNonPFFree);
1,558✔
851
    if (tx_engines_hash == NULL) {
1,558✔
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) {
1,558✔
859
        de_ctx->non_pf_engine_names =
835✔
860
                HashTableInit(512, NonPFNamesHash, NonPFNamesCompare, NonPFNamesFree);
835✔
861
        if (de_ctx->non_pf_engine_names == NULL) {
835✔
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
    }
835✔
869

870
    SignatureMask pkt_mask = 0;
1,558✔
871
    bool pkt_mask_init = false;
1,558✔
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");
1,558✔
878
    SCLogDebug("app_events_list_id %d", app_events_list_id);
1,558✔
879
    const int app_state_list_id = DetectBufferTypeGetByName("app-layer-state");
1,558✔
880
    SCLogDebug("app_state_list_id %d", app_state_list_id);
1,558✔
881
    for (uint32_t sig = 0; sig < sgh->init->sig_cnt; sig++) {
1,505,669✔
882
        Signature *s = sgh->init->match_array[sig];
1,504,111✔
883
        if (s == NULL)
1,504,111✔
884
            continue;
×
885
        SCLogDebug("checking sid %u for non-prefilter", s->id);
1,504,111✔
886
        if (s->init_data->mpm_sm != NULL && (s->flags & SIG_FLAG_MPM_NEG) == 0)
1,504,111✔
887
            continue;
1,503,409✔
888
        if (s->init_data->prefilter_sm != NULL)
702✔
889
            continue;
×
890
        if ((s->flags & (SIG_FLAG_PREFILTER | SIG_FLAG_MPM_NEG)) == SIG_FLAG_PREFILTER)
702✔
891
            continue;
×
892
        SCLogDebug("setting up sid %u for non-prefilter", s->id);
702✔
893

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

899
        if (s->init_data->hook.type == SIGNATURE_HOOK_TYPE_PKT &&
702✔
900
                s->init_data->hook.t.pkt.ph == SIGNATURE_HOOK_PKT_FLOW_START) {
702✔
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 */
905
            uint8_t type;
×
906
            uint16_t value;
×
907
            if ((s->flags & SIG_FLAG_DSIZE) && s->dsize_mode == DETECT_UINT_EQ) {
×
908
                SCLogDebug("dsize extra match");
×
909
                type = 2;
×
910
                value = s->dsize_low;
×
911
            } else if (s->dp != NULL && s->dp->next == NULL && s->dp->port == s->dp->port2) {
×
912
                type = 1;
×
913
                value = s->dp->port;
×
914
            } else {
×
915
                type = 0;
×
916
                value = s->alproto;
×
917
            }
×
918
            pkt_hook_flow_start_non_pf_array[pkt_hook_flow_start_non_pf_array_size].sid = s->iid;
×
919
            pkt_hook_flow_start_non_pf_array[pkt_hook_flow_start_non_pf_array_size].value = value;
×
920
            pkt_hook_flow_start_non_pf_array[pkt_hook_flow_start_non_pf_array_size].type = type;
×
921
            pkt_hook_flow_start_non_pf_array[pkt_hook_flow_start_non_pf_array_size].pkt.sig_mask =
×
922
                    s->mask;
×
923
            pkt_hook_flow_start_non_pf_array_size++;
×
924

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

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

936
        for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
884✔
937
            const int list_id = s->init_data->buffers[x].id;
182✔
938
            const DetectBufferType *buf = DetectEngineBufferTypeGetById(de_ctx, list_id);
182✔
939
            if (buf == NULL)
182✔
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);
182✔
944
            if (list_id == app_events_list_id)
182✔
945
                continue;
30✔
946
            if (buf->packet) {
152✔
947
                SCLogDebug("packet buf");
30✔
948
                /* packet is handled below */
949
                pkt_non_pf = true;
30✔
950
            } else if (buf->frame) {
152✔
951
                for (DetectEngineFrameInspectionEngine *f = de_ctx->frame_inspect_engines;
×
952
                        f != NULL; f = f->next) {
×
953
                    if (!((((s->flags & SIG_FLAG_TOSERVER) != 0 && f->dir == 0) ||
×
954
                                  ((s->flags & SIG_FLAG_TOCLIENT) != 0 && f->dir == 1)) &&
×
955
                                list_id == (int)f->sm_list &&
×
956
                                AppProtoEquals(s->alproto, f->alproto)))
×
957
                        continue;
×
958

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

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

970
            } else {
122✔
971
                SCLogDebug("x %u list_id %d", x, list_id);
122✔
972
                for (DetectEngineAppInspectionEngine *app = de_ctx->app_inspect_engines;
122✔
973
                        app != NULL; app = app->next) {
83,814✔
974
                    SCLogDebug("app %p proto %s list_d %d sig dir %0x", app,
83,692✔
975
                            AppProtoToString(app->alproto), app->sm_list,
83,692✔
976
                            s->flags & (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT));
83,692✔
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) ||
83,692✔
983
                                  ((s->flags & SIG_FLAG_TOCLIENT) != 0 && app->dir == 1)) &&
83,692✔
984
                                list_id == (int)app->sm_list &&
83,692✔
985
                                (s->alproto == ALPROTO_UNKNOWN || app->alproto == ALPROTO_UNKNOWN ||
83,692✔
986
                                        AppProtoEquals(s->alproto, app->alproto))))
702✔
987
                        continue;
83,498✔
988

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

1007
            if (TxNonPFAddSig(de_ctx, tx_engines_hash, s->alproto, dir,
×
1008
                        (int16_t)s->init_data->hook.t.app.app_progress, s->init_data->hook.sm_list,
×
1009
                        pname, s) != 0) {
×
1010
                goto error;
×
1011
            }
×
1012
            tx_non_pf = true;
×
1013
        }
×
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)) {
702✔
1020
            if (!pkt_non_pf) {
580✔
1021
                SCLogDebug("not frame, not tx, so pkt");
550✔
1022
            }
550✔
1023
            pkt_non_pf = true;
580✔
1024
        }
580✔
1025

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

1029
        if (pkt_non_pf) {
702✔
1030
            /* for pkt non prefilter, we have some space in the structure,
1031
             * so we can squeeze another filter */
1032
            uint8_t type;
580✔
1033
            uint16_t value;
580✔
1034
            if ((s->flags & SIG_FLAG_DSIZE) && s->dsize_mode == DETECT_UINT_EQ) {
580✔
1035
                SCLogDebug("dsize extra match");
43✔
1036
                type = 2;
43✔
1037
                value = s->dsize_low;
43✔
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) {
537✔
1042
                type = 1;
56✔
1043
                value = s->dp->port;
56✔
1044
#ifdef NONPF_PKT_STATS
1045
                nonpf_pkt_dport++;
1046
#endif
1047
            } else {
481✔
1048
                type = 0;
481✔
1049
                value = s->alproto;
481✔
1050
#ifdef NONPF_PKT_STATS
1051
                nonpf_pkt_alproto++;
1052
#endif
1053
            }
481✔
1054

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

1061
            if (pkt_mask_init) {
580✔
1062
                pkt_mask &= s->mask;
251✔
1063
            } else {
338✔
1064
                pkt_mask = s->mask;
329✔
1065
                pkt_mask_init = true;
329✔
1066
            }
329✔
1067
        }
580✔
1068
    }
702✔
1069

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

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

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

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

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

1158
    SCFree(pkt_hook_flow_start_non_pf_array);
1,558✔
1159
    pkt_hook_flow_start_non_pf_array = NULL;
1,558✔
1160
    SCFree(pkt_non_pf_array);
1,558✔
1161
    pkt_non_pf_array = NULL;
1,558✔
1162
    SCFree(frame_non_pf_array);
1,558✔
1163
    frame_non_pf_array = NULL;
1,558✔
1164
    return 0;
1,558✔
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
}
1,558✔
1175

1176
int PrefilterSetupRuleGroup(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
1177
{
1,558✔
1178
    int r = PatternMatchPrepareGroup(de_ctx, sgh);
1,558✔
1179
    if (r != 0) {
1,558✔
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;
1,558✔
1187
    for (int i = 0; i < DETECT_TBLSIZE; i++) {
760,304✔
1188
        if (sigmatch_table[i].SetupPrefilter != NULL &&
758,746✔
1189
                (setting == DETECT_PREFILTER_AUTO || de_ctx->sm_types_prefilter[i])) {
758,746✔
1190
            sigmatch_table[i].SetupPrefilter(de_ctx, sgh);
×
1191
        }
×
1192
    }
758,746✔
1193

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

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

1212
        PrefilterEngine *e = sgh->pkt_engines;
329✔
1213
        for (el = sgh->init->pkt_engines ; el != NULL; el = el->next) {
658✔
1214
            e->local_id = el->id;
329✔
1215
            e->cb.Prefilter = el->Prefilter;
329✔
1216
            e->ctx.pkt.mask = el->pkt_mask;
329✔
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);
329✔
1220
            e->ctx.pkt.hook = (uint8_t)el->pkt_hook;
329✔
1221
            e->pectx = el->pectx;
329✔
1222
            el->pectx = NULL; // e now owns the ctx
329✔
1223
            e->gid = el->gid;
329✔
1224
            if (el->next == NULL) {
329✔
1225
                e->is_last = true;
329✔
1226
            }
329✔
1227
            e++;
329✔
1228
        }
329✔
1229
    }
329✔
1230
    if (sgh->init->payload_engines != NULL) {
1,558✔
1231
        uint32_t cnt = 0;
810✔
1232
        for (el = sgh->init->payload_engines ; el != NULL; el = el->next) {
1,808✔
1233
            cnt++;
998✔
1234
        }
998✔
1235
        sgh->payload_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
810✔
1236
        if (sgh->payload_engines == NULL) {
810✔
1237
            return -1;
×
1238
        }
×
1239
        memset(sgh->payload_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
810✔
1240

1241
        PrefilterEngine *e = sgh->payload_engines;
810✔
1242
        for (el = sgh->init->payload_engines ; el != NULL; el = el->next) {
1,808✔
1243
            e->local_id = el->id;
998✔
1244
            e->cb.Prefilter = el->Prefilter;
998✔
1245
            e->ctx.pkt.mask = el->pkt_mask;
998✔
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);
998✔
1249
            e->ctx.pkt.hook = (uint8_t)el->pkt_hook;
998✔
1250
            e->pectx = el->pectx;
998✔
1251
            el->pectx = NULL; // e now owns the ctx
998✔
1252
            e->gid = el->gid;
998✔
1253
            if (el->next == NULL) {
998✔
1254
                e->is_last = true;
810✔
1255
            }
810✔
1256
            e++;
998✔
1257
        }
998✔
1258
    }
810✔
1259
    if (sgh->init->tx_engines != NULL) {
1,558✔
1260
        uint32_t cnt = 0;
656✔
1261
        for (el = sgh->init->tx_engines ; el != NULL; el = el->next) {
4,291✔
1262
            cnt++;
3,635✔
1263
        }
3,635✔
1264
        sgh->tx_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
656✔
1265
        if (sgh->tx_engines == NULL) {
656✔
1266
            return -1;
×
1267
        }
×
1268
        memset(sgh->tx_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
656✔
1269

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

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

1289
        PrefilterEngine *engine;
656✔
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++) {
25,584✔
1294
            int last_tx_progress = 0;
24,928✔
1295
            bool last_tx_progress_set = false;
24,928✔
1296
            PrefilterEngine *prev_engine = NULL;
24,928✔
1297
            engine = sgh->tx_engines;
24,928✔
1298
            do {
138,130✔
1299
                if (engine->ctx.tx_min_progress != -1)
138,130✔
1300
                    BUG_ON(engine->ctx.tx_min_progress < last_tx_progress);
138,130✔
1301
                if (engine->alproto == a) {
138,130✔
1302
                    if (last_tx_progress_set && engine->ctx.tx_min_progress > last_tx_progress) {
3,635✔
1303
                        if (prev_engine) {
313✔
1304
                            prev_engine->is_last_for_progress = true;
313✔
1305
                        }
313✔
1306
                    }
313✔
1307

1308
                    last_tx_progress_set = true;
3,635✔
1309
                    prev_engine = engine;
3,635✔
1310
                } else {
134,495✔
1311
                    if (prev_engine) {
134,495✔
1312
                        prev_engine->is_last_for_progress = true;
12,630✔
1313
                    }
12,630✔
1314
                }
134,495✔
1315
                last_tx_progress = engine->ctx.tx_min_progress;
138,130✔
1316
                if (engine->is_last)
138,130✔
1317
                    break;
24,928✔
1318
                engine++;
113,202✔
1319
            } while (1);
113,202✔
1320
        }
24,928✔
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
    }
656✔
1336
    if (sgh->init->frame_engines != NULL) {
1,558✔
1337
        uint32_t cnt = 0;
×
1338
        for (el = sgh->init->frame_engines; el != NULL; el = el->next) {
×
1339
            cnt++;
×
1340
        }
×
1341
        sgh->frame_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS);
×
1342
        if (sgh->frame_engines == NULL) {
×
1343
            return -1;
×
1344
        }
×
1345
        memset(sgh->frame_engines, 0x00, (cnt * sizeof(PrefilterEngine)));
×
1346

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

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

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

1388
    return 0;
1,558✔
1389
}
1,558✔
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
{
5,977✔
1395
    PrefilterStore *ctx = data;
5,977✔
1396

1397
    uint32_t hash = (uint32_t)strlen(ctx->name);
5,977✔
1398

1399
    for (size_t u = 0; u < strlen(ctx->name); u++) {
80,074✔
1400
        hash += ctx->name[u];
74,097✔
1401
    }
74,097✔
1402

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

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

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

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

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

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

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

1443
    BUG_ON(de_ctx->prefilter_hash_table == NULL);
4,962✔
1444

1445
    SCLogDebug("looking up %s", name);
4,962✔
1446

1447
    PrefilterStore *rctx = HashListTableLookup(de_ctx->prefilter_hash_table, (void *)&ctx, 0);
4,962✔
1448
    if (rctx != NULL) {
4,962✔
1449
        return rctx->id;
3,947✔
1450
    }
3,947✔
1451

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

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

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

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

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

1477
    const PrefilterStore *store = NULL;
4,962✔
1478
    if (de_ctx->prefilter_hash_table != NULL) {
4,962✔
1479
        HashListTableBucket *hb = HashListTableGetListHead(de_ctx->prefilter_hash_table);
4,962✔
1480
        for ( ; hb != NULL; hb = HashListTableGetListNext(hb)) {
56,235✔
1481
            PrefilterStore *ctx = HashListTableGetListData(hb);
56,235✔
1482
            if (ctx->id == id) {
56,235✔
1483
                store = ctx;
4,962✔
1484
                break;
4,962✔
1485
            }
4,962✔
1486
        }
56,235✔
1487
    }
4,962✔
1488
    return store;
4,962✔
1489
}
4,962✔
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
{
60✔
1514
    SCEnter();
60✔
1515

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

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

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

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

1531
    if (data != NULL && data_len >= mpm_ctx->minlen) {
60✔
1532
        (void)mpm_table[mpm_ctx->mpm_type].Search(
×
1533
                mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, data, data_len);
×
1534
        PREFILTER_PROFILING_ADD_BYTES(det_ctx, data_len);
×
1535
    }
×
1536
}
60✔
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
{
69,759✔
1549
    SCEnter();
69,759✔
1550

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

1555
    InspectionBuffer *buffer = ctx->GetData(det_ctx, ctx->transforms, f, flags, txv, ctx->list_id);
69,759✔
1556
    if (buffer == NULL)
69,759✔
1557
        return;
24,386✔
1558

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

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

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

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

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

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

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

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

1618
static void PrefilterMultiGenericMpmFree(void *ptr)
1619
{
335✔
1620
    // PrefilterMpmListId
1621
    SCFree(ptr);
335✔
1622
}
335✔
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
{
5,222✔
1627
    SCEnter();
5,222✔
1628

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

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

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

1647
        local_id++;
5,322✔
1648
    } while (1);
5,322✔
1649
}
5,222✔
1650

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

1663
    int r = PrefilterAppendTxEngine(de_ctx, sgh, PrefilterMultiMpm, mpm_reg->app_v2.alproto,
335✔
1664
            mpm_reg->app_v2.tx_min_progress, pectx, PrefilterMultiGenericMpmFree, mpm_reg->pname);
335✔
1665
    if (r != 0) {
335✔
1666
        SCFree(pectx);
×
1667
    }
×
1668
    return r;
335✔
1669
}
335✔
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
{
×
1691
    SCEnter();
×
1692

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

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

1702
    const uint32_t data_len = buffer->inspect_len;
×
1703
    const uint8_t *data = buffer->inspect;
×
1704

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

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

1715
static void PrefilterMpmPktFree(void *ptr)
1716
{
×
1717
    SCFree(ptr);
×
1718
}
×
1719

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

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

1741
#define QUEUE_STEP 16
×
1742

1743
void PostRuleMatchWorkQueueAppend(
1744
        DetectEngineThreadCtx *det_ctx, const Signature *s, const int type, const uint32_t value)
1745
{
×
1746
    if (det_ctx->post_rule_work_queue.q == NULL) {
×
1747
        det_ctx->post_rule_work_queue.q =
×
1748
                SCCalloc(1, sizeof(PostRuleMatchWorkQueueItem) * QUEUE_STEP);
×
1749
        if (det_ctx->post_rule_work_queue.q == NULL) {
×
1750
            DetectEngineSetEvent(det_ctx, DETECT_EVENT_POST_MATCH_QUEUE_FAILED);
×
1751
            return;
×
1752
        }
×
1753
        det_ctx->post_rule_work_queue.size = QUEUE_STEP;
×
1754
    } else if (det_ctx->post_rule_work_queue.len == det_ctx->post_rule_work_queue.size) {
×
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;
×
1766
    det_ctx->post_rule_work_queue.q[det_ctx->post_rule_work_queue.len].value = value;
×
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++;
×
1771
    SCLogDebug("det_ctx->post_rule_work_queue.len %u", det_ctx->post_rule_work_queue.len);
×
1772
}
×
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