• 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

83.05
/src/detect-engine-event.c
1
/* Copyright (C) 2007-2021 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 Breno Silva <breno.silva@gmail.com>
22
 *
23
 * Implements the decode-event keyword
24
 */
25

26
#include "suricata-common.h"
27
#include "suricata.h"
28
#include "decode.h"
29
#include "detect.h"
30
#include "detect-parse.h"
31
#include "detect-engine-prefilter-common.h"
32
#include "detect-engine-uint.h"
33

34
#include "flow-var.h"
35
#include "decode-events.h"
36

37
#include "util-debug.h"
38

39
#include "stream-tcp.h"
40

41

42
/* Need to get the DEvents[] array */
43

44
#include "detect-engine-event.h"
45
#include "util-unittest.h"
46

47
#define PARSE_REGEX "\\S[0-9A-z_]+[.][A-z0-9_+.]+$"
3✔
48

49
static DetectParseRegex parse_regex;
50

51
static int DetectEngineEventMatch (DetectEngineThreadCtx *,
52
        Packet *, const Signature *, const SigMatchCtx *);
53
static int DetectEngineEventSetup (DetectEngineCtx *, Signature *, const char *);
54
static int DetectDecodeEventSetup (DetectEngineCtx *, Signature *, const char *);
55
static int DetectStreamEventSetup (DetectEngineCtx *, Signature *, const char *);
56
static void DetectEngineEventFree (DetectEngineCtx *, void *);
57
#ifdef UNITTESTS
58
void EngineEventRegisterTests(void);
59
#endif
60

61
static bool PrefilterEventIsPrefilterable(const Signature *s, int smtype)
62
{
×
63
    const SigMatch *sm;
×
64
    for (sm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; sm != NULL; sm = sm->next) {
×
65
        if (sm->type == smtype) {
×
66
            return true;
×
67
        }
×
68
    }
×
69
    return false;
×
70
}
×
71
static bool PrefilterStreamEventIsPrefilterable(const Signature *s)
72
{
×
73
    return PrefilterEventIsPrefilterable(s, DETECT_STREAM_EVENT);
×
74
}
×
75

76
static bool PrefilterDecodeEventIsPrefilterable(const Signature *s)
77
{
×
78
    return PrefilterEventIsPrefilterable(s, DETECT_DECODE_EVENT);
×
79
}
×
80

81
static void PrefilterPacketEventSet(PrefilterPacketHeaderValue *v, void *smctx)
82
{
549✔
83
    const DetectEngineEventData *a = smctx;
549✔
84
    v->u8[0] = PREFILTER_U8HASH_MODE_EQ;
549✔
85
    v->u8[1] = a->event; // arg1
549✔
86
    v->u8[2] = 0;        // arg2
549✔
87
}
549✔
88

89
static bool PrefilterPacketEventCompare(PrefilterPacketHeaderValue v, void *smctx)
90
{
×
91
    const DetectEngineEventData *a = smctx;
×
92
    DetectUintData_u8 du8;
×
93
    du8.mode = DETECT_UINT_EQ;
×
94
    du8.arg1 = a->event;
×
95
    du8.arg2 = 0;
×
96
    return PrefilterPacketU8Compare(v, &du8);
×
97
}
×
98

99
static void PrefilterPacketEventMatch(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
100
{
78✔
101
    const PrefilterPacketU8HashCtx *h = pectx;
78✔
102
    for (uint8_t u = 0; u < p->events.cnt; u++) {
159✔
103
        const SigsArray *sa = h->array[p->events.events[u]];
81✔
104
        if (sa) {
81✔
105
            PrefilterAddSids(&det_ctx->pmq, sa->sigs, sa->cnt);
12✔
106
        }
12✔
107
    }
81✔
108
}
78✔
109

110
static int PrefilterSetupStreamEvent(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
111
{
80✔
112
    return PrefilterSetupPacketHeaderU8Hash(de_ctx, sgh, DETECT_STREAM_EVENT,
80✔
113
            SIG_MASK_REQUIRE_ENGINE_EVENT, PrefilterPacketEventSet, PrefilterPacketEventCompare,
80✔
114
            PrefilterPacketEventMatch);
80✔
115
}
80✔
116

117
static int PrefilterSetupDecodeEvent(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
118
{
219✔
119
    return PrefilterSetupPacketHeaderU8Hash(de_ctx, sgh, DETECT_DECODE_EVENT,
219✔
120
            SIG_MASK_REQUIRE_ENGINE_EVENT, PrefilterPacketEventSet, PrefilterPacketEventCompare,
219✔
121
            PrefilterPacketEventMatch);
219✔
122
}
219✔
123

124
/**
125
 * \brief Registration function for decode-event: keyword
126
 */
127
void DetectEngineEventRegister (void)
128
{
3✔
129
    sigmatch_table[DETECT_ENGINE_EVENT].name = "engine-event";
3✔
130
    sigmatch_table[DETECT_ENGINE_EVENT].Match = DetectEngineEventMatch;
3✔
131
    sigmatch_table[DETECT_ENGINE_EVENT].Setup = DetectEngineEventSetup;
3✔
132
    sigmatch_table[DETECT_ENGINE_EVENT].Free  = DetectEngineEventFree;
3✔
133
#ifdef UNITTESTS
134
    sigmatch_table[DETECT_ENGINE_EVENT].RegisterTests = EngineEventRegisterTests;
135
#endif
136

137
    sigmatch_table[DETECT_DECODE_EVENT].name = "decode-event";
3✔
138
    sigmatch_table[DETECT_DECODE_EVENT].Match = DetectEngineEventMatch;
3✔
139
    sigmatch_table[DETECT_DECODE_EVENT].Setup = DetectDecodeEventSetup;
3✔
140
    sigmatch_table[DETECT_DECODE_EVENT].Free  = DetectEngineEventFree;
3✔
141
    sigmatch_table[DETECT_DECODE_EVENT].desc =
3✔
142
            "match on events triggered by structural or invalid values during packet decoding";
3✔
143
    sigmatch_table[DETECT_DECODE_EVENT].url = "/rules/decode-layer.html#decode-event";
3✔
144
    sigmatch_table[DETECT_DECODE_EVENT].flags |= SIGMATCH_DEONLY_COMPAT;
3✔
145
    sigmatch_table[DETECT_DECODE_EVENT].SupportsPrefilter = PrefilterDecodeEventIsPrefilterable;
3✔
146
    sigmatch_table[DETECT_DECODE_EVENT].SetupPrefilter = PrefilterSetupDecodeEvent;
3✔
147

148
    sigmatch_table[DETECT_STREAM_EVENT].name = "stream-event";
3✔
149
    sigmatch_table[DETECT_STREAM_EVENT].Match = DetectEngineEventMatch;
3✔
150
    sigmatch_table[DETECT_STREAM_EVENT].Setup = DetectStreamEventSetup;
3✔
151
    sigmatch_table[DETECT_STREAM_EVENT].Free  = DetectEngineEventFree;
3✔
152
    sigmatch_table[DETECT_STREAM_EVENT].desc =
3✔
153
            "match on events triggered by anomalies during TCP streaming";
3✔
154
    sigmatch_table[DETECT_STREAM_EVENT].SupportsPrefilter = PrefilterStreamEventIsPrefilterable;
3✔
155
    sigmatch_table[DETECT_STREAM_EVENT].SetupPrefilter = PrefilterSetupStreamEvent;
3✔
156

157
    DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
3✔
158
}
3✔
159

160
/**
161
 * \brief This function is used to match decoder event flags set on a packet with those passed via decode-event:
162
 *
163
 * \param t pointer to thread vars
164
 * \param det_ctx pointer to the pattern matcher thread
165
 * \param p pointer to the current packet
166
 * \param s pointer to the Signature
167
 * \param m pointer to the sigmatch
168
 *
169
 * \retval 0 no match
170
 * \retval 1 match
171
 */
172
static int DetectEngineEventMatch (DetectEngineThreadCtx *det_ctx,
173
        Packet *p, const Signature *s, const SigMatchCtx *ctx)
174
{
118,914✔
175
    SCEnter();
118,914✔
176

177
    const DetectEngineEventData *de = (const DetectEngineEventData *)ctx;
118,914✔
178

179
    if (ENGINE_ISSET_EVENT(p, de->event)) {
118,914✔
180
        SCLogDebug("de->event matched %u", de->event);
17,270✔
181
        SCReturnInt(1);
17,270✔
182
    }
17,270✔
183

184
    SCReturnInt(0);
118,914✔
185
}
118,914✔
186

187
static bool OutdatedEvent(const char *raw)
188
{
41,165✔
189
    if (strcmp(raw, "decoder.udp.hlen_invalid") == 0) {
41,165✔
190
        return true;
79✔
191
    }
79✔
192
    return false;
41,086✔
193
}
41,165✔
194

195
/**
196
 * \brief This function is used to parse decoder events options passed via decode-event: keyword
197
 *
198
 * \param rawstr Pointer to the user provided decode-event options
199
 *
200
 * \retval de pointer to DetectFlowData on success
201
 * \retval NULL on failure
202
 */
203
static DetectEngineEventData *DetectEngineEventParse (const char *rawstr)
204
{
44,503✔
205
    int i;
44,503✔
206
    DetectEngineEventData *de = NULL;
44,503✔
207
    int res = 0, found = 0;
44,503✔
208
    size_t pcre2len;
44,503✔
209
    pcre2_match_data *match = NULL;
44,503✔
210

211
    int ret = DetectParsePcreExec(&parse_regex, &match, rawstr, 0, 0);
44,503✔
212
    if (ret < 1) {
44,503✔
213
        SCLogError("pcre_exec parse error, ret %" PRId32 ", string %s", ret, rawstr);
1,772✔
214
        goto error;
1,772✔
215
    }
1,772✔
216

217
    char copy_str[128] = "";
42,731✔
218
    pcre2len = sizeof(copy_str);
42,731✔
219
    res = pcre2_substring_copy_bynumber(match, 0, (PCRE2_UCHAR8 *)copy_str, &pcre2len);
42,731✔
220

221
    if (res < 0) {
42,731✔
222
        SCLogError("pcre2_substring_copy_bynumber failed");
2✔
223
        goto error;
2✔
224
    }
2✔
225

226
    for (i = 0; DEvents[i].event_name != NULL; i++) {
4,676,284✔
227
        if (strcasecmp(DEvents[i].event_name,copy_str) == 0) {
4,674,720✔
228
            found = 1;
41,165✔
229
            break;
41,165✔
230
        }
41,165✔
231
    }
4,674,720✔
232

233
    if (found == 0) {
42,729✔
234
        SCLogError("unknown decode event \"%s\"", copy_str);
1,564✔
235
        goto error;
1,564✔
236
    }
1,564✔
237

238
    de = SCMalloc(sizeof(DetectEngineEventData));
41,165✔
239
    if (unlikely(de == NULL))
41,165✔
240
        goto error;
×
241

242
    de->event = DEvents[i].code;
41,165✔
243

244
    if (de->event == STREAM_REASSEMBLY_OVERLAP_DIFFERENT_DATA) {
41,165✔
245
        StreamTcpReassembleConfigEnableOverlapCheck();
413✔
246
    }
413✔
247

248
    if (OutdatedEvent(rawstr)) {
41,165✔
249
        if (SigMatchStrictEnabled(DETECT_DECODE_EVENT)) {
79✔
UNCOV
250
            SCLogError("decode-event keyword no longer supports event \"%s\"", rawstr);
×
UNCOV
251
            goto error;
×
252
        } else {
79✔
253
            SCLogWarning("decode-event keyword no longer supports event \"%s\"", rawstr);
79✔
254
        }
79✔
255
    }
79✔
256

257
    pcre2_match_data_free(match);
41,165✔
258
    return de;
41,165✔
259

260
error:
3,338✔
261
    if (de)
3,338✔
UNCOV
262
        SCFree(de);
×
263
    if (match) {
3,338✔
264
        pcre2_match_data_free(match);
3,338✔
265
    }
3,338✔
266
    return NULL;
3,338✔
267
}
41,165✔
268

269
/**
270
 * \brief this function is used to add the parsed decode-event into the current signature
271
 *
272
 * \param de_ctx pointer to the Detection Engine Context
273
 * \param s pointer to the Current Signature
274
 * \param rawstr pointer to the user provided decode-event options
275
 *
276
 * \retval 0 on Success
277
 * \retval -1 on Failure
278
 */
279
static int DetectEngineEventSetupDo(
280
        DetectEngineCtx *de_ctx, Signature *s, const char *rawstr, uint16_t smtype)
281
{
44,503✔
282
    DetectEngineEventData *de = DetectEngineEventParse(rawstr);
44,503✔
283
    if (de == NULL)
44,503✔
284
        return -1;
3,338✔
285

286
    SCLogDebug("rawstr %s %u", rawstr, de->event);
41,165✔
287

288
    if (SCSigMatchAppendSMToList(de_ctx, s, smtype, (SigMatchCtx *)de, DETECT_SM_LIST_MATCH) ==
41,165✔
289
            NULL) {
41,165✔
290
        SCFree(de);
×
291
        return -1;
×
292
    }
×
293
    return 0;
41,165✔
294
}
41,165✔
295

296

297
static int DetectEngineEventSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
298
{
12✔
299
    return DetectEngineEventSetupDo (de_ctx, s, rawstr, DETECT_ENGINE_EVENT);
12✔
300
}
12✔
301

302
/**
303
 * \brief this function will free memory associated with DetectEngineEventData
304
 *
305
 * \param de pointer to DetectEngineEventData
306
 */
307
static void DetectEngineEventFree(DetectEngineCtx *de_ctx, void *ptr)
308
{
41,165✔
309
    DetectEngineEventData *de = (DetectEngineEventData *)ptr;
41,165✔
310
    if (de)
41,165✔
311
        SCFree(de);
41,165✔
312
}
41,165✔
313

314

315
/**
316
 * \brief this function Setup the 'decode-event' keyword by setting the correct
317
 * signature type
318
*/
319
static int DetectDecodeEventSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
320
{
26,434✔
321
    char drawstr[64] = "decoder.";
26,434✔
322

323
    /* decoder:$EVENT alias command develop as decode-event:decoder.$EVENT */
324
    strlcat(drawstr, rawstr, sizeof(drawstr));
26,434✔
325

326
    return DetectEngineEventSetupDo(de_ctx, s, drawstr, DETECT_DECODE_EVENT);
26,434✔
327
}
26,434✔
328

329
/**
330
 * \brief this function Setup the 'stream-event' keyword by resolving the alias
331
*/
332
static int DetectStreamEventSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
333
{
18,057✔
334
    char srawstr[64] = "stream.";
18,057✔
335

336
    if (strcmp(rawstr, "est_synack_resend_with_different_ack") == 0) {
18,057✔
337
        rawstr = "est_synack_resend_with_diff_ack";
460✔
338
    } else if (strcmp(rawstr, "3whs_synack_resend_with_different_ack") == 0) {
17,597✔
339
        rawstr = "3whs_synack_resend_with_diff_ack";
1,440✔
340
    }
1,440✔
341

342
    /* stream:$EVENT alias command develop as decode-event:stream.$EVENT */
343
    strlcat(srawstr, rawstr, sizeof(srawstr));
18,057✔
344

345
    return DetectEngineEventSetupDo(de_ctx, s, srawstr, DETECT_STREAM_EVENT);
18,057✔
346
}
18,057✔
347

348
/*
349
 * ONLY TESTS BELOW THIS COMMENT
350
 */
351
#ifdef UNITTESTS
352

353
/**
354
 * \test EngineEventTestParse01 is a test for a valid decode-event value
355
 */
356
static int EngineEventTestParse01 (void)
357
{
358
    DetectEngineEventData *de = DetectEngineEventParse("decoder.ipv4.pkt_too_small");
359

360
    FAIL_IF_NULL(de);
361

362
    DetectEngineEventFree(NULL, de);
363

364
    PASS;
365
}
366

367
/**
368
 * \test EngineEventTestParse02 is a test for a valid upper + lower case decode-event value
369
 */
370
static int EngineEventTestParse02 (void)
371
{
372
    DetectEngineEventData *de = DetectEngineEventParse("decoder.PPP.pkt_too_small");
373

374
    FAIL_IF_NULL(de);
375

376
    DetectEngineEventFree(NULL, de);
377

378
    PASS;
379
}
380

381
/**
382
 * \test EngineEventTestParse03 is a test for a valid upper case decode-event value
383
 */
384
static int EngineEventTestParse03 (void)
385
{
386
    DetectEngineEventData *de = DetectEngineEventParse("decoder.IPV6.PKT_TOO_SMALL");
387

388
    FAIL_IF_NULL(de);
389

390
    DetectEngineEventFree(NULL, de);
391

392
    PASS;
393
}
394

395
/**
396
 * \test EngineEventTestParse04 is a test for an invalid upper case decode-event value
397
 */
398
static int EngineEventTestParse04 (void)
399
{
400
    DetectEngineEventData *de = DetectEngineEventParse("decoder.IPV6.INVALID_EVENT");
401

402
    FAIL_IF_NOT_NULL(de);
403

404
    DetectEngineEventFree(NULL, de);
405

406
    PASS;
407
}
408

409
/**
410
 * \test EngineEventTestParse05 is a test for an invalid char into the decode-event value
411
 */
412
static int EngineEventTestParse05 (void)
413
{
414
    DetectEngineEventData *de = DetectEngineEventParse("decoder.IPV-6,INVALID_CHAR");
415

416
    FAIL_IF_NOT_NULL(de);
417

418
    DetectEngineEventFree(NULL, de);
419

420
    PASS;
421
}
422

423
/**
424
 * \test EngineEventTestParse06 is a test for match function with valid decode-event value
425
 */
426
static int EngineEventTestParse06 (void)
427
{
428
    Packet *p = PacketGetFromAlloc();
429
    FAIL_IF_NULL(p);
430

431
    ThreadVars tv;
432

433
    memset(&tv, 0, sizeof(ThreadVars));
434

435
    ENGINE_SET_EVENT(p,PPP_PKT_TOO_SMALL);
436

437
    DetectEngineEventData *de = DetectEngineEventParse("decoder.ppp.pkt_too_small");
438
    FAIL_IF_NULL(de);
439

440
    de->event = PPP_PKT_TOO_SMALL;
441

442
    SigMatch *sm = SigMatchAlloc();
443
    FAIL_IF_NULL(sm);
444

445
    sm->type = DETECT_DECODE_EVENT;
446
    sm->ctx = (SigMatchCtx *)de;
447

448
    FAIL_IF_NOT(DetectEngineEventMatch(NULL, p, NULL, sm->ctx));
449

450
    PacketFree(p);
451
    SCFree(de);
452
    SCFree(sm);
453

454
    PASS;
455
}
456

457
/**
458
 * \brief this function registers unit tests for EngineEvent
459
 */
460
void EngineEventRegisterTests(void)
461
{
462
    UtRegisterTest("EngineEventTestParse01", EngineEventTestParse01);
463
    UtRegisterTest("EngineEventTestParse02", EngineEventTestParse02);
464
    UtRegisterTest("EngineEventTestParse03", EngineEventTestParse03);
465
    UtRegisterTest("EngineEventTestParse04", EngineEventTestParse04);
466
    UtRegisterTest("EngineEventTestParse05", EngineEventTestParse05);
467
    UtRegisterTest("EngineEventTestParse06", EngineEventTestParse06);
468
}
469
#endif /* UNITTESTS */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc