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

OISF / suricata / 22618661228

02 Mar 2026 09:33PM UTC coverage: 42.258% (-34.4%) from 76.611%
22618661228

push

github

victorjulien
github-actions: bump actions/download-artifact from 7.0.0 to 8.0.0

Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7.0.0 to 8.0.0.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/37930b1c2...70fc10c6e)

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

Signed-off-by: dependabot[bot] <support@github.com>

91511 of 216553 relevant lines covered (42.26%)

3416852.41 hits per line

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

62.45
/src/detect-fast-pattern.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 Anoop Saldanha <anoopsaldanha@gmail.com>
22
 *
23
 * Implements the fast_pattern keyword
24
 */
25

26
#include "suricata-common.h"
27
#include "detect.h"
28
#include "flow.h"
29
#include "detect-content.h"
30
#include "detect-parse.h"
31
#include "detect-engine.h"
32
#include "detect-engine-mpm.h"
33
#include "detect-engine-build.h"
34
#include "detect-fast-pattern.h"
35

36
#include "util-error.h"
37
#include "util-byte.h"
38
#include "util-debug.h"
39
#include "util-unittest.h"
40
#include "util-unittest-helper.h"
41

42
#define PARSE_REGEX "^(\\s*only\\s*)|\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*$"
7✔
43

44
static DetectParseRegex parse_regex;
45

46
static int DetectFastPatternSetup(DetectEngineCtx *, Signature *, const char *);
47
#ifdef UNITTESTS
48
static void DetectFastPatternRegisterTests(void);
49
#endif
50

51
/* holds the list of sm match lists that need to be searched for a keyword
52
 * that has fp support */
53
static SCFPSupportSMList *g_fp_support_smlist_list = NULL;
54

55
/**
56
 * \brief Checks if a particular buffer is in the list
57
 *        of lists that need to be searched for a keyword that has fp support.
58
 *
59
 * \param list_id The list id.
60
 *
61
 * \retval 1 If supported.
62
 * \retval 0 If not.
63
 */
64
int FastPatternSupportEnabledForSigMatchList(const DetectEngineCtx *de_ctx,
65
        const int list_id)
66
{
48,105✔
67
    if (de_ctx->fp_support_smlist_list == NULL) {
48,105✔
68
        return 0;
×
69
    }
×
70

71
    if (list_id == DETECT_SM_LIST_PMATCH)
48,105✔
72
        return 1;
4,867✔
73

74
    return DetectEngineBufferTypeSupportsMpmGetById(de_ctx, list_id);
43,238✔
75
}
48,105✔
76

77
static void Add(SCFPSupportSMList **list, const int list_id, const int priority)
78
{
2,673✔
79
    SCFPSupportSMList *ip = NULL;
2,673✔
80
    /* insertion point - ip */
81
    for (SCFPSupportSMList *tmp = *list; tmp != NULL; tmp = tmp->next) {
171,918✔
82
        if (list_id == tmp->list_id) {
171,897✔
83
            SCLogDebug("SM list already registered.");
1,508✔
84
            return;
1,508✔
85
        }
1,508✔
86

87
        /* We need a strict check to be sure that the current list
88
         * was not already registered
89
         * and other lists with the same priority hide it.
90
         */
91
        if (priority < tmp->priority)
170,389✔
92
            break;
1,144✔
93

94
        ip = tmp;
169,245✔
95
    }
169,245✔
96

97
    if (*list == NULL) {
1,165✔
98
        SCFPSupportSMList *new = SCCalloc(1, sizeof(SCFPSupportSMList));
7✔
99
        if (unlikely(new == NULL))
7✔
100
            exit(EXIT_FAILURE);
×
101
        new->list_id = list_id;
7✔
102
        new->priority = priority;
7✔
103

104
        *list = new;
7✔
105
        return;
7✔
106
    }
7✔
107

108
    SCFPSupportSMList *new = SCCalloc(1, sizeof(SCFPSupportSMList));
1,158✔
109
    if (unlikely(new == NULL))
1,158✔
110
        exit(EXIT_FAILURE);
×
111
    new->list_id = list_id;
1,158✔
112
    new->priority = priority;
1,158✔
113
    if (ip == NULL) {
1,158✔
114
        new->next = *list;
14✔
115
        *list = new;
14✔
116
    } else {
1,144✔
117
        new->next = ip->next;
1,144✔
118
        ip->next = new;
1,144✔
119
    }
1,144✔
120
}
1,158✔
121

122
/**
123
 * \brief Lets one add a sm list id to be searched for potential fp supported
124
 *        keywords later.
125
 *
126
 * \param list_id SM list id.
127
 * \param priority Priority for this list.
128
 */
129
void SupportFastPatternForSigMatchList(int list_id, int priority)
130
{
2,667✔
131
    Add(&g_fp_support_smlist_list, list_id, priority);
2,667✔
132
}
2,667✔
133

134
void DetectEngineRegisterFastPatternForId(DetectEngineCtx *de_ctx, int list_id, int priority)
135
{
6✔
136
    Add(&de_ctx->fp_support_smlist_list, list_id, priority);
6✔
137
}
6✔
138

139
/**
140
 * \brief Registers the keywords(SMs) that should be given fp support.
141
 */
142
void SupportFastPatternForSigMatchTypes(void)
143
{
7✔
144
    SupportFastPatternForSigMatchList(DETECT_SM_LIST_PMATCH, 3);
7✔
145

146
    /* other types are handled by DetectMpmAppLayerRegister() */
147
}
7✔
148

149
void DetectEngineInitializeFastPatternList(DetectEngineCtx *de_ctx)
150
{
7✔
151
    SCFPSupportSMList *last = NULL;
7✔
152
    for (SCFPSupportSMList *tmp = g_fp_support_smlist_list; tmp != NULL; tmp = tmp->next) {
1,169✔
153
        SCFPSupportSMList *n = SCCalloc(1, sizeof(*n));
1,162✔
154
        if (n == NULL) {
1,162✔
155
            FatalError("out of memory: %s", strerror(errno));
×
156
        }
×
157
        n->list_id = tmp->list_id;
1,162✔
158
        n->priority = tmp->priority;
1,162✔
159

160
        // append
161
        if (de_ctx->fp_support_smlist_list == NULL) {
1,162✔
162
            last = de_ctx->fp_support_smlist_list = n;
7✔
163
        } else {
1,155✔
164
            BUG_ON(last == NULL);
1,155✔
165
            last->next = n;
1,155✔
166
            last = n;
1,155✔
167
        }
1,155✔
168
    }
1,162✔
169
}
7✔
170

171
void DetectEngineFreeFastPatternList(DetectEngineCtx *de_ctx)
172
{
7✔
173
    for (SCFPSupportSMList *tmp = de_ctx->fp_support_smlist_list; tmp != NULL;) {
1,172✔
174
        SCFPSupportSMList *next = tmp->next;
1,165✔
175
        SCFree(tmp);
1,165✔
176
        tmp = next;
1,165✔
177
    }
1,165✔
178
    de_ctx->fp_support_smlist_list = NULL;
7✔
179
}
7✔
180

181
/**
182
 * \brief Registration function for fast_pattern keyword
183
 */
184
void DetectFastPatternRegister(void)
185
{
7✔
186
    sigmatch_table[DETECT_FAST_PATTERN].name = "fast_pattern";
7✔
187
    sigmatch_table[DETECT_FAST_PATTERN].desc = "force using preceding content in the multi pattern matcher";
7✔
188
    sigmatch_table[DETECT_FAST_PATTERN].url = "/rules/prefilter-keywords.html#fast-pattern";
7✔
189
    sigmatch_table[DETECT_FAST_PATTERN].Match = NULL;
7✔
190
    sigmatch_table[DETECT_FAST_PATTERN].Setup = DetectFastPatternSetup;
7✔
191
    sigmatch_table[DETECT_FAST_PATTERN].Free  = NULL;
7✔
192
#ifdef UNITTESTS
193
    sigmatch_table[DETECT_FAST_PATTERN].RegisterTests = DetectFastPatternRegisterTests;
194
#endif
195
    sigmatch_table[DETECT_FAST_PATTERN].flags |= SIGMATCH_OPTIONAL_OPT;
7✔
196

197
    DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
7✔
198
}
7✔
199

200
/**
201
 * \brief Configures the previous content context for a fast_pattern modifier
202
 *        keyword used in the rule.
203
 *
204
 * \param de_ctx   Pointer to the Detection Engine Context.
205
 * \param s        Pointer to the Signature to which the current keyword belongs.
206
 * \param arg      May hold an argument
207
 *
208
 * \retval  0 On success.
209
 * \retval -1 On failure.
210
 */
211
static int DetectFastPatternSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
212
{
18,122✔
213
    int res = 0;
18,122✔
214
    size_t pcre2len;
18,122✔
215
    char arg_substr[128] = "";
18,122✔
216
    DetectContentData *cd = NULL;
18,122✔
217
    pcre2_match_data *match = NULL;
18,122✔
218

219
    SigMatch *pm1 = DetectGetLastSMFromMpmLists(de_ctx, s);
18,122✔
220
    SigMatch *pm2 = SCDetectGetLastSMFromLists(s, DETECT_CONTENT, -1);
18,122✔
221
    if (pm1 == NULL && pm2 == NULL) {
18,122✔
222
        SCLogError("fast_pattern found inside "
×
223
                   "the rule, without a content context. Please use a "
×
224
                   "content based keyword before using fast_pattern");
×
225
        return -1;
×
226
    }
×
227

228
    SigMatch *pm = NULL;
18,122✔
229
    if (pm1 && pm2) {
18,122✔
230
        if (pm1->idx > pm2->idx)
15,973✔
231
            pm = pm1;
×
232
        else
15,973✔
233
            pm = pm2;
15,973✔
234
    } else if (pm1 && !pm2) {
15,973✔
235
        pm = pm1;
×
236
    } else {
2,149✔
237
        pm = pm2;
2,149✔
238
    }
2,149✔
239

240
    if (s->flags & SIG_FLAG_TXBOTHDIR && s->init_data->curbuf != NULL) {
18,122✔
241
        if (DetectBufferToClient(de_ctx, s->init_data->curbuf->id, s->alproto)) {
×
242
            if (s->init_data->init_flags & SIG_FLAG_INIT_TXDIR_STREAMING_TOSERVER) {
×
243
                SCLogError("fast_pattern cannot be used on to_client keyword for "
×
244
                           "transactional rule with a streaming buffer to server %u",
×
245
                        s->id);
×
246
                goto error;
×
247
            }
×
248
            s->init_data->init_flags |= SIG_FLAG_INIT_TXDIR_FAST_TOCLIENT;
×
249
        }
×
250
    }
×
251

252
    cd = (DetectContentData *)pm->ctx;
18,122✔
253
    if ((cd->flags & DETECT_CONTENT_NEGATED) &&
18,122✔
254
        ((cd->flags & DETECT_CONTENT_DISTANCE) ||
18,122✔
255
         (cd->flags & DETECT_CONTENT_WITHIN) ||
×
256
         (cd->flags & DETECT_CONTENT_OFFSET) ||
×
257
         (cd->flags & DETECT_CONTENT_DEPTH))) {
×
258

259
        /* we can't have any of these if we are having "only" */
260
        SCLogError("fast_pattern; cannot be "
×
261
                   "used with negated content, along with relative modifiers");
×
262
        goto error;
×
263
    }
×
264

265
    if (arg == NULL|| strcmp(arg, "") == 0) {
18,122✔
266
        if (cd->flags & DETECT_CONTENT_FAST_PATTERN) {
18,120✔
267
            SCLogError("can't use multiple fast_pattern "
×
268
                       "options for the same content");
×
269
            goto error;
×
270
        }
×
271
        else { /*allow only one content to have fast_pattern modifier*/
18,120✔
272
            for (uint32_t list_id = 0; list_id < DETECT_SM_LIST_MAX; list_id++) {
144,960✔
273
                SigMatch *sm = NULL;
126,840✔
274
                for (sm = s->init_data->smlists[list_id]; sm != NULL; sm = sm->next) {
146,405✔
275
                    if (sm->type == DETECT_CONTENT) {
19,565✔
276
                        DetectContentData *tmp_cd = (DetectContentData *)sm->ctx;
3,200✔
277
                        if (tmp_cd->flags & DETECT_CONTENT_FAST_PATTERN) {
3,200✔
278
                            SCLogError("fast_pattern "
×
279
                                       "can be used on only one content in a rule");
×
280
                            goto error;
×
281
                        }
×
282
                    }
3,200✔
283
                }
19,565✔
284
            }
126,840✔
285
        }
18,120✔
286
        if (SigMatchListSMBelongsTo(s, pm) == DETECT_SM_LIST_BASE64_DATA) {
18,120✔
287
            SCLogError("fast_pattern cannot be used with base64_data");
×
288
            goto error;
×
289
        }
×
290
        cd->flags |= DETECT_CONTENT_FAST_PATTERN;
18,120✔
291
        return 0;
18,120✔
292
    }
18,120✔
293

294
    /* Execute the regex and populate args with captures. */
295
    int ret = DetectParsePcreExec(&parse_regex, &match, arg, 0, 0);
2✔
296
    /* fast pattern only */
297
    if (ret == 2) {
2✔
298
        if ((cd->flags & DETECT_CONTENT_NEGATED) ||
×
299
            (cd->flags & DETECT_CONTENT_DISTANCE) ||
×
300
            (cd->flags & DETECT_CONTENT_WITHIN) ||
×
301
            (cd->flags & DETECT_CONTENT_OFFSET) ||
×
302
            (cd->flags & DETECT_CONTENT_DEPTH)) {
×
303

304
            /* we can't have any of these if we are having "only" */
305
            SCLogError("fast_pattern: only; cannot be "
×
306
                       "used with negated content or with any of the relative "
×
307
                       "modifiers like distance, within, offset, depth");
×
308
            goto error;
×
309
        }
×
310
        cd->flags |= DETECT_CONTENT_FAST_PATTERN_ONLY;
×
311

312
        /* fast pattern chop */
313
    } else if (ret == 4) {
2✔
314
        pcre2len = sizeof(arg_substr);
2✔
315
        res = pcre2_substring_copy_bynumber(match, 2, (PCRE2_UCHAR8 *)arg_substr, &pcre2len);
2✔
316
        if (res < 0) {
2✔
317
            SCLogError("pcre2_substring_copy_bynumber failed "
×
318
                       "for fast_pattern offset");
×
319
            goto error;
×
320
        }
×
321
        uint16_t offset;
2✔
322
        if (StringParseUint16(&offset, 10, 0, (const char *)arg_substr) <= 0) {
2✔
323
            SCLogError("Invalid fast pattern offset:"
×
324
                       " \"%s\"",
×
325
                    arg_substr);
×
326
            goto error;
×
327
        }
×
328

329
        pcre2len = sizeof(arg_substr);
2✔
330
        res = pcre2_substring_copy_bynumber(match, 3, (PCRE2_UCHAR8 *)arg_substr, &pcre2len);
2✔
331
        if (res < 0) {
2✔
332
            SCLogError("pcre2_substring_copy_bynumber failed "
×
333
                       "for fast_pattern offset");
×
334
            goto error;
×
335
        }
×
336
        uint16_t length;
2✔
337
        if (StringParseUint16(&length, 10, 0, (const char *)arg_substr) <= 0) {
2✔
338
            SCLogError("Invalid value for fast "
×
339
                       "pattern: \"%s\"",
×
340
                    arg_substr);
×
341
            goto error;
×
342
        }
×
343

344
        // Avoiding integer overflow
345
        if (offset > (65535 - length)) {
2✔
346
            SCLogError("Fast pattern (length + offset) "
×
347
                       "exceeds limit pattern length limit");
×
348
            goto error;
×
349
        }
×
350

351
        if (offset + length > cd->content_len) {
2✔
352
            SCLogError("Fast pattern (length + "
×
353
                       "offset (%u)) exceeds pattern length (%u)",
×
354
                    offset + length, cd->content_len);
×
355
            goto error;
×
356
        }
×
357

358
        cd->fp_chop_offset = offset;
2✔
359
        cd->fp_chop_len = length;
2✔
360
        cd->flags |= DETECT_CONTENT_FAST_PATTERN_CHOP;
2✔
361

362
    } else {
2✔
363
        SCLogError("parse error, ret %" PRId32 ", string %s", ret, arg);
×
364
        goto error;
×
365
    }
×
366

367
    cd->flags |= DETECT_CONTENT_FAST_PATTERN;
2✔
368

369
    pcre2_match_data_free(match);
2✔
370
    return 0;
2✔
371

372
 error:
×
373
     if (match) {
×
374
         pcre2_match_data_free(match);
375
     }
×
376
    return -1;
×
377
}
2✔
378

379
/*----------------------------------Unittests---------------------------------*/
380

381
#ifdef UNITTESTS
382
#include "detect-engine-alert.h"
383
#include "detect-engine-buffer.h"
384
static SigMatch *GetMatches(Signature *s, const int list)
385
{
386
    SigMatch *sm = DetectBufferGetFirstSigMatch(s, list);
387
    if (sm == NULL && list < DETECT_SM_LIST_MAX) {
388
        sm = s->init_data->smlists[list];
389
    }
390
    return sm;
391
}
392

393
static int DetectFastPatternStickySingle(const char *sticky, const int list)
394
{
395
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
396
    FAIL_IF_NULL(de_ctx);
397
    char string[1024];
398
    snprintf(string, sizeof(string),
399
            "alert tcp any any -> any any "
400
            "(%s%scontent:\"one\"; fast_pattern; sid:1;)",
401
            sticky ? sticky : "", sticky ? "; " : " ");
402
    Signature *s = DetectEngineAppendSig(de_ctx, string);
403
    FAIL_IF_NULL(s);
404
    SigMatch *sm = GetMatches(s, list);
405
    FAIL_IF_NULL(sm);
406
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
407
    DetectContentData *cd = (DetectContentData *)sm->ctx;
408
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
409
    FAIL_IF_NOT((cd->flags & DETECT_CONTENT_FAST_PATTERN) == DETECT_CONTENT_FAST_PATTERN);
410
    DetectEngineCtxFree(de_ctx);
411
    PASS;
412
}
413

414
static int DetectFastPatternModifierSingle(const char *sticky, const int list)
415
{
416
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
417
    FAIL_IF_NULL(de_ctx);
418
    char string[1024];
419
    snprintf(string, sizeof(string),
420
            "alert tcp any any -> any any "
421
            "(content:\"one\"; %s%sfast_pattern; sid:1;)",
422
            sticky ? sticky : "", sticky ? "; " : " ");
423
    Signature *s = DetectEngineAppendSig(de_ctx, string);
424
    FAIL_IF_NULL(s);
425
    SigMatch *sm = GetMatches(s, list);
426
    FAIL_IF_NULL(sm);
427
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
428
    DetectContentData *cd = (DetectContentData *)sm->ctx;
429
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
430
    FAIL_IF_NOT((cd->flags & DETECT_CONTENT_FAST_PATTERN) == DETECT_CONTENT_FAST_PATTERN);
431
    DetectEngineCtxFree(de_ctx);
432
    PASS;
433
}
434

435
static int DetectFastPatternStickySingleNoFP(const char *sticky, const int list)
436
{
437
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
438
    FAIL_IF_NULL(de_ctx);
439
    char string[1024];
440
    snprintf(string, sizeof(string),
441
            "alert tcp any any -> any any "
442
            "(%s%scontent:\"one\"; sid:1;)",
443
            sticky ? sticky : "", sticky ? "; " : " ");
444
    Signature *s = DetectEngineAppendSig(de_ctx, string);
445
    FAIL_IF_NULL(s);
446
    SigMatch *sm = GetMatches(s, list);
447
    FAIL_IF_NULL(sm);
448
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
449
    DetectContentData *cd = (DetectContentData *)sm->ctx;
450
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
451
    FAIL_IF_NOT((cd->flags & DETECT_CONTENT_FAST_PATTERN) == 0);
452
    DetectEngineCtxFree(de_ctx);
453
    PASS;
454
}
455

456
static int DetectFastPatternModifierSingleNoFP(const char *sticky, const int list)
457
{
458
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
459
    FAIL_IF_NULL(de_ctx);
460
    char string[1024];
461
    snprintf(string, sizeof(string),
462
            "alert tcp any any -> any any "
463
            "(content:\"one\"; %s%ssid:1;)",
464
            sticky ? sticky : "", sticky ? "; " : " ");
465
    Signature *s = DetectEngineAppendSig(de_ctx, string);
466
    FAIL_IF_NULL(s);
467
    SigMatch *sm = GetMatches(s, list);
468
    FAIL_IF_NULL(sm);
469
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
470
    DetectContentData *cd = (DetectContentData *)sm->ctx;
471
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
472
    FAIL_IF_NOT((cd->flags & DETECT_CONTENT_FAST_PATTERN) == 0);
473
    DetectEngineCtxFree(de_ctx);
474
    PASS;
475
}
476

477
static int DetectFastPatternStickySingleBadArg(const char *sticky)
478
{
479
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
480
    FAIL_IF_NULL(de_ctx);
481
    char string[1024];
482
    /* bogus argument to fast_pattern */
483
    snprintf(string, sizeof(string),
484
            "alert tcp any any -> any any "
485
            "(%s%scontent:\"one\"; fast_pattern:boo; sid:1;)",
486
            sticky ? sticky : "", sticky ? "; " : " ");
487
    Signature *s = DetectEngineAppendSig(de_ctx, string);
488
    FAIL_IF_NOT_NULL(s);
489
    /* fast_pattern only with distance */
490
    snprintf(string, sizeof(string),
491
            "alert tcp any any -> any any "
492
            "(%s%scontent:\"one\"; fast_pattern:only; content:\"two\"; distance:10; sid:1;)",
493
            sticky ? sticky : "", sticky ? "; " : " ");
494
    s = DetectEngineAppendSig(de_ctx, string);
495
    FAIL_IF_NOT_NULL(s);
496
    /* fast_pattern only with distance */
497
    snprintf(string, sizeof(string),
498
            "alert tcp any any -> any any "
499
            "(%s%scontent:\"one\"; content:\"two\"; fast_pattern:only; distance:10; sid:1;)",
500
            sticky ? sticky : "", sticky ? "; " : " ");
501
    s = DetectEngineAppendSig(de_ctx, string);
502
    FAIL_IF_NOT_NULL(s);
503
    /* fast_pattern only with distance */
504
    snprintf(string, sizeof(string),
505
            "alert tcp any any -> any any "
506
            "(%s%scontent:\"one\"; content:\"two\"; distance:10; fast_pattern:only; sid:1;)",
507
            sticky ? sticky : "", sticky ? "; " : " ");
508
    s = DetectEngineAppendSig(de_ctx, string);
509
    FAIL_IF_NOT_NULL(s);
510
    /* fast_pattern chop with invalid values */
511
    snprintf(string, sizeof(string),
512
            "alert tcp any any -> any any "
513
            "(%s%scontent:\"one\"; fast_pattern:5,6; sid:1;)",
514
            sticky ? sticky : "", sticky ? "; " : " ");
515
    s = DetectEngineAppendSig(de_ctx, string);
516
    FAIL_IF_NOT_NULL(s);
517
    DetectEngineCtxFree(de_ctx);
518
    PASS;
519
}
520

521
static int DetectFastPatternModifierBadRules(const char *sticky)
522
{
523
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
524
    FAIL_IF_NULL(de_ctx);
525
    char string[1024];
526
    /* bogus argument to fast_pattern */
527
    snprintf(string, sizeof(string),
528
            "alert tcp any any -> any any "
529
            "(content:\"one\"; %s%sfast_pattern:boo; sid:1;)",
530
            sticky ? sticky : "", sticky ? "; " : " ");
531
    Signature *s = DetectEngineAppendSig(de_ctx, string);
532
    FAIL_IF_NOT_NULL(s);
533
    /* fast_pattern only with distance */
534
    snprintf(string, sizeof(string),
535
            "alert tcp any any -> any any "
536
            "(content:\"one\"; %s%sfast_pattern:only; content:\"two\"; %s%sdistance:10; sid:1;)",
537
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
538
    s = DetectEngineAppendSig(de_ctx, string);
539
    FAIL_IF_NOT_NULL(s);
540
#if 0 // TODO bug?
541
    /* fast_pattern only with distance */
542
    snprintf(string, sizeof(string), "alert tcp any any -> any any "
543
            "(content:\"one\"; %s%s content:\"two\"; %s%sdistance:10; fast_pattern:only; sid:1;)",
544
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
545
    s = DetectEngineAppendSig(de_ctx, string);
546
    FAIL_IF_NOT_NULL(s);
547
#endif
548
    /* fast_pattern only with within */
549
    snprintf(string, sizeof(string),
550
            "alert tcp any any -> any any "
551
            "(content:\"one\"; %s%sfast_pattern:only; content:\"two\"; %s%swithin:10; sid:1;)",
552
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
553
    s = DetectEngineAppendSig(de_ctx, string);
554
    FAIL_IF_NOT_NULL(s);
555
    /* fast_pattern only with within */
556
    snprintf(string, sizeof(string),
557
            "alert tcp any any -> any any "
558
            "(content:\"one\"; %s%s content:\"two\"; %s%swithin:10; fast_pattern:only; sid:1;)",
559
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
560
    s = DetectEngineAppendSig(de_ctx, string);
561
    FAIL_IF_NOT_NULL(s);
562
    /* fast_pattern only with offset */
563
    snprintf(string, sizeof(string),
564
            "alert tcp any any -> any any "
565
            "(content:\"one\"; %s%sfast_pattern:only; offset:10; sid:1;)",
566
            sticky ? sticky : "", sticky ? "; " : " ");
567
    s = DetectEngineAppendSig(de_ctx, string);
568
    FAIL_IF_NOT_NULL(s);
569
    /* fast_pattern only with offset */
570
    snprintf(string, sizeof(string),
571
            "alert tcp any any -> any any "
572
            "(content:\"one\"; %s%s offset:10; fast_pattern:only; sid:1;)",
573
            sticky ? sticky : "", sticky ? "; " : " ");
574
    s = DetectEngineAppendSig(de_ctx, string);
575
    FAIL_IF_NOT_NULL(s);
576
    /* fast_pattern only with depth */
577
    snprintf(string, sizeof(string),
578
            "alert tcp any any -> any any "
579
            "(content:\"one\"; %s%sfast_pattern:only; depth:10; sid:1;)",
580
            sticky ? sticky : "", sticky ? "; " : " ");
581
    s = DetectEngineAppendSig(de_ctx, string);
582
    FAIL_IF_NOT_NULL(s);
583
    /* fast_pattern only with depth */
584
    snprintf(string, sizeof(string),
585
            "alert tcp any any -> any any "
586
            "(content:\"one\"; %s%s depth:10; fast_pattern:only; sid:1;)",
587
            sticky ? sticky : "", sticky ? "; " : " ");
588
    s = DetectEngineAppendSig(de_ctx, string);
589
    FAIL_IF_NOT_NULL(s);
590
    /* fast_pattern only negate */
591
    snprintf(string, sizeof(string),
592
            "alert tcp any any -> any any "
593
            "(content:\"one\"; %s%s content:!\"two\"; %s%sfast_pattern:only; sid:1;)",
594
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
595
    s = DetectEngineAppendSig(de_ctx, string);
596
    FAIL_IF_NOT_NULL(s);
597
    /* fast_pattern chop with invalid values */
598
    snprintf(string, sizeof(string),
599
            "alert tcp any any -> any any "
600
            "(content:\"one\"; %s%sfast_pattern:5,6; sid:1;)",
601
            sticky ? sticky : "", sticky ? "; " : " ");
602
    s = DetectEngineAppendSig(de_ctx, string);
603
    FAIL_IF_NOT_NULL(s);
604
    /* fast_pattern chop with invalid values */
605
    snprintf(string, sizeof(string),
606
            "alert tcp any any -> any any "
607
            "(content:\"one\"; %s%sfast_pattern:65977,2; sid:1;)",
608
            sticky ? sticky : "", sticky ? "; " : " ");
609
    s = DetectEngineAppendSig(de_ctx, string);
610
    FAIL_IF_NOT_NULL(s);
611
    /* fast_pattern chop with invalid values */
612
    snprintf(string, sizeof(string),
613
            "alert tcp any any -> any any "
614
            "(content:\"one\"; %s%sfast_pattern:2,65977; sid:1;)",
615
            sticky ? sticky : "", sticky ? "; " : " ");
616
    s = DetectEngineAppendSig(de_ctx, string);
617
    FAIL_IF_NOT_NULL(s);
618
    /* fast_pattern chop with invalid values */
619
    snprintf(string, sizeof(string),
620
            "alert tcp any any -> any any "
621
            "(content:\"one\"; %s%sfast_pattern:2,65534; sid:1;)",
622
            sticky ? sticky : "", sticky ? "; " : " ");
623
    s = DetectEngineAppendSig(de_ctx, string);
624
    FAIL_IF_NOT_NULL(s);
625
    /* fast_pattern chop with invalid values */
626
    snprintf(string, sizeof(string),
627
            "alert tcp any any -> any any "
628
            "(content:\"one\"; %s%sfast_pattern:65534,2; sid:1;)",
629
            sticky ? sticky : "", sticky ? "; " : " ");
630
    s = DetectEngineAppendSig(de_ctx, string);
631
    FAIL_IF_NOT_NULL(s);
632
    /* negated fast_pattern with distance */
633
    snprintf(string, sizeof(string),
634
            "alert tcp any any -> any any "
635
            "(content:\"one\"; %s%scontent:!\"two\"; fast_pattern:1,2; %s%sdistance:10; sid:1;)",
636
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
637
    s = DetectEngineAppendSig(de_ctx, string);
638
    FAIL_IF_NOT_NULL(s);
639
    /* negated fast_pattern with within */
640
    snprintf(string, sizeof(string),
641
            "alert tcp any any -> any any "
642
            "(content:\"one\"; %s%scontent:!\"two\"; fast_pattern:1,2; %s%swithin:10; sid:1;)",
643
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
644
    s = DetectEngineAppendSig(de_ctx, string);
645
    FAIL_IF_NOT_NULL(s);
646
    /* negated fast_pattern with depth */
647
    snprintf(string, sizeof(string),
648
            "alert tcp any any -> any any "
649
            "(content:\"one\"; %s%scontent:!\"two\"; fast_pattern:1,2; %s%sdepth:10; sid:1;)",
650
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
651
    s = DetectEngineAppendSig(de_ctx, string);
652
    FAIL_IF_NOT_NULL(s);
653
    /* negated fast_pattern with offset */
654
    snprintf(string, sizeof(string),
655
            "alert tcp any any -> any any "
656
            "(content:\"one\"; %s%scontent:!\"two\"; fast_pattern:1,2; %s%soffset:10; sid:1;)",
657
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
658
    s = DetectEngineAppendSig(de_ctx, string);
659
    FAIL_IF_NOT_NULL(s);
660
    DetectEngineCtxFree(de_ctx);
661
    PASS;
662
}
663

664
static int DetectFastPatternStickySingleFPOnly(const char *sticky, const int list)
665
{
666
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
667
    FAIL_IF_NULL(de_ctx);
668
    char string[1024];
669
    snprintf(string, sizeof(string),
670
            "alert tcp any any -> any any "
671
            "(%s%scontent:\"one\"; fast_pattern:only; sid:1;)",
672
            sticky ? sticky : "", sticky ? "; " : " ");
673
    Signature *s = DetectEngineAppendSig(de_ctx, string);
674
    FAIL_IF_NULL(s);
675
    SigMatch *sm = GetMatches(s, list);
676
    FAIL_IF_NULL(sm);
677
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
678
    DetectContentData *cd = (DetectContentData *)sm->ctx;
679
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
680
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
681
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY));
682
    DetectEngineCtxFree(de_ctx);
683
    PASS;
684
}
685

686
static int DetectFastPatternModifierFPOnly(const char *sticky, const int list)
687
{
688
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
689
    FAIL_IF_NULL(de_ctx);
690
    char string[1024];
691
    snprintf(string, sizeof(string),
692
            "alert tcp any any -> any any "
693
            "(content:\"one\"; %s%sfast_pattern:only; sid:1;)",
694
            sticky ? sticky : "", sticky ? "; " : " ");
695
    Signature *s = DetectEngineAppendSig(de_ctx, string);
696
    FAIL_IF_NULL(s);
697
    SigMatch *sm = GetMatches(s, list);
698
    FAIL_IF_NULL(sm);
699
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
700
    DetectContentData *cd = (DetectContentData *)sm->ctx;
701
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
702
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
703
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY));
704

705
    snprintf(string, sizeof(string),
706
            "alert tcp any any -> any any "
707
            "(content:\"one\"; %s%scontent:\"two\"; %s%sfast_pattern:only; sid:2;)",
708
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
709
    s = DetectEngineAppendSig(de_ctx, string);
710
    FAIL_IF_NULL(s);
711
    sm = GetMatches(s, list);
712
    FAIL_IF_NULL(sm);
713
    FAIL_IF_NULL(sm->next);
714
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
715
    cd = (DetectContentData *)sm->ctx;
716
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
717
    FAIL_IF_NOT(
718
            (cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) == 0);
719
    sm = sm->next;
720
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
721
    cd = (DetectContentData *)sm->ctx;
722
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
723
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
724
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY));
725

726
    snprintf(string, sizeof(string),
727
            "alert tcp any any -> any any "
728
            "(content:\"one\"; %s%scontent:\"two\"; distance:10; %s%scontent:\"three\"; "
729
            "%s%sfast_pattern:only; sid:3;)",
730
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ",
731
            sticky ? sticky : "", sticky ? "; " : " ");
732
    s = DetectEngineAppendSig(de_ctx, string);
733
    FAIL_IF_NULL(s);
734
    sm = GetMatches(s, list);
735
    FAIL_IF_NULL(sm);
736
    FAIL_IF_NULL(sm->next);
737
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
738
    cd = (DetectContentData *)sm->ctx;
739
    FAIL_IF(DETECT_CONTENT_IS_SINGLE(cd));
740
    FAIL_IF_NOT(
741
            (cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) == 0);
742
    sm = sm->next;
743
    FAIL_IF_NULL(sm->next);
744
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
745
    cd = (DetectContentData *)sm->ctx;
746
    FAIL_IF(DETECT_CONTENT_IS_SINGLE(cd));
747
    FAIL_IF_NOT(
748
            (cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) == 0);
749
    sm = sm->next;
750
    FAIL_IF_NOT_NULL(sm->next);
751
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
752
    cd = (DetectContentData *)sm->ctx;
753
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
754
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
755
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY));
756

757
    snprintf(string, sizeof(string),
758
            "alert tcp any any -> any any "
759
            "(content:\"one\"; %s%scontent:\"two\"; within:10; %s%scontent:\"three\"; "
760
            "%s%sfast_pattern:only; sid:4;)",
761
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ",
762
            sticky ? sticky : "", sticky ? "; " : " ");
763
    s = DetectEngineAppendSig(de_ctx, string);
764
    FAIL_IF_NULL(s);
765
    sm = GetMatches(s, list);
766
    FAIL_IF_NULL(sm);
767
    FAIL_IF_NULL(sm->next);
768
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
769
    cd = (DetectContentData *)sm->ctx;
770
    FAIL_IF(DETECT_CONTENT_IS_SINGLE(cd));
771
    FAIL_IF_NOT(
772
            (cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) == 0);
773
    sm = sm->next;
774
    FAIL_IF_NULL(sm->next);
775
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
776
    cd = (DetectContentData *)sm->ctx;
777
    FAIL_IF(DETECT_CONTENT_IS_SINGLE(cd));
778
    FAIL_IF_NOT(
779
            (cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) == 0);
780
    sm = sm->next;
781
    FAIL_IF_NOT_NULL(sm->next);
782
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
783
    cd = (DetectContentData *)sm->ctx;
784
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
785
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
786
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY));
787

788
    snprintf(string, sizeof(string),
789
            "alert tcp any any -> any any "
790
            "(content:\"one\"; %s%scontent:\"two\"; offset:10; %s%scontent:\"three\"; "
791
            "%s%sfast_pattern:only; sid:5;)",
792
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ",
793
            sticky ? sticky : "", sticky ? "; " : " ");
794
    s = DetectEngineAppendSig(de_ctx, string);
795
    FAIL_IF_NULL(s);
796
    sm = GetMatches(s, list);
797
    FAIL_IF_NULL(sm);
798
    FAIL_IF_NULL(sm->next);
799
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
800
    cd = (DetectContentData *)sm->ctx;
801
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
802
    FAIL_IF_NOT(
803
            (cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) == 0);
804
    sm = sm->next;
805
    FAIL_IF_NULL(sm->next);
806
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
807
    cd = (DetectContentData *)sm->ctx;
808
    FAIL_IF(DETECT_CONTENT_IS_SINGLE(cd));
809
    FAIL_IF_NOT(
810
            (cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) == 0);
811
    sm = sm->next;
812
    FAIL_IF_NOT_NULL(sm->next);
813
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
814
    cd = (DetectContentData *)sm->ctx;
815
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
816
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
817
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY));
818

819
    snprintf(string, sizeof(string),
820
            "alert tcp any any -> any any "
821
            "(content:\"one\"; %s%scontent:\"two\"; depth:10; %s%scontent:\"three\"; "
822
            "%s%sfast_pattern:only; sid:6;)",
823
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ",
824
            sticky ? sticky : "", sticky ? "; " : " ");
825
    s = DetectEngineAppendSig(de_ctx, string);
826
    FAIL_IF_NULL(s);
827
    sm = GetMatches(s, list);
828
    FAIL_IF_NULL(sm);
829
    FAIL_IF_NULL(sm->next);
830
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
831
    cd = (DetectContentData *)sm->ctx;
832
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
833
    FAIL_IF_NOT(
834
            (cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) == 0);
835
    sm = sm->next;
836
    FAIL_IF_NULL(sm->next);
837
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
838
    cd = (DetectContentData *)sm->ctx;
839
    FAIL_IF(DETECT_CONTENT_IS_SINGLE(cd));
840
    FAIL_IF_NOT(
841
            (cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) == 0);
842
    sm = sm->next;
843
    FAIL_IF_NOT_NULL(sm->next);
844
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
845
    cd = (DetectContentData *)sm->ctx;
846
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
847
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
848
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY));
849

850
    snprintf(string, sizeof(string),
851
            "alert tcp any any -> any any "
852
            "(content:!\"one\"; %s%sfast_pattern; content:\"two\"; depth:10; %s%ssid:7;)",
853
            sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
854
    s = DetectEngineAppendSig(de_ctx, string);
855
    FAIL_IF_NULL(s);
856
    sm = GetMatches(s, list);
857
    FAIL_IF_NULL(sm);
858
    FAIL_IF_NULL(sm->next);
859
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
860
    cd = (DetectContentData *)sm->ctx;
861
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
862
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY |
863
                                     DETECT_CONTENT_NEGATED)) ==
864
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_NEGATED));
865
    sm = sm->next;
866
    FAIL_IF_NOT_NULL(sm->next);
867
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
868
    cd = (DetectContentData *)sm->ctx;
869
    FAIL_IF(DETECT_CONTENT_IS_SINGLE(cd));
870
    FAIL_IF_NOT(
871
            (cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_ONLY)) == 0);
872

873
    DetectEngineCtxFree(de_ctx);
874
    PASS;
875
}
876

877
static int DetectFastPatternStickyFPChop(const char *sticky, const int list)
878
{
879
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
880
    FAIL_IF_NULL(de_ctx);
881
    char string[1024];
882
    snprintf(string, sizeof(string),
883
            "alert tcp any any -> any any "
884
            "(%s%scontent:\"onetwothree\"; fast_pattern:3,4; sid:1;)",
885
            sticky ? sticky : "", sticky ? "; " : " ");
886
    Signature *s = DetectEngineAppendSig(de_ctx, string);
887
    FAIL_IF_NULL(s);
888
    SigMatch *sm = GetMatches(s, list);
889
    FAIL_IF_NULL(sm);
890
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
891
    DetectContentData *cd = (DetectContentData *)sm->ctx;
892
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
893
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_CHOP |
894
                                     DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
895
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_CHOP));
896
    FAIL_IF_NOT(cd->fp_chop_offset == 3);
897
    FAIL_IF_NOT(cd->fp_chop_len == 4);
898

899
    snprintf(string, sizeof(string),
900
            "alert tcp any any -> any any "
901
            "(%s%scontent:\"onetwothree\"; fast_pattern:3,4; content:\"xyz\"; distance:10; sid:2;)",
902
            sticky ? sticky : "", sticky ? "; " : " ");
903
    s = DetectEngineAppendSig(de_ctx, string);
904
    FAIL_IF_NULL(s);
905
    sm = GetMatches(s, list);
906
    FAIL_IF_NULL(sm);
907
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
908
    cd = (DetectContentData *)sm->ctx;
909
    FAIL_IF(DETECT_CONTENT_IS_SINGLE(cd));
910
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_CHOP |
911
                                     DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
912
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_CHOP));
913
    FAIL_IF_NOT(cd->fp_chop_offset == 3);
914
    FAIL_IF_NOT(cd->fp_chop_len == 4);
915

916
    DetectEngineCtxFree(de_ctx);
917
    PASS;
918
}
919

920
static int DetectFastPatternModifierFPChop(const char *sticky, const int list)
921
{
922
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
923
    FAIL_IF_NULL(de_ctx);
924
    char string[1024];
925
    snprintf(string, sizeof(string),
926
            "alert tcp any any -> any any "
927
            "(content:\"onetwothree\"; %s%sfast_pattern:3,4; sid:1;)",
928
            sticky ? sticky : "", sticky ? "; " : " ");
929
    Signature *s = DetectEngineAppendSig(de_ctx, string);
930
    FAIL_IF_NULL(s);
931
    SigMatch *sm = GetMatches(s, list);
932
    FAIL_IF_NULL(sm);
933
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
934
    DetectContentData *cd = (DetectContentData *)sm->ctx;
935
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
936
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_CHOP |
937
                                     DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
938
                (DETECT_CONTENT_FAST_PATTERN | DETECT_CONTENT_FAST_PATTERN_CHOP));
939
    FAIL_IF_NOT(cd->fp_chop_offset == 3);
940
    FAIL_IF_NOT(cd->fp_chop_len == 4);
941

942
    snprintf(string, sizeof(string),
943
            "alert tcp any any -> any any "
944
            "(content:!\"onetwothree\"; %s%sfast_pattern:3,4; sid:2;)",
945
            sticky ? sticky : "", sticky ? "; " : " ");
946
    s = DetectEngineAppendSig(de_ctx, string);
947
    FAIL_IF_NULL(s);
948
    sm = GetMatches(s, list);
949
    FAIL_IF_NULL(sm);
950
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
951
    cd = (DetectContentData *)sm->ctx;
952
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
953
    FAIL_IF_NOT((cd->flags & (DETECT_CONTENT_NEGATED | DETECT_CONTENT_FAST_PATTERN |
954
                                     DETECT_CONTENT_FAST_PATTERN_CHOP |
955
                                     DETECT_CONTENT_FAST_PATTERN_ONLY)) ==
956
                (DETECT_CONTENT_NEGATED | DETECT_CONTENT_FAST_PATTERN |
957
                        DETECT_CONTENT_FAST_PATTERN_CHOP));
958
    FAIL_IF_NOT(cd->fp_chop_offset == 3);
959
    FAIL_IF_NOT(cd->fp_chop_len == 4);
960

961
    DetectEngineCtxFree(de_ctx);
962
    PASS;
963
}
964

965
/**
966
 * \test Checks if a fast_pattern is registered in a Signature
967
 */
968
static int DetectFastPatternTest01(void)
969
{
970
    FAIL_IF_NOT(DetectFastPatternStickySingle(NULL, DETECT_SM_LIST_PMATCH));
971
    FAIL_IF_NOT(DetectFastPatternModifierSingle(NULL, DETECT_SM_LIST_PMATCH));
972
    FAIL_IF_NOT(DetectFastPatternStickySingleNoFP(NULL, DETECT_SM_LIST_PMATCH));
973
    FAIL_IF_NOT(DetectFastPatternModifierSingleNoFP(NULL, DETECT_SM_LIST_PMATCH));
974
    FAIL_IF_NOT(DetectFastPatternStickySingleBadArg(NULL));
975
    FAIL_IF_NOT(DetectFastPatternModifierBadRules(NULL));
976
    FAIL_IF_NOT(DetectFastPatternStickySingleFPOnly(NULL, DETECT_SM_LIST_PMATCH));
977
    FAIL_IF_NOT(DetectFastPatternModifierFPOnly(NULL, DETECT_SM_LIST_PMATCH));
978
    FAIL_IF_NOT(DetectFastPatternStickyFPChop(NULL, DETECT_SM_LIST_PMATCH));
979
    FAIL_IF_NOT(DetectFastPatternModifierFPChop(NULL, DETECT_SM_LIST_PMATCH));
980

981
    struct {
982
        const char *buffer_name;
983
        const char *sb_name;
984
        const char *mod_name;
985
    } keywords[] = {
986
        { "file_data", "file.data", NULL },
987
        { "http_uri", "http.uri", "http_uri" },
988
        { "http_raw_uri", "http.uri.raw", "http_raw_uri" },
989
        { "http_user_agent", "http.user_agent", "http_user_agent" },
990
        { "http_header", "http.header", "http_header" },
991
        // http_raw_header requires sigs to have a direction
992
        //{ "http_raw_header", "http.header.raw", "http_raw_header" },
993
        { "http_method", "http.method", "http_method" },
994
        { "http_cookie", "http.cookie", "http_cookie" },
995
        { "http_host", "http.host", "http_host" },
996
        { "http_raw_host", "http.host.raw", "http_raw_host" },
997
        { "http_stat_code", "http.stat_code", "http_stat_code" },
998
        { "http_stat_msg", "http.stat_msg", "http_stat_msg" },
999
        { "http_client_body", "http.request_body", "http_client_body" },
1000
        { NULL, NULL, NULL },
1001
    };
1002

1003
    for (int i = 0; keywords[i].buffer_name != NULL; i++) {
1004
        const int list_id = DetectBufferTypeGetByName(keywords[i].buffer_name);
1005
        FAIL_IF(list_id == -1);
1006

1007
        const char *k = keywords[i].sb_name;
1008
        if (k) {
1009
            FAIL_IF_NOT(DetectFastPatternStickySingle(k, list_id));
1010
            FAIL_IF_NOT(DetectFastPatternStickySingleNoFP(k, list_id));
1011
            FAIL_IF_NOT(DetectFastPatternStickySingleBadArg(k));
1012
            FAIL_IF_NOT(DetectFastPatternStickySingleFPOnly(k, list_id));
1013
            FAIL_IF_NOT(DetectFastPatternStickyFPChop(k, list_id));
1014
        }
1015
        k = keywords[i].mod_name;
1016
        if (k) {
1017
            FAIL_IF_NOT(DetectFastPatternModifierSingle(k, list_id));
1018
            FAIL_IF_NOT(DetectFastPatternModifierSingleNoFP(k, list_id));
1019
            FAIL_IF_NOT(DetectFastPatternModifierBadRules(k));
1020
            FAIL_IF_NOT(DetectFastPatternModifierFPOnly(k, list_id));
1021
            FAIL_IF_NOT(DetectFastPatternModifierFPChop(k, list_id));
1022
        }
1023
    }
1024

1025
    PASS;
1026
}
1027

1028
/**
1029
 * \test Checks to make sure that other sigs work that should when fast_pattern is inspecting on the
1030
 * same payload
1031
 *
1032
 */
1033
static int DetectFastPatternTest14(void)
1034
{
1035
    uint8_t *buf = (uint8_t *)"Dummy is our name.  Oh yes.  From right here "
1036
                              "right now, all the way to hangover.  right.  strings5_imp now here "
1037
                              "comes our dark knight strings_string5.  Yes here is our dark knight";
1038
    uint16_t buflen = strlen((char *)buf);
1039
    ThreadVars th_v;
1040
    DetectEngineThreadCtx *det_ctx = NULL;
1041

1042
    memset(&th_v, 0, sizeof(th_v));
1043
    StatsThreadInit(&th_v.stats);
1044
    Packet *p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
1045
    FAIL_IF_NULL(p);
1046

1047
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1048
    FAIL_IF_NULL(de_ctx);
1049
    de_ctx->flags |= DE_QUIET;
1050

1051
    FlowInitConfig(FLOW_QUIET);
1052

1053
    Signature *s = DetectEngineAppendSig(de_ctx,
1054
            "alert tcp any any -> any any "
1055
            "(msg:\"fast_pattern test\"; content:\"strings_string5\"; content:\"knight\"; "
1056
            "fast_pattern; sid:1;)");
1057
    FAIL_IF_NULL(s);
1058

1059
    s = DetectEngineAppendSig(de_ctx,
1060
            "alert tcp any any -> any any "
1061
            "(msg:\"test different content\"; content:\"Dummy is our name\"; sid:2;)");
1062
    FAIL_IF_NULL(s);
1063

1064
    SigGroupBuild(de_ctx);
1065
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1066

1067
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1068
    FAIL_IF_NOT(PacketAlertCheck(p, 1));
1069
    FAIL_IF_NOT(PacketAlertCheck(p, 2));
1070

1071
    UTHFreePackets(&p, 1);
1072
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1073
    DetectEngineCtxFree(de_ctx);
1074
    FlowShutdown();
1075
    StatsThreadCleanup(&th_v.stats);
1076
    PASS;
1077
}
1078

1079
/**
1080
 * Unittest to check
1081
 * - if we assign different content_ids to duplicate patterns, but one of the
1082
 *   patterns has a fast_pattern chop set.
1083
 * - if 2 unique patterns get unique ids.
1084
 * - if 2 duplicate patterns, with no chop set get unique ids.
1085
 */
1086
static int DetectFastPatternTest671(void)
1087
{
1088
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1089
    FAIL_IF_NULL(de_ctx);
1090
    de_ctx->flags |= DE_QUIET;
1091

1092
    Signature *s[6];
1093
    s[0] = DetectEngineAppendSig(
1094
            de_ctx, "alert tcp any any -> any any (content:\"onetwothreefour\"; sid:1;)");
1095
    FAIL_IF_NULL(s[0]);
1096
    s[1] = DetectEngineAppendSig(
1097
            de_ctx, "alert tcp any any -> any any (content:\"onetwothreefour\"; sid:2;)");
1098
    FAIL_IF_NULL(s[1]);
1099
    s[2] = DetectEngineAppendSig(
1100
            de_ctx, "alert tcp any any -> any any (content:\"uniquepattern\"; sid:3;)");
1101
    FAIL_IF_NULL(s[2]);
1102
    s[3] = DetectEngineAppendSig(de_ctx,
1103
            "alert tcp any any -> any any (content:\"onetwothreefour\"; fast_pattern:3,5; sid:4;)");
1104
    FAIL_IF_NULL(s[3]);
1105
    s[4] = DetectEngineAppendSig(
1106
            de_ctx, "alert tcp any any -> any any (content:\"twoth\"; sid:5;)");
1107
    FAIL_IF_NULL(s[4]);
1108
    s[5] = DetectEngineAppendSig(de_ctx,
1109
            "alert tcp any any -> any any (content:\"onetwothreefour\"; fast_pattern:0,15; "
1110
            "sid:6;)");
1111
    FAIL_IF_NULL(s[5]);
1112

1113
    SigGroupBuild(de_ctx);
1114

1115
    SigMatchData *smd = s[0]->sm_arrays[DETECT_SM_LIST_PMATCH];
1116
    DetectContentData *cd = (DetectContentData *)smd->ctx;
1117
    FAIL_IF(cd->id != 0);
1118

1119
    smd = s[1]->sm_arrays[DETECT_SM_LIST_PMATCH];
1120
    cd = (DetectContentData *)smd->ctx;
1121
    FAIL_IF(cd->id != 0);
1122

1123
    smd = s[2]->sm_arrays[DETECT_SM_LIST_PMATCH];
1124
    cd = (DetectContentData *)smd->ctx;
1125
    FAIL_IF(cd->id != 2);
1126

1127
    smd = s[3]->sm_arrays[DETECT_SM_LIST_PMATCH];
1128
    cd = (DetectContentData *)smd->ctx;
1129
    FAIL_IF(cd->id != 1);
1130

1131
    smd = s[4]->sm_arrays[DETECT_SM_LIST_PMATCH];
1132
    cd = (DetectContentData *)smd->ctx;
1133
    FAIL_IF(cd->id != 1);
1134

1135
    smd = s[5]->sm_arrays[DETECT_SM_LIST_PMATCH];
1136
    cd = (DetectContentData *)smd->ctx;
1137
    FAIL_IF(cd->id != 0);
1138

1139
    DetectEngineCtxFree(de_ctx);
1140
    PASS;
1141
}
1142

1143
static int DetectFastPatternPrefilter(void)
1144
{
1145
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
1146
    FAIL_IF_NULL(de_ctx);
1147
    const char *string = "alert tcp any any -> any any "
1148
                         "(content:\"one\"; prefilter; sid:1;)";
1149
    Signature *s = DetectEngineAppendSig(de_ctx, string);
1150
    FAIL_IF_NULL(s);
1151
    SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_PMATCH];
1152
    FAIL_IF_NULL(sm);
1153
    FAIL_IF_NOT(sm->type == DETECT_CONTENT);
1154
    DetectContentData *cd = (DetectContentData *)sm->ctx;
1155
    FAIL_IF_NOT(DETECT_CONTENT_IS_SINGLE(cd));
1156
    FAIL_IF_NOT((cd->flags & DETECT_CONTENT_FAST_PATTERN) == DETECT_CONTENT_FAST_PATTERN);
1157
    DetectEngineCtxFree(de_ctx);
1158
    PASS;
1159
}
1160

1161
static void DetectFastPatternRegisterTests(void)
1162
{
1163
    UtRegisterTest("DetectFastPatternTest01", DetectFastPatternTest01);
1164
    UtRegisterTest("DetectFastPatternTest14", DetectFastPatternTest14);
1165
    /* Unittest to check
1166
     * - if we assign different content_ids to duplicate patterns, but one of the
1167
     *   patterns has a fast_pattern chop set.
1168
     * - if 2 unique patterns get unique ids.
1169
     * - if 2 duplicate patterns, with no chop set get unique ids.
1170
     */
1171
    UtRegisterTest("DetectFastPatternTest671", DetectFastPatternTest671);
1172

1173
    UtRegisterTest("DetectFastPatternPrefilter", DetectFastPatternPrefilter);
1174
}
1175
#endif
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