• 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

57.2
/src/detect-engine.c
1
/* Copyright (C) 2007-2022 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

24
#include "suricata-common.h"
25
#include "suricata.h"
26
#include "detect.h"
27
#include "flow.h"
28
#include "flow-private.h"
29
#include "flow-util.h"
30
#include "flow-worker.h"
31
#include "conf.h"
32
#include "conf-yaml-loader.h"
33
#include "datasets.h"
34

35
#include "app-layer-parser.h"
36
#include "app-layer-events.h"
37
#include "app-layer-htp.h"
38

39
#include "detect-parse.h"
40
#include "detect-engine-sigorder.h"
41

42
#include "detect-engine-build.h"
43
#include "detect-engine-siggroup.h"
44
#include "detect-engine-address.h"
45
#include "detect-engine-port.h"
46
#include "detect-engine-prefilter.h"
47
#include "detect-engine-mpm.h"
48
#include "detect-engine-iponly.h"
49
#include "detect-engine-tag.h"
50
#include "detect-engine-frame.h"
51

52
#include "detect-engine-file.h"
53

54
#include "detect-engine.h"
55
#include "detect-engine-state.h"
56
#include "detect-engine-payload.h"
57
#include "detect-fast-pattern.h"
58
#include "detect-byte-extract.h"
59
#include "detect-content.h"
60
#include "detect-uricontent.h"
61
#include "detect-tcphdr.h"
62
#include "detect-engine-threshold.h"
63
#include "detect-engine-content-inspection.h"
64

65
#include "detect-engine-loader.h"
66

67
#include "detect-engine-alert.h"
68

69
#include "util-classification-config.h"
70
#include "util-reference-config.h"
71
#include "util-threshold-config.h"
72
#include "util-error.h"
73
#include "util-hash.h"
74
#include "util-byte.h"
75
#include "util-debug.h"
76
#include "util-unittest.h"
77
#include "util-action.h"
78
#include "util-magic.h"
79
#include "util-signal.h"
80
#include "util-spm.h"
81
#include "util-device-private.h"
82
#include "util-var-name.h"
83
#include "util-path.h"
84
#include "util-profiling.h"
85
#include "util-validate.h"
86
#include "util-hash-string.h"
87
#include "util-enum.h"
88
#include "util-conf.h"
89

90
#include "tm-threads.h"
91
#include "runmodes.h"
92

93
#include "reputation.h"
94

95
#define DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT 3000
35,469✔
96

97
static int DetectEngineCtxLoadConf(DetectEngineCtx *);
98

99
static DetectEngineMasterCtx g_master_de_ctx = { SCMUTEX_INITIALIZER,
100
    0, 99, NULL, NULL, TENANT_SELECTOR_UNKNOWN, NULL, NULL, 0};
101

102
static uint32_t TenantIdHash(HashTable *h, void *data, uint16_t data_len);
103
static char TenantIdCompare(void *d1, uint16_t d1_len, void *d2, uint16_t d2_len);
104
static void TenantIdFree(void *d);
105
static uint32_t DetectEngineTenantGetIdFromLivedev(const void *ctx, const Packet *p);
106
static uint32_t DetectEngineTenantGetIdFromVlanId(const void *ctx, const Packet *p);
107
static uint32_t DetectEngineTenantGetIdFromPcap(const void *ctx, const Packet *p);
108

109
static bool DetectEngineMultiTenantEnabledWithLock(void);
110
static DetectEngineAppInspectionEngine *g_app_inspect_engines = NULL;
111
static DetectEnginePktInspectionEngine *g_pkt_inspect_engines = NULL;
112
static DetectEngineFrameInspectionEngine *g_frame_inspect_engines = NULL;
113

114
// clang-format off
115
// rule types documentation tag start: SignatureProperties
116
const struct SignatureProperties signature_properties[SIG_TYPE_MAX] = {
117
    /* SIG_TYPE_NOT_SET */      { SIG_PROP_FLOW_ACTION_PACKET, },
118
    /* SIG_TYPE_IPONLY */       { SIG_PROP_FLOW_ACTION_FLOW, },
119
    /* SIG_TYPE_LIKE_IPONLY */  { SIG_PROP_FLOW_ACTION_FLOW, },
120
    /* SIG_TYPE_PDONLY */       { SIG_PROP_FLOW_ACTION_FLOW, },
121
    /* SIG_TYPE_DEONLY */       { SIG_PROP_FLOW_ACTION_PACKET, },
122
    /* SIG_TYPE_PKT */          { SIG_PROP_FLOW_ACTION_PACKET, },
123
    /* SIG_TYPE_PKT_STREAM */   { SIG_PROP_FLOW_ACTION_FLOW_IF_STATEFUL, },
124
    /* SIG_TYPE_STREAM */       { SIG_PROP_FLOW_ACTION_FLOW_IF_STATEFUL, },
125
    /* SIG_TYPE_APPLAYER */     { SIG_PROP_FLOW_ACTION_FLOW, },
126
    /* SIG_TYPE_APP_TX */       { SIG_PROP_FLOW_ACTION_FLOW, },
127
};
128
// rule types documentation tag end: SignatureProperties
129
// clang-format on
130

131
const char *DetectTableToString(enum DetectTable table)
132
{
×
133
    switch (table) {
×
134
        case DETECT_TABLE_NOT_SET:
×
135
            return "not_set";
×
136
        case DETECT_TABLE_PACKET_PRE_FLOW:
×
137
            return "pre_flow";
×
138
        case DETECT_TABLE_PACKET_PRE_STREAM:
×
139
            return "pre_stream";
×
140
        case DETECT_TABLE_PACKET_FILTER:
×
141
            return "packet_filter";
×
142
        case DETECT_TABLE_PACKET_TD:
×
143
            return "packet_td";
×
144
        case DETECT_TABLE_APP_FILTER:
×
145
            return "app_filter";
×
146
        case DETECT_TABLE_APP_TD:
×
147
            return "app_td";
×
148
        default:
×
149
            return "unknown";
×
150
    }
×
151
}
×
152

153
/** \brief register inspect engine at start up time
154
 *
155
 *  \note errors are fatal */
156
void DetectPktInspectEngineRegister(const char *name,
157
        InspectionBufferGetPktDataPtr GetPktData,
158
        InspectionBufferPktInspectFunc Callback)
159
{
27✔
160
    DetectBufferTypeRegister(name);
27✔
161
    const int sm_list = DetectBufferTypeGetByName(name);
27✔
162
    if (sm_list == -1) {
27✔
163
        FatalError("failed to register inspect engine %s", name);
×
164
    }
×
165

166
    if ((sm_list < DETECT_SM_LIST_MATCH) || (sm_list >= SHRT_MAX) ||
27✔
167
        (Callback == NULL))
27✔
168
    {
×
169
        SCLogError("Invalid arguments");
×
170
        BUG_ON(1);
×
171
    }
×
172

173
    DetectEnginePktInspectionEngine *new_engine = SCCalloc(1, sizeof(*new_engine));
27✔
174
    if (unlikely(new_engine == NULL)) {
27✔
175
        FatalError("failed to register inspect engine %s: %s", name, strerror(errno));
×
176
    }
×
177
    new_engine->sm_list = (uint16_t)sm_list;
27✔
178
    new_engine->sm_list_base = (uint16_t)sm_list;
27✔
179
    new_engine->v1.Callback = Callback;
27✔
180
    new_engine->v1.GetData = GetPktData;
27✔
181

182
    if (g_pkt_inspect_engines == NULL) {
27✔
183
        g_pkt_inspect_engines = new_engine;
3✔
184
    } else {
24✔
185
        DetectEnginePktInspectionEngine *t = g_pkt_inspect_engines;
24✔
186
        while (t->next != NULL) {
108✔
187
            t = t->next;
84✔
188
        }
84✔
189

190
        t->next = new_engine;
24✔
191
    }
24✔
192
}
27✔
193

194
/** \brief register inspect engine at start up time
195
 *
196
 *  \note errors are fatal */
197
static void AppLayerInspectEngineRegisterInternal(const char *name, AppProto alproto, uint32_t dir,
198
        int progress, InspectEngineFuncPtr Callback, InspectionBufferGetDataPtr GetData,
199
        InspectionSingleBufferGetDataPtr GetDataSingle,
200
        InspectionMultiBufferGetDataPtr GetMultiData)
201
{
2,060✔
202
    BUG_ON(progress >= 48);
2,060✔
203

204
    DetectBufferTypeRegister(name);
2,060✔
205
    const int sm_list = DetectBufferTypeGetByName(name);
2,060✔
206
    if (sm_list == -1) {
2,060✔
207
        FatalError("failed to register inspect engine %s", name);
×
208
    }
×
209
    SCLogDebug("name %s id %d", name, sm_list);
2,060✔
210

211
    if ((alproto == ALPROTO_FAILED) || (!(dir == SIG_FLAG_TOSERVER || dir == SIG_FLAG_TOCLIENT)) ||
2,060✔
212
            (sm_list < DETECT_SM_LIST_MATCH) || (sm_list >= SHRT_MAX) ||
2,060✔
213
            (progress < 0 || progress >= SHRT_MAX) || (Callback == NULL)) {
2,060✔
214
        SCLogError("Invalid arguments");
×
215
        BUG_ON(1);
×
216
    } else if (Callback == DetectEngineInspectBufferGeneric && GetData == NULL) {
2,060✔
217
        SCLogError("Invalid arguments: must register "
×
218
                   "GetData with DetectEngineInspectBufferGeneric");
×
219
        BUG_ON(1);
×
220
    } else if (Callback == DetectEngineInspectBufferSingle && GetDataSingle == NULL) {
2,060✔
221
        SCLogError("Invalid arguments: must register "
×
222
                   "GetData with DetectEngineInspectBufferGeneric");
×
223
        BUG_ON(1);
×
224
    } else if (Callback == DetectEngineInspectMultiBufferGeneric && GetMultiData == NULL) {
2,060✔
225
        SCLogError("Invalid arguments: must register "
×
226
                   "GetData with DetectEngineInspectMultiBufferGeneric");
×
227
        BUG_ON(1);
×
228
    }
×
229

230
    uint8_t direction;
2,060✔
231
    if (dir == SIG_FLAG_TOSERVER) {
2,060✔
232
        direction = 0;
1,111✔
233
    } else {
1,111✔
234
        direction = 1;
949✔
235
    }
949✔
236
    // every DNS or HTTP2 can be accessed from DOH2
237
    if (alproto == ALPROTO_HTTP2 || alproto == ALPROTO_DNS) {
2,060✔
238
        AppLayerInspectEngineRegisterInternal(
231✔
239
                name, ALPROTO_DOH2, dir, progress, Callback, GetData, GetDataSingle, GetMultiData);
231✔
240
    }
231✔
241

242
    DetectEngineAppInspectionEngine *new_engine =
2,060✔
243
            SCCalloc(1, sizeof(DetectEngineAppInspectionEngine));
2,060✔
244
    if (unlikely(new_engine == NULL)) {
2,060✔
245
        exit(EXIT_FAILURE);
×
246
    }
×
247
    new_engine->alproto = alproto;
2,060✔
248
    new_engine->dir = direction;
2,060✔
249
    new_engine->sm_list = (uint16_t)sm_list;
2,060✔
250
    new_engine->sm_list_base = (uint16_t)sm_list;
2,060✔
251
    new_engine->progress = (int16_t)progress;
2,060✔
252
    new_engine->v2.Callback = Callback;
2,060✔
253
    if (Callback == DetectEngineInspectBufferGeneric) {
2,060✔
254
        new_engine->v2.GetData = GetData;
396✔
255
    } else if (Callback == DetectEngineInspectBufferSingle) {
1,664✔
256
        new_engine->v2.GetDataSingle = GetDataSingle;
242✔
257
    } else if (Callback == DetectEngineInspectMultiBufferGeneric) {
1,422✔
258
        new_engine->v2.GetMultiData = GetMultiData;
279✔
259
    }
279✔
260

261
    if (g_app_inspect_engines == NULL) {
2,060✔
262
        g_app_inspect_engines = new_engine;
3✔
263
    } else {
2,057✔
264
        DetectEngineAppInspectionEngine *t = g_app_inspect_engines;
2,057✔
265
        while (t->next != NULL) {
706,238✔
266
            t = t->next;
704,181✔
267
        }
704,181✔
268

269
        t->next = new_engine;
2,057✔
270
    }
2,057✔
271
}
2,060✔
272

273
void DetectAppLayerInspectEngineRegister(const char *name, AppProto alproto, uint32_t dir,
274
        int progress, InspectEngineFuncPtr Callback, InspectionBufferGetDataPtr GetData)
275
{
1,359✔
276
    /* before adding, check that we don't add a duplicate entry, which will
277
     * propagate all the way into the packet runtime if allowed. */
278
    DetectEngineAppInspectionEngine *t = g_app_inspect_engines;
1,359✔
279
    while (t != NULL) {
382,738✔
280
        const uint32_t t_direction = t->dir == 0 ? SIG_FLAG_TOSERVER : SIG_FLAG_TOCLIENT;
381,379✔
281
        const int sm_list = DetectBufferTypeGetByName(name);
381,379✔
282

283
        if (t->sm_list == sm_list && t->alproto == alproto && t_direction == dir &&
381,379✔
284
                t->progress == progress && t->v2.Callback == Callback && t->v2.GetData == GetData) {
381,379✔
285
            DEBUG_VALIDATE_BUG_ON(1);
×
286
            return;
×
287
        }
×
288
        t = t->next;
381,379✔
289
    }
381,379✔
290

291
    AppLayerInspectEngineRegisterInternal(
1,359✔
292
            name, alproto, dir, progress, Callback, GetData, NULL, NULL);
1,359✔
293
}
1,359✔
294

295
void DetectAppLayerInspectEngineRegisterSingle(const char *name, AppProto alproto, uint32_t dir,
296
        int progress, InspectEngineFuncPtr Callback, InspectionSingleBufferGetDataPtr GetData)
297
{
242✔
298
    /* before adding, check that we don't add a duplicate entry, which will
299
     * propagate all the way into the packet runtime if allowed. */
300
    DetectEngineAppInspectionEngine *t = g_app_inspect_engines;
242✔
301
    while (t != NULL) {
141,924✔
302
        const uint32_t t_direction = t->dir == 0 ? SIG_FLAG_TOSERVER : SIG_FLAG_TOCLIENT;
141,682✔
303
        const int sm_list = DetectBufferTypeGetByName(name);
141,682✔
304

305
        if (t->sm_list == sm_list && t->alproto == alproto && t_direction == dir &&
141,682✔
306
                t->progress == progress && t->v2.Callback == Callback &&
141,682✔
307
                t->v2.GetDataSingle == GetData) {
141,682✔
308
            DEBUG_VALIDATE_BUG_ON(1);
×
309
            return;
×
310
        }
×
311
        t = t->next;
141,682✔
312
    }
141,682✔
313

314
    AppLayerInspectEngineRegisterInternal(
242✔
315
            name, alproto, dir, progress, Callback, NULL, GetData, NULL);
242✔
316
}
242✔
317

318
/* copy an inspect engine with transforms to a new list id. */
319
static void DetectAppLayerInspectEngineCopy(
320
        DetectEngineCtx *de_ctx,
321
        int sm_list, int new_list,
322
        const DetectEngineTransforms *transforms)
323
{
63,634✔
324
    const DetectEngineAppInspectionEngine *t = g_app_inspect_engines;
63,634✔
325
    while (t) {
43,840,356✔
326
        if (t->sm_list == sm_list) {
43,776,722✔
327
            DetectEngineAppInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEngineAppInspectionEngine));
532,302✔
328
            if (unlikely(new_engine == NULL)) {
532,302✔
329
                exit(EXIT_FAILURE);
×
330
            }
×
331
            new_engine->alproto = t->alproto;
532,302✔
332
            new_engine->dir = t->dir;
532,302✔
333
            DEBUG_VALIDATE_BUG_ON(new_list < 0 || new_list > UINT16_MAX);
532,302✔
334
            new_engine->sm_list = (uint16_t)new_list; /* use new list id */
532,302✔
335
            DEBUG_VALIDATE_BUG_ON(sm_list < 0 || sm_list > UINT16_MAX);
532,302✔
336
            new_engine->sm_list_base = (uint16_t)sm_list;
532,302✔
337
            new_engine->progress = t->progress;
532,302✔
338
            new_engine->v2 = t->v2;
532,302✔
339
            new_engine->v2.transforms = transforms; /* assign transforms */
532,302✔
340

341
            if (de_ctx->app_inspect_engines == NULL) {
532,302✔
342
                de_ctx->app_inspect_engines = new_engine;
×
343
            } else {
532,302✔
344
                DetectEngineAppInspectionEngine *list = de_ctx->app_inspect_engines;
532,302✔
345
                while (list->next != NULL) {
1,047,775,445✔
346
                    list = list->next;
1,047,243,143✔
347
                }
1,047,243,143✔
348

349
                list->next = new_engine;
532,302✔
350
            }
532,302✔
351
        }
532,302✔
352
        t = t->next;
43,776,722✔
353
    }
43,776,722✔
354
}
63,634✔
355

356
/* copy inspect engines from global registrations to de_ctx list */
357
static void DetectAppLayerInspectEngineCopyListToDetectCtx(DetectEngineCtx *de_ctx)
358
{
35,469✔
359
    const DetectEngineAppInspectionEngine *t = g_app_inspect_engines;
35,469✔
360
    DetectEngineAppInspectionEngine *list = de_ctx->app_inspect_engines;
35,469✔
361
    while (t) {
24,438,079✔
362
        DetectEngineAppInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEngineAppInspectionEngine));
24,402,610✔
363
        if (unlikely(new_engine == NULL)) {
24,402,610✔
364
            exit(EXIT_FAILURE);
×
365
        }
×
366
        new_engine->alproto = t->alproto;
24,402,610✔
367
        new_engine->dir = t->dir;
24,402,610✔
368
        new_engine->sm_list = t->sm_list;
24,402,610✔
369
        new_engine->sm_list_base = t->sm_list;
24,402,610✔
370
        new_engine->progress = t->progress;
24,402,610✔
371
        new_engine->v2 = t->v2;
24,402,610✔
372

373
        if (list == NULL) {
24,402,610✔
374
            de_ctx->app_inspect_engines = new_engine;
35,469✔
375
        } else {
24,367,141✔
376
            list->next = new_engine;
24,367,141✔
377
        }
24,367,141✔
378
        list = new_engine;
24,402,610✔
379

380
        t = t->next;
24,402,610✔
381
    }
24,402,610✔
382
}
35,469✔
383

384
/* copy an inspect engine with transforms to a new list id. */
385
static void DetectPktInspectEngineCopy(
386
        DetectEngineCtx *de_ctx,
387
        int sm_list, int new_list,
388
        const DetectEngineTransforms *transforms)
389
{
1,531✔
390
    const DetectEnginePktInspectionEngine *t = g_pkt_inspect_engines;
1,531✔
391
    while (t) {
15,310✔
392
        if (t->sm_list == sm_list) {
13,779✔
393
            DetectEnginePktInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEnginePktInspectionEngine));
1,531✔
394
            if (unlikely(new_engine == NULL)) {
1,531✔
395
                exit(EXIT_FAILURE);
×
396
            }
×
397
            DEBUG_VALIDATE_BUG_ON(new_list < 0 || new_list > UINT16_MAX);
1,531✔
398
            new_engine->sm_list = (uint16_t)new_list; /* use new list id */
1,531✔
399
            DEBUG_VALIDATE_BUG_ON(sm_list < 0 || sm_list > UINT16_MAX);
1,531✔
400
            new_engine->sm_list_base = (uint16_t)sm_list;
1,531✔
401
            new_engine->v1 = t->v1;
1,531✔
402
            new_engine->v1.transforms = transforms; /* assign transforms */
1,531✔
403

404
            if (de_ctx->pkt_inspect_engines == NULL) {
1,531✔
405
                de_ctx->pkt_inspect_engines = new_engine;
×
406
            } else {
1,531✔
407
                DetectEnginePktInspectionEngine *list = de_ctx->pkt_inspect_engines;
1,531✔
408
                while (list->next != NULL) {
84,380✔
409
                    list = list->next;
82,849✔
410
                }
82,849✔
411

412
                list->next = new_engine;
1,531✔
413
            }
1,531✔
414
        }
1,531✔
415
        t = t->next;
13,779✔
416
    }
13,779✔
417
}
1,531✔
418

419
/* copy inspect engines from global registrations to de_ctx list */
420
static void DetectPktInspectEngineCopyListToDetectCtx(DetectEngineCtx *de_ctx)
421
{
35,469✔
422
    const DetectEnginePktInspectionEngine *t = g_pkt_inspect_engines;
35,469✔
423
    while (t) {
354,690✔
424
        SCLogDebug("engine %p", t);
319,221✔
425
        DetectEnginePktInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEnginePktInspectionEngine));
319,221✔
426
        if (unlikely(new_engine == NULL)) {
319,221✔
427
            exit(EXIT_FAILURE);
×
428
        }
×
429
        new_engine->sm_list = t->sm_list;
319,221✔
430
        new_engine->sm_list_base = t->sm_list;
319,221✔
431
        new_engine->v1 = t->v1;
319,221✔
432

433
        if (de_ctx->pkt_inspect_engines == NULL) {
319,221✔
434
            de_ctx->pkt_inspect_engines = new_engine;
35,469✔
435
        } else {
283,752✔
436
            DetectEnginePktInspectionEngine *list = de_ctx->pkt_inspect_engines;
283,752✔
437
            while (list->next != NULL) {
1,276,884✔
438
                list = list->next;
993,132✔
439
            }
993,132✔
440

441
            list->next = new_engine;
283,752✔
442
        }
283,752✔
443

444
        t = t->next;
319,221✔
445
    }
319,221✔
446
}
35,469✔
447

448
/** \brief register inspect engine at start up time
449
 *
450
 *  \note errors are fatal */
451
void DetectEngineFrameInspectEngineRegister(DetectEngineCtx *de_ctx, const char *name, int dir,
452
        InspectionBufferFrameInspectFunc Callback, AppProto alproto, uint8_t type)
453
{
6,970✔
454
    const int sm_list = DetectEngineBufferTypeRegister(de_ctx, name);
6,970✔
455
    if (sm_list < 0) {
6,970✔
456
        FatalError("failed to register inspect engine %s", name);
×
457
    }
×
458

459
    if ((sm_list < DETECT_SM_LIST_MATCH) || (sm_list >= SHRT_MAX) || (Callback == NULL)) {
6,970✔
460
        SCLogError("Invalid arguments");
×
461
        BUG_ON(1);
×
462
    }
×
463

464
    uint8_t direction;
6,970✔
465
    if (dir == SIG_FLAG_TOSERVER) {
6,970✔
466
        direction = 0;
3,485✔
467
    } else {
3,485✔
468
        direction = 1;
3,485✔
469
    }
3,485✔
470

471
    DetectEngineFrameInspectionEngine *new_engine = SCCalloc(1, sizeof(*new_engine));
6,970✔
472
    if (unlikely(new_engine == NULL)) {
6,970✔
473
        FatalError("failed to register inspect engine %s: %s", name, strerror(errno));
×
474
    }
×
475
    new_engine->sm_list = (uint16_t)sm_list;
6,970✔
476
    new_engine->sm_list_base = (uint16_t)sm_list;
6,970✔
477
    new_engine->dir = direction;
6,970✔
478
    new_engine->v1.Callback = Callback;
6,970✔
479
    new_engine->alproto = alproto;
6,970✔
480
    new_engine->type = type;
6,970✔
481

482
    if (de_ctx->frame_inspect_engines == NULL) {
6,970✔
483
        de_ctx->frame_inspect_engines = new_engine;
1,717✔
484
    } else {
5,253✔
485
        DetectEngineFrameInspectionEngine *list = de_ctx->frame_inspect_engines;
5,253✔
486
        while (list->next != NULL) {
16,261✔
487
            list = list->next;
11,008✔
488
        }
11,008✔
489

490
        list->next = new_engine;
5,253✔
491
    }
5,253✔
492
}
6,970✔
493

494
/* copy an inspect engine with transforms to a new list id. */
495
static void DetectFrameInspectEngineCopy(DetectEngineCtx *de_ctx, int sm_list, int new_list,
496
        const DetectEngineTransforms *transforms)
497
{
375✔
498
    /* take the list from the detect engine as the buffers can be registered
499
     * dynamically. */
500
    DetectEngineFrameInspectionEngine *t = de_ctx->frame_inspect_engines;
375✔
501
    while (t) {
6,991✔
502
        if (t->sm_list == sm_list) {
6,616✔
503
            DetectEngineFrameInspectionEngine *new_engine =
750✔
504
                    SCCalloc(1, sizeof(DetectEngineFrameInspectionEngine));
750✔
505
            if (unlikely(new_engine == NULL)) {
750✔
506
                exit(EXIT_FAILURE);
×
507
            }
×
508
            DEBUG_VALIDATE_BUG_ON(new_list < 0 || new_list > UINT16_MAX);
750✔
509
            new_engine->sm_list = (uint16_t)new_list; /* use new list id */
750✔
510
            DEBUG_VALIDATE_BUG_ON(sm_list < 0 || sm_list > UINT16_MAX);
750✔
511
            new_engine->sm_list_base = (uint16_t)sm_list;
750✔
512
            new_engine->dir = t->dir;
750✔
513
            new_engine->alproto = t->alproto;
750✔
514
            new_engine->type = t->type;
750✔
515
            new_engine->v1 = t->v1;
750✔
516
            new_engine->v1.transforms = transforms; /* assign transforms */
750✔
517

518
            /* append to the list */
519
            DetectEngineFrameInspectionEngine *list = t;
750✔
520
            while (list->next != NULL) {
9,500✔
521
                list = list->next;
8,750✔
522
            }
8,750✔
523

524
            list->next = new_engine;
750✔
525
        }
750✔
526
        t = t->next;
6,616✔
527
    }
6,616✔
528
}
375✔
529

530
/* copy inspect engines from global registrations to de_ctx list */
531
static void DetectFrameInspectEngineCopyListToDetectCtx(DetectEngineCtx *de_ctx)
532
{
35,469✔
533
    const DetectEngineFrameInspectionEngine *t = g_frame_inspect_engines;
35,469✔
534
    while (t) {
35,469✔
535
        SCLogDebug("engine %p", t);
×
536
        DetectEngineFrameInspectionEngine *new_engine =
×
537
                SCCalloc(1, sizeof(DetectEngineFrameInspectionEngine));
×
538
        if (unlikely(new_engine == NULL)) {
×
539
            exit(EXIT_FAILURE);
×
540
        }
×
541
        new_engine->sm_list = t->sm_list;
×
542
        new_engine->sm_list_base = t->sm_list;
×
543
        new_engine->dir = t->dir;
×
544
        new_engine->alproto = t->alproto;
×
545
        new_engine->type = t->type;
×
546
        new_engine->v1 = t->v1;
×
547

548
        if (de_ctx->frame_inspect_engines == NULL) {
×
549
            de_ctx->frame_inspect_engines = new_engine;
×
550
        } else {
×
551
            DetectEngineFrameInspectionEngine *list = de_ctx->frame_inspect_engines;
×
552
            while (list->next != NULL) {
×
553
                list = list->next;
×
554
            }
×
555

556
            list->next = new_engine;
×
557
        }
×
558

559
        t = t->next;
×
560
    }
×
561
}
35,469✔
562

563
/** \internal
564
 *  \brief append the stream inspection
565
 *
566
 *  If stream inspection is MPM, then prepend it.
567
 */
568
static void AppendStreamInspectEngine(
569
        Signature *s, SigMatchData *stream, uint8_t direction, uint8_t id)
570
{
4,382✔
571
    bool prepend = false;
4,382✔
572

573
    DetectEngineAppInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEngineAppInspectionEngine));
4,382✔
574
    if (unlikely(new_engine == NULL)) {
4,382✔
575
        exit(EXIT_FAILURE);
×
576
    }
×
577
    if (s->init_data->mpm_sm_list == DETECT_SM_LIST_PMATCH) {
4,382✔
578
        SCLogDebug("stream is mpm");
1,094✔
579
        prepend = true;
1,094✔
580
        new_engine->mpm = true;
1,094✔
581
    }
1,094✔
582
    new_engine->alproto = ALPROTO_UNKNOWN; /* all */
4,382✔
583
    new_engine->dir = direction;
4,382✔
584
    new_engine->stream = true;
4,382✔
585
    new_engine->sm_list = DETECT_SM_LIST_PMATCH;
4,382✔
586
    new_engine->sm_list_base = DETECT_SM_LIST_PMATCH;
4,382✔
587
    new_engine->smd = stream;
4,382✔
588
    new_engine->v2.Callback = DetectEngineInspectStream;
4,382✔
589
    new_engine->progress = 0;
4,382✔
590

591
    /* append */
592
    if (s->app_inspect == NULL) {
4,382✔
593
        s->app_inspect = new_engine;
1✔
594
        new_engine->id = DE_STATE_FLAG_BASE; /* id is used as flag in stateful detect */
1✔
595
    } else if (prepend) {
4,381✔
596
        new_engine->next = s->app_inspect;
1,093✔
597
        s->app_inspect = new_engine;
1,093✔
598
        new_engine->id = id;
1,093✔
599

600
    } else {
3,288✔
601
        DetectEngineAppInspectionEngine *a = s->app_inspect;
3,288✔
602
        while (a->next != NULL) {
71,345✔
603
            a = a->next;
68,057✔
604
        }
68,057✔
605

606
        a->next = new_engine;
3,288✔
607
        new_engine->id = id;
3,288✔
608
    }
3,288✔
609
    SCLogDebug("sid %u: engine %p/%u added", s->id, new_engine, new_engine->id);
4,382✔
610
}
4,382✔
611

612
static void AppendFrameInspectEngine(DetectEngineCtx *de_ctx,
613
        const DetectEngineFrameInspectionEngine *u, Signature *s, SigMatchData *smd,
614
        const int mpm_list)
615
{
6,498✔
616
    bool prepend = false;
6,498✔
617

618
    if (u->alproto == ALPROTO_UNKNOWN) {
6,498✔
619
        /* special case, inspect engine applies to all protocols */
620
    } else if (s->alproto != ALPROTO_UNKNOWN && !AppProtoEquals(s->alproto, u->alproto))
6,498✔
621
        return;
×
622

623
    if (s->flags & SIG_FLAG_TOSERVER && !(s->flags & SIG_FLAG_TOCLIENT)) {
6,498✔
624
        if (u->dir == 1)
2,644✔
625
            return;
1,322✔
626
    } else if (s->flags & SIG_FLAG_TOCLIENT && !(s->flags & SIG_FLAG_TOSERVER)) {
3,854✔
627
        if (u->dir == 0)
1,516✔
628
            return;
758✔
629
    }
1,516✔
630

631
    DetectEngineFrameInspectionEngine *new_engine =
4,418✔
632
            SCCalloc(1, sizeof(DetectEngineFrameInspectionEngine));
4,418✔
633
    if (unlikely(new_engine == NULL)) {
4,418✔
634
        exit(EXIT_FAILURE);
×
635
    }
×
636
    if (mpm_list == u->sm_list) {
4,418✔
637
        SCLogDebug("%s is mpm", DetectEngineBufferTypeGetNameById(de_ctx, u->sm_list));
4,303✔
638
        prepend = true;
4,303✔
639
        new_engine->mpm = true;
4,303✔
640
    }
4,303✔
641

642
    new_engine->type = u->type;
4,418✔
643
    new_engine->sm_list = u->sm_list;
4,418✔
644
    new_engine->sm_list_base = u->sm_list_base;
4,418✔
645
    new_engine->smd = smd;
4,418✔
646
    new_engine->v1 = u->v1;
4,418✔
647
    SCLogDebug("sm_list %d new_engine->v1 %p/%p", new_engine->sm_list, new_engine->v1.Callback,
4,418✔
648
            new_engine->v1.transforms);
4,418✔
649

650
    if (s->frame_inspect == NULL) {
4,418✔
651
        s->frame_inspect = new_engine;
3,249✔
652
    } else if (prepend) {
3,249✔
653
        new_engine->next = s->frame_inspect;
1,113✔
654
        s->frame_inspect = new_engine;
1,113✔
655
    } else {
1,113✔
656
        DetectEngineFrameInspectionEngine *a = s->frame_inspect;
56✔
657
        while (a->next != NULL) {
56✔
658
            a = a->next;
×
659
        }
×
660
        new_engine->next = a->next;
56✔
661
        a->next = new_engine;
56✔
662
    }
56✔
663
}
4,418✔
664

665
static void AppendPacketInspectEngine(DetectEngineCtx *de_ctx,
666
        const DetectEnginePktInspectionEngine *e, Signature *s, SigMatchData *smd,
667
        const int mpm_list)
668
{
882✔
669
    bool prepend = false;
882✔
670

671
    DetectEnginePktInspectionEngine *new_engine =
882✔
672
            SCCalloc(1, sizeof(DetectEnginePktInspectionEngine));
882✔
673
    if (unlikely(new_engine == NULL)) {
882✔
674
        exit(EXIT_FAILURE);
×
675
    }
×
676
    if (mpm_list == e->sm_list) {
882✔
677
        SCLogDebug("%s is mpm", DetectEngineBufferTypeGetNameById(de_ctx, e->sm_list));
372✔
678
        prepend = true;
372✔
679
        new_engine->mpm = true;
372✔
680
    }
372✔
681

682
    new_engine->sm_list = e->sm_list;
882✔
683
    new_engine->sm_list_base = e->sm_list_base;
882✔
684
    new_engine->smd = smd;
882✔
685
    new_engine->v1 = e->v1;
882✔
686
    SCLogDebug("sm_list %d new_engine->v1 %p/%p/%p", new_engine->sm_list, new_engine->v1.Callback,
882✔
687
            new_engine->v1.GetData, new_engine->v1.transforms);
882✔
688

689
    if (s->pkt_inspect == NULL) {
882✔
690
        s->pkt_inspect = new_engine;
802✔
691
    } else if (prepend) {
802✔
692
        new_engine->next = s->pkt_inspect;
13✔
693
        s->pkt_inspect = new_engine;
13✔
694
    } else {
67✔
695
        DetectEnginePktInspectionEngine *a = s->pkt_inspect;
67✔
696
        while (a->next != NULL) {
331✔
697
            a = a->next;
264✔
698
        }
264✔
699
        new_engine->next = a->next;
67✔
700
        a->next = new_engine;
67✔
701
    }
67✔
702
}
882✔
703

704
static void AppendAppInspectEngine(DetectEngineCtx *de_ctx,
705
        const DetectEngineAppInspectionEngine *t, Signature *s, SigMatchData *smd,
706
        const int mpm_list, const int files_id, uint8_t *last_id, bool *head_is_mpm)
707
{
256,787✔
708
    if (t->alproto == ALPROTO_UNKNOWN) {
256,787✔
709
        /* special case, inspect engine applies to all protocols */
710
    } else if (s->alproto != ALPROTO_UNKNOWN && !AppProtoEquals(s->alproto, t->alproto))
221,633✔
711
        return;
75,761✔
712

713
    if (s->flags & SIG_FLAG_TOSERVER && !(s->flags & SIG_FLAG_TOCLIENT)) {
181,026✔
714
        if (t->dir == 1)
51,425✔
715
            return;
10,486✔
716
    } else if (s->flags & SIG_FLAG_TOCLIENT && !(s->flags & SIG_FLAG_TOSERVER)) {
129,601✔
717
        if (t->dir == 0)
16,136✔
718
            return;
5,205✔
719
    }
16,136✔
720
    SCLogDebug("app engine: t %p t->id %u => alproto:%s files:%s", t, t->id,
165,335✔
721
            AppProtoToString(t->alproto), BOOL2STR(t->sm_list == files_id));
165,335✔
722

723
    DetectEngineAppInspectionEngine *new_engine =
165,335✔
724
            SCCalloc(1, sizeof(DetectEngineAppInspectionEngine));
165,335✔
725
    if (unlikely(new_engine == NULL)) {
165,335✔
726
        exit(EXIT_FAILURE);
×
727
    }
×
728
    bool prepend = false;
165,335✔
729
    if (mpm_list == t->sm_list) {
165,335✔
730
        SCLogDebug("%s is mpm", DetectEngineBufferTypeGetNameById(de_ctx, t->sm_list));
79,767✔
731
        prepend = true;
79,767✔
732
        *head_is_mpm = true;
79,767✔
733
        new_engine->mpm = true;
79,767✔
734
    }
79,767✔
735

736
    new_engine->alproto = t->alproto;
165,335✔
737
    new_engine->dir = t->dir;
165,335✔
738
    new_engine->sm_list = t->sm_list;
165,335✔
739
    new_engine->sm_list_base = t->sm_list_base;
165,335✔
740
    new_engine->smd = smd;
165,335✔
741
    new_engine->match_on_null = smd ? DetectContentInspectionMatchOnAbsentBuffer(smd) : false;
165,335✔
742
    new_engine->progress = t->progress;
165,335✔
743
    new_engine->v2 = t->v2;
165,335✔
744
    SCLogDebug("sm_list %d new_engine->v2 %p/%p/%p", new_engine->sm_list, new_engine->v2.Callback,
165,335✔
745
            new_engine->v2.GetData, new_engine->v2.transforms);
165,335✔
746

747
    if (s->app_inspect == NULL) {
165,335✔
748
        s->app_inspect = new_engine;
59,953✔
749
        if (new_engine->sm_list == files_id) {
59,953✔
750
            new_engine->id = DE_STATE_ID_FILE_INSPECT;
640✔
751
            SCLogDebug("sid %u: engine %p/%u is FILE ENGINE", s->id, new_engine, new_engine->id);
640✔
752
        } else {
59,313✔
753
            new_engine->id = DE_STATE_FLAG_BASE; /* id is used as flag in stateful detect */
59,313✔
754
            SCLogDebug("sid %u: engine %p/%u %s", s->id, new_engine, new_engine->id,
59,313✔
755
                    DetectEngineBufferTypeGetNameById(de_ctx, new_engine->sm_list));
59,313✔
756
        }
59,313✔
757

758
        /* prepend engine if forced or if our engine has a lower progress. */
759
    } else if (prepend || (!(*head_is_mpm) && s->app_inspect->progress > new_engine->progress)) {
105,382✔
760
        new_engine->next = s->app_inspect;
60,370✔
761
        s->app_inspect = new_engine;
60,370✔
762
        if (new_engine->sm_list == files_id) {
60,370✔
763
            new_engine->id = DE_STATE_ID_FILE_INSPECT;
2✔
764
            SCLogDebug("sid %u: engine %p/%u is FILE ENGINE", s->id, new_engine, new_engine->id);
2✔
765
        } else {
60,368✔
766
            new_engine->id = ++(*last_id);
60,368✔
767
            SCLogDebug("sid %u: engine %p/%u %s", s->id, new_engine, new_engine->id,
60,368✔
768
                    DetectEngineBufferTypeGetNameById(de_ctx, new_engine->sm_list));
60,368✔
769
        }
60,368✔
770

771
    } else {
60,370✔
772
        DetectEngineAppInspectionEngine *a = s->app_inspect;
45,012✔
773
        while (a->next != NULL) {
237,277✔
774
            if (a->next && a->next->progress > new_engine->progress) {
209,081✔
775
                break;
16,816✔
776
            }
16,816✔
777
            a = a->next;
192,265✔
778
        }
192,265✔
779

780
        new_engine->next = a->next;
45,012✔
781
        a->next = new_engine;
45,012✔
782
        if (new_engine->sm_list == files_id) {
45,012✔
783
            new_engine->id = DE_STATE_ID_FILE_INSPECT;
24,264✔
784
            SCLogDebug("sid %u: engine %p/%u is FILE ENGINE", s->id, new_engine, new_engine->id);
24,264✔
785
        } else {
24,264✔
786
            new_engine->id = ++(*last_id);
20,748✔
787
            SCLogDebug("sid %u: engine %p/%u %s", s->id, new_engine, new_engine->id,
20,748✔
788
                    DetectEngineBufferTypeGetNameById(de_ctx, new_engine->sm_list));
20,748✔
789
        }
20,748✔
790
    }
45,012✔
791

792
    SCLogDebug("sid %u: engine %p/%u added", s->id, new_engine, new_engine->id);
165,335✔
793

794
    s->init_data->init_flags |= SIG_FLAG_INIT_STATE_MATCH;
165,335✔
795
}
165,335✔
796

797
/**
798
 *  \note for the file inspect engine, the id DE_STATE_ID_FILE_INSPECT
799
 *        is assigned.
800
 */
801
int DetectEngineAppInspectionEngine2Signature(DetectEngineCtx *de_ctx, Signature *s)
802
{
119,313✔
803
    const int mpm_list = s->init_data->mpm_sm ? s->init_data->mpm_sm_list : -1;
119,313✔
804
    const int files_id = DetectBufferTypeGetByName("files");
119,313✔
805
    bool head_is_mpm = false;
119,313✔
806
    uint8_t last_id = DE_STATE_FLAG_BASE;
119,313✔
807
    SCLogDebug("%u: setup app inspect engines. %u buffers", s->id, s->init_data->buffer_index);
119,313✔
808

809
    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
188,582✔
810
        SigMatchData *smd = SigMatchList2DataArray(s->init_data->buffers[x].head);
69,269✔
811
        SCLogDebug("smd %p, id %u", smd, s->init_data->buffers[x].id);
69,269✔
812

813
        const DetectBufferType *b =
69,269✔
814
                DetectEngineBufferTypeGetById(de_ctx, s->init_data->buffers[x].id);
69,269✔
815
        if (b == NULL)
69,269✔
816
            FatalError("unknown buffer");
×
817

818
        if (b->frame) {
69,269✔
819
            for (const DetectEngineFrameInspectionEngine *u = de_ctx->frame_inspect_engines;
3,249✔
820
                    u != NULL; u = u->next) {
24,953✔
821
                if (u->sm_list == s->init_data->buffers[x].id) {
21,704✔
822
                    AppendFrameInspectEngine(de_ctx, u, s, smd, mpm_list);
6,498✔
823
                }
6,498✔
824
            }
21,704✔
825
        } else if (b->packet) {
66,020✔
826
            /* set up pkt inspect engines */
827
            for (const DetectEnginePktInspectionEngine *e = de_ctx->pkt_inspect_engines; e != NULL;
9,121✔
828
                    e = e->next) {
8,239✔
829
                SCLogDebug("e %p sm_list %u", e, e->sm_list);
8,239✔
830
                if (e->sm_list == s->init_data->buffers[x].id) {
8,239✔
831
                    AppendPacketInspectEngine(de_ctx, e, s, smd, mpm_list);
882✔
832
                }
882✔
833
            }
8,239✔
834
        } else {
65,138✔
835
            SCLogDebug("app %s id %u parent %u rule %u xforms %u", b->name, b->id, b->parent_id,
65,138✔
836
                    s->init_data->buffers[x].id, b->transforms.cnt);
65,138✔
837
            for (const DetectEngineAppInspectionEngine *t = de_ctx->app_inspect_engines; t != NULL;
48,882,881✔
838
                    t = t->next) {
48,817,743✔
839
                if (t->sm_list == s->init_data->buffers[x].id) {
48,817,743✔
840
                    if (s->flags & SIG_FLAG_TXBOTHDIR) {
257,233✔
841
                        // ambiguous keywords have app engines in both directions
842
                        // so we skip the wrong direction for this buffer
843
                        if (s->init_data->buffers[x].only_tc && t->dir == 0) {
1,729✔
844
                            continue;
355✔
845
                        } else if (s->init_data->buffers[x].only_ts && t->dir == 1) {
1,374✔
846
                            continue;
96✔
847
                        }
96✔
848
                    }
1,729✔
849
                    AppendAppInspectEngine(
256,782✔
850
                            de_ctx, t, s, smd, mpm_list, files_id, &last_id, &head_is_mpm);
256,782✔
851
                }
256,782✔
852
            }
48,817,743✔
853
        }
65,138✔
854
    }
69,269✔
855

856
    /* handle rules that have an app-layer hook w/o bringing their own app inspect engine,
857
     * e.g. `alert dns:request_complete ... (sid:1;)`
858
     *
859
     * Here we use a minimal stub inspect engine in which we set:
860
     * - alproto
861
     * - progress
862
     * - sm_list/sm_list_base to get the mapping to the hook name
863
     * - dir based on sig direction
864
     *
865
     * The inspect engine has no callback and is thus considered a straight match.
866
     */
867
    if (s->init_data->buffer_index == 0 && s->init_data->hook.type == SIGNATURE_HOOK_TYPE_APP) {
119,313✔
868
        uint8_t dir = 0;
5✔
869
        BUG_ON((s->flags & (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT)) ==
5✔
870
                (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT));
5✔
871
        BUG_ON((s->flags & (SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT)) == 0);
5✔
872
        if (s->flags & SIG_FLAG_TOSERVER)
5✔
873
            dir = 0;
4✔
874
        else if (s->flags & SIG_FLAG_TOCLIENT)
1✔
875
            dir = 1;
1✔
876

877
        DetectEngineAppInspectionEngine t = {
5✔
878
            .alproto = s->init_data->hook.t.app.alproto,
5✔
879
            .progress = (uint16_t)s->init_data->hook.t.app.app_progress,
5✔
880
            .sm_list = (uint16_t)s->init_data->hook.sm_list,
5✔
881
            .sm_list_base = (uint16_t)s->init_data->hook.sm_list,
5✔
882
            .dir = dir,
5✔
883
        };
5✔
884
        AppendAppInspectEngine(de_ctx, &t, s, NULL, mpm_list, files_id, &last_id, &head_is_mpm);
5✔
885
    }
5✔
886

887
    if ((s->init_data->init_flags & SIG_FLAG_INIT_STATE_MATCH) &&
119,313✔
888
            s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL)
119,313✔
889
    {
2,626✔
890
        /* if engine is added multiple times, we pass it the same list */
891
        SigMatchData *stream = SigMatchList2DataArray(s->init_data->smlists[DETECT_SM_LIST_PMATCH]);
2,626✔
892
        BUG_ON(stream == NULL);
2,626✔
893
        if (s->flags & SIG_FLAG_TOSERVER && !(s->flags & SIG_FLAG_TOCLIENT)) {
2,626✔
894
            AppendStreamInspectEngine(s, stream, 0, last_id + 1);
844✔
895
        } else if (s->flags & SIG_FLAG_TOCLIENT && !(s->flags & SIG_FLAG_TOSERVER)) {
1,782✔
896
            AppendStreamInspectEngine(s, stream, 1, last_id + 1);
26✔
897
        } else {
1,756✔
898
            AppendStreamInspectEngine(s, stream, 0, last_id + 1);
1,756✔
899
            AppendStreamInspectEngine(s, stream, 1, last_id + 1);
1,756✔
900
        }
1,756✔
901

902
        if (s->init_data->init_flags & SIG_FLAG_INIT_NEED_FLUSH) {
2,626✔
903
            SCLogDebug("set SIG_FLAG_FLUSH on %u", s->id);
344✔
904
            s->flags |= SIG_FLAG_FLUSH;
344✔
905
        }
344✔
906
    }
2,626✔
907

908
#ifdef DEBUG
909
    const DetectEngineAppInspectionEngine *iter = s->app_inspect;
910
    while (iter) {
911
        SCLogDebug("%u: engine %s id %u progress %d %s", s->id,
912
                DetectEngineBufferTypeGetNameById(de_ctx, iter->sm_list), iter->id, iter->progress,
913
                iter->sm_list == mpm_list ? "MPM" : "");
914
        iter = iter->next;
915
    }
916
#endif
917
    return 0;
119,313✔
918
}
119,313✔
919

920
/** \brief free app inspect engines for a signature
921
 *
922
 *  For lists that are registered multiple times, like http_header and
923
 *  http_cookie, making the engines owner of the lists is complicated.
924
 *  Multiple engines in a sig may be pointing to the same list. To
925
 *  address this the 'free' code needs to be extra careful about not
926
 *  double freeing, so it takes an approach to first fill an array
927
 *  of the to-free pointers before freeing them.
928
 */
929
void DetectEngineAppInspectionEngineSignatureFree(DetectEngineCtx *de_ctx, Signature *s)
930
{
5,155,026✔
931
    int engines = 0;
5,155,026✔
932

933
    DetectEngineAppInspectionEngine *ie = s->app_inspect;
5,155,026✔
934
    while (ie) {
5,324,739✔
935
        ie = ie->next;
169,713✔
936
        engines++;
169,713✔
937
    }
169,713✔
938
    DetectEnginePktInspectionEngine *e = s->pkt_inspect;
5,155,026✔
939
    while (e) {
5,216,433✔
940
        e = e->next;
61,407✔
941
        engines++;
61,407✔
942
    }
61,407✔
943
    DetectEngineFrameInspectionEngine *u = s->frame_inspect;
5,155,026✔
944
    while (u) {
5,159,444✔
945
        u = u->next;
4,418✔
946
        engines++;
4,418✔
947
    }
4,418✔
948
    if (engines == 0) {
5,155,026✔
949
        BUG_ON(s->pkt_inspect);
5,047,417✔
950
        BUG_ON(s->frame_inspect);
5,047,417✔
951
        return;
5,047,417✔
952
    }
5,047,417✔
953

954
    SigMatchData *bufs[engines];
107,609✔
955
    memset(&bufs, 0, (engines * sizeof(SigMatchData *)));
107,609✔
956
    int arrays = 0;
107,609✔
957

958
    /* free engines and put smd in the array */
959
    ie = s->app_inspect;
107,609✔
960
    while (ie) {
277,322✔
961
        DetectEngineAppInspectionEngine *next = ie->next;
169,713✔
962

963
        bool skip = false;
169,713✔
964
        for (int i = 0; i < arrays; i++) {
212,948✔
965
            if (bufs[i] == ie->smd) {
145,181✔
966
                skip = true;
101,946✔
967
                break;
101,946✔
968
            }
101,946✔
969
        }
145,181✔
970
        if (!skip) {
169,713✔
971
            bufs[arrays++] = ie->smd;
67,767✔
972
        }
67,767✔
973
        SCFree(ie);
169,713✔
974
        ie = next;
169,713✔
975
    }
169,713✔
976
    e = s->pkt_inspect;
107,609✔
977
    while (e) {
169,016✔
978
        DetectEnginePktInspectionEngine *next = e->next;
61,407✔
979

980
        bool skip = false;
61,407✔
981
        for (int i = 0; i < arrays; i++) {
76,074✔
982
            if (bufs[i] == e->smd) {
18,202✔
983
                skip = true;
3,535✔
984
                break;
3,535✔
985
            }
3,535✔
986
        }
18,202✔
987
        if (!skip) {
61,407✔
988
            bufs[arrays++] = e->smd;
57,872✔
989
        }
57,872✔
990
        SCFree(e);
61,407✔
991
        e = next;
61,407✔
992
    }
61,407✔
993
    u = s->frame_inspect;
107,609✔
994
    while (u) {
112,027✔
995
        DetectEngineFrameInspectionEngine *next = u->next;
4,418✔
996

997
        bool skip = false;
4,418✔
998
        for (int i = 0; i < arrays; i++) {
4,565✔
999
            if (bufs[i] == u->smd) {
1,316✔
1000
                skip = true;
1,169✔
1001
                break;
1,169✔
1002
            }
1,169✔
1003
        }
1,316✔
1004
        if (!skip) {
4,418✔
1005
            bufs[arrays++] = u->smd;
3,249✔
1006
        }
3,249✔
1007
        SCFree(u);
4,418✔
1008
        u = next;
4,418✔
1009
    }
4,418✔
1010

1011
    for (int i = 0; i < engines; i++) {
343,147✔
1012
        if (bufs[i] == NULL)
235,538✔
1013
            continue;
163,645✔
1014
        SigMatchData *smd = bufs[i];
71,893✔
1015
        while (1) {
80,823✔
1016
            if (sigmatch_table[smd->type].Free != NULL) {
80,823✔
1017
                sigmatch_table[smd->type].Free(de_ctx, smd->ctx);
80,821✔
1018
            }
80,821✔
1019
            if (smd->is_last)
80,823✔
1020
                break;
71,893✔
1021
            smd++;
8,930✔
1022
        }
8,930✔
1023
        SCFree(bufs[i]);
71,893✔
1024
    }
71,893✔
1025
}
107,609✔
1026

1027
/* code for registering buffers */
1028

1029
#include "util-hash-lookup3.h"
1030

1031
static HashListTable *g_buffer_type_hash = NULL;
1032
static int g_buffer_type_id = DETECT_SM_LIST_DYNAMIC_START;
1033
static int g_buffer_type_reg_closed = 0;
1034

1035
int DetectBufferTypeMaxId(void)
1036
{
×
1037
    return g_buffer_type_id;
×
1038
}
×
1039

1040
static void DetectBufferAddTransformData(DetectBufferType *map)
1041
{
99,028✔
1042
    for (int i = 0; i < map->transforms.cnt; i++) {
229,108✔
1043
        const TransformData *t = &map->transforms.transforms[i];
130,080✔
1044
        if (sigmatch_table[t->transform].TransformId) {
130,080✔
1045
            sigmatch_table[t->transform].TransformId(
63,137✔
1046
                    &map->xform_id[i].id_data, &map->xform_id[i].id_data_len, t->options);
63,137✔
1047
            SCLogDebug("transform identity data: [%p] \"%s\" [%d]", map->xform_id[i].id_data,
63,137✔
1048
                    (char *)map->xform_id[i].id_data, map->xform_id[i].id_data_len);
63,137✔
1049
        }
63,137✔
1050
    }
130,080✔
1051
}
99,028✔
1052

1053
static uint32_t DetectBufferTypeHashNameFunc(HashListTable *ht, void *data, uint16_t datalen)
1054
{
15,966,596✔
1055
    const DetectBufferType *map = (DetectBufferType *)data;
15,966,596✔
1056
    uint32_t hash = hashlittle_safe(map->name, strlen(map->name), 0);
15,966,596✔
1057

1058
    // Add the transform data
1059
    // - Collect transform id and position
1060
    // - Collect identity data, if any
1061
    hash += hashlittle_safe((uint8_t *)&map->transforms.cnt, sizeof(map->transforms.cnt), 0);
15,966,596✔
1062
    for (int i = 0; i < map->transforms.cnt; i++) {
16,192,174✔
1063
        const TransformData *t = &map->transforms.transforms[i];
225,578✔
1064
        int tval = t->transform;
225,578✔
1065
        hash += hashlittle_safe((uint8_t *)&tval, sizeof(tval), 0);
225,578✔
1066
        if (map->xform_id[i].id_data) {
225,578✔
1067
            hash += hashlittle_safe(
63,137✔
1068
                    &map->xform_id[i].id_data_len, sizeof(map->xform_id[i].id_data_len), 0);
63,137✔
1069
            hash += hashlittle_safe(map->xform_id[i].id_data, map->xform_id[i].id_data_len, 0);
63,137✔
1070
        }
63,137✔
1071
    }
225,578✔
1072
    hash %= ht->array_size;
15,966,596✔
1073
    SCLogDebug("map->name %s, hash %d", map->name, hash);
15,966,596✔
1074
    return hash;
15,966,596✔
1075
}
15,966,596✔
1076

1077
static uint32_t DetectBufferTypeHashIdFunc(HashListTable *ht, void *data, uint16_t datalen)
1078
{
20,206,474✔
1079
    const DetectBufferType *map = (DetectBufferType *)data;
20,206,474✔
1080
    uint32_t hash = map->id;
20,206,474✔
1081
    hash %= ht->array_size;
20,206,474✔
1082
    return hash;
20,206,474✔
1083
}
20,206,474✔
1084

1085
static char DetectBufferTypeCompareNameFunc(void *data1, uint16_t len1, void *data2, uint16_t len2)
1086
{
2,104,850✔
1087
    DetectBufferType *map1 = (DetectBufferType *)data1;
2,104,850✔
1088
    DetectBufferType *map2 = (DetectBufferType *)data2;
2,104,850✔
1089

1090
    char r = (strcmp(map1->name, map2->name) == 0);
2,104,850✔
1091

1092
    // Compare the transforms
1093
    // the transform supports identity, that data will also be added.
1094
    r &= map1->transforms.cnt == map2->transforms.cnt;
2,104,850✔
1095
    if (r && map1->transforms.cnt) {
2,104,850✔
1096
        for (int i = 0; i < map1->transforms.cnt; i++) {
68,208✔
1097
            if (map1->transforms.transforms[i].transform !=
34,720✔
1098
                    map2->transforms.transforms[i].transform) {
34,720✔
1099
                r = 0;
×
1100
                break;
×
1101
            }
×
1102

1103
            SCLogDebug("%s: transform ids match; checking specialized data", map1->name);
34,720✔
1104
            // Checks
1105
            // - Both NULL: --> ok, continue
1106
            // - One NULL: --> no match, break?
1107
            // - identity data lengths match: --> ok, continue
1108
            // - identity data matches: ok
1109

1110
            // Stop if only one is NULL
1111
            if ((map1->xform_id[i].id_data == NULL) ^ (map2->xform_id[i].id_data == NULL)) {
34,720✔
1112
                SCLogDebug("identity data: only one is null");
116✔
1113
                r = 0;
116✔
1114
                break;
116✔
1115
            } else if (map1->xform_id[i].id_data == NULL) { /* continue when both are null */
34,604✔
1116
                SCLogDebug("identity data: both null");
34,604✔
1117
                r = 1;
34,604✔
1118
                continue;
34,604✔
1119
            } else if (map1->xform_id[i].id_data_len != map2->xform_id[i].id_data_len) {
34,604✔
1120
                // Stop when id data lengths aren't equal
1121
                SCLogDebug("id data: unequal lengths");
×
1122
                r = 0;
×
1123
                break;
×
1124
            }
×
1125

1126
            // stop if the identity data is different
1127
            r &= memcmp(map1->xform_id[i].id_data, map2->xform_id[i].id_data,
×
1128
                         map1->xform_id[i].id_data_len) == 0;
×
1129
            if (r == 0)
×
1130
                break;
×
1131
            SCLogDebug("identity data: data matches");
×
1132
        }
×
1133
    }
33,604✔
1134
    return r;
2,104,850✔
1135
}
2,104,850✔
1136

1137
static char DetectBufferTypeCompareIdFunc(void *data1, uint16_t len1, void *data2, uint16_t len2)
1138
{
5,958,563✔
1139
    DetectBufferType *map1 = (DetectBufferType *)data1;
5,958,563✔
1140
    DetectBufferType *map2 = (DetectBufferType *)data2;
5,958,563✔
1141
    return map1->id == map2->id;
5,958,563✔
1142
}
5,958,563✔
1143

1144
static void DetectBufferTypeFreeFunc(void *data)
1145
{
14,645,496✔
1146
    DetectBufferType *map = (DetectBufferType *)data;
14,645,496✔
1147

1148
    if (map == NULL) {
14,645,496✔
1149
        return;
×
1150
    }
×
1151

1152
    /* Release transformation option memory, if any */
1153
    for (int i = 0; i < map->transforms.cnt; i++) {
14,740,957✔
1154
        if (map->transforms.transforms[i].options == NULL)
95,461✔
1155
            continue;
32,339✔
1156

1157
        if (sigmatch_table[map->transforms.transforms[i].transform].Free == NULL) {
63,122✔
1158
            SCLogError("%s allocates transform option memory but has no free routine",
×
1159
                    sigmatch_table[map->transforms.transforms[i].transform].name);
×
1160
            continue;
×
1161
        }
×
1162
        sigmatch_table[map->transforms.transforms[i].transform].Free(NULL, map->transforms.transforms[i].options);
63,122✔
1163
    }
63,122✔
1164

1165
    SCFree(map);
14,645,496✔
1166
}
14,645,496✔
1167

1168
static int DetectBufferTypeInit(void)
1169
{
3✔
1170
    BUG_ON(g_buffer_type_hash);
3✔
1171
    g_buffer_type_hash = HashListTableInit(256, DetectBufferTypeHashNameFunc,
3✔
1172
            DetectBufferTypeCompareNameFunc, DetectBufferTypeFreeFunc);
3✔
1173
    if (g_buffer_type_hash == NULL)
3✔
1174
        return -1;
×
1175

1176
    return 0;
3✔
1177
}
3✔
1178
#if 0
1179
static void DetectBufferTypeFree(void)
1180
{
1181
    if (g_buffer_type_hash == NULL)
1182
        return;
1183

1184
    HashListTableFree(g_buffer_type_hash);
1185
    g_buffer_type_hash = NULL;
1186
}
1187
#endif
1188
static int DetectBufferTypeAdd(const char *string)
1189
{
1,231✔
1190
    BUG_ON(string == NULL || strlen(string) >= 64);
1,231✔
1191

1192
    DetectBufferType *map = SCCalloc(1, sizeof(*map));
1,231✔
1193
    if (map == NULL)
1,231✔
1194
        return -1;
×
1195

1196
    strlcpy(map->name, string, sizeof(map->name));
1,231✔
1197
    map->id = g_buffer_type_id++;
1,231✔
1198

1199
    BUG_ON(HashListTableAdd(g_buffer_type_hash, (void *)map, 0) != 0);
1,231✔
1200
    SCLogDebug("buffer %s registered with id %d", map->name, map->id);
1,231✔
1201
    return map->id;
1,231✔
1202
}
1,231✔
1203

1204
static DetectBufferType *DetectBufferTypeLookupByName(const char *string)
1205
{
1,148,256✔
1206
    DetectBufferType map;
1,148,256✔
1207
    memset(&map, 0, sizeof(map));
1,148,256✔
1208
    strlcpy(map.name, string, sizeof(map.name));
1,148,256✔
1209

1210
    DetectBufferType *res = HashListTableLookup(g_buffer_type_hash, &map, 0);
1,148,256✔
1211
    return res;
1,148,256✔
1212
}
1,148,256✔
1213

1214
int DetectBufferTypeRegister(const char *name)
1215
{
4,884✔
1216
    BUG_ON(g_buffer_type_reg_closed);
4,884✔
1217
    if (g_buffer_type_hash == NULL)
4,884✔
1218
        DetectBufferTypeInit();
3✔
1219

1220
    DetectBufferType *exists = DetectBufferTypeLookupByName(name);
4,884✔
1221
    if (!exists) {
4,884✔
1222
        return DetectBufferTypeAdd(name);
1,231✔
1223
    } else {
3,653✔
1224
        return exists->id;
3,653✔
1225
    }
3,653✔
1226
}
4,884✔
1227

1228
void DetectBufferTypeSupportsMultiInstance(const char *name)
1229
{
150✔
1230
    BUG_ON(g_buffer_type_reg_closed);
150✔
1231
    DetectBufferTypeRegister(name);
150✔
1232
    DetectBufferType *exists = DetectBufferTypeLookupByName(name);
150✔
1233
    BUG_ON(!exists);
150✔
1234
    exists->multi_instance = true;
150✔
1235
    SCLogDebug("%p %s -- %d supports multi instance", exists, name, exists->id);
150✔
1236
}
150✔
1237

1238
void DetectBufferTypeSupportsFrames(const char *name)
1239
{
×
1240
    BUG_ON(g_buffer_type_reg_closed);
×
1241
    DetectBufferTypeRegister(name);
×
1242
    DetectBufferType *exists = DetectBufferTypeLookupByName(name);
×
1243
    BUG_ON(!exists);
×
1244
    exists->frame = true;
×
1245
    SCLogDebug("%p %s -- %d supports frame inspection", exists, name, exists->id);
×
1246
}
×
1247

1248
void DetectBufferTypeSupportsPacket(const char *name)
1249
{
27✔
1250
    BUG_ON(g_buffer_type_reg_closed);
27✔
1251
    DetectBufferTypeRegister(name);
27✔
1252
    DetectBufferType *exists = DetectBufferTypeLookupByName(name);
27✔
1253
    BUG_ON(!exists);
27✔
1254
    exists->packet = true;
27✔
1255
    SCLogDebug("%p %s -- %d supports packet inspection", exists, name, exists->id);
27✔
1256
}
27✔
1257

1258
void DetectBufferTypeSupportsMpm(const char *name)
1259
{
1,142✔
1260
    BUG_ON(g_buffer_type_reg_closed);
1,142✔
1261
    DetectBufferTypeRegister(name);
1,142✔
1262
    DetectBufferType *exists = DetectBufferTypeLookupByName(name);
1,142✔
1263
    BUG_ON(!exists);
1,142✔
1264
    exists->mpm = true;
1,142✔
1265
    SCLogDebug("%p %s -- %d supports mpm", exists, name, exists->id);
1,142✔
1266
}
1,142✔
1267

1268
void DetectBufferTypeSupportsTransformations(const char *name)
1269
{
1,142✔
1270
    BUG_ON(g_buffer_type_reg_closed);
1,142✔
1271
    DetectBufferTypeRegister(name);
1,142✔
1272
    DetectBufferType *exists = DetectBufferTypeLookupByName(name);
1,142✔
1273
    BUG_ON(!exists);
1,142✔
1274
    exists->supports_transforms = true;
1,142✔
1275
    SCLogDebug("%p %s -- %d supports transformations", exists, name, exists->id);
1,142✔
1276
}
1,142✔
1277

1278
int DetectBufferTypeGetByName(const char *name)
1279
{
1,140,358✔
1280
    DetectBufferType *exists = DetectBufferTypeLookupByName(name);
1,140,358✔
1281
    if (!exists) {
1,140,358✔
1282
        return -1;
276,328✔
1283
    }
276,328✔
1284
    return exists->id;
864,030✔
1285
}
1,140,358✔
1286

1287
static DetectBufferType *DetectEngineBufferTypeLookupByName(
1288
        const DetectEngineCtx *de_ctx, const char *string)
1289
{
71,328✔
1290
    DetectBufferType map;
71,328✔
1291
    memset(&map, 0, sizeof(map));
71,328✔
1292
    strlcpy(map.name, string, sizeof(map.name));
71,328✔
1293

1294
    DetectBufferType *res = HashListTableLookup(de_ctx->buffer_type_hash_name, &map, 0);
71,328✔
1295
    return res;
71,328✔
1296
}
71,328✔
1297

1298
const DetectBufferType *DetectEngineBufferTypeGetById(const DetectEngineCtx *de_ctx, const int id)
1299
{
5,559,721✔
1300
    DetectBufferType lookup;
5,559,721✔
1301
    memset(&lookup, 0, sizeof(lookup));
5,559,721✔
1302
    lookup.id = id;
5,559,721✔
1303
    const DetectBufferType *res =
5,559,721✔
1304
            HashListTableLookup(de_ctx->buffer_type_hash_id, (void *)&lookup, 0);
5,559,721✔
1305
    return res;
5,559,721✔
1306
}
5,559,721✔
1307

1308
const char *DetectEngineBufferTypeGetNameById(const DetectEngineCtx *de_ctx, const int id)
1309
{
30,303✔
1310
    const DetectBufferType *res = DetectEngineBufferTypeGetById(de_ctx, id);
30,303✔
1311
    return res ? res->name : NULL;
30,303✔
1312
}
30,303✔
1313

1314
static int DetectEngineBufferTypeAdd(DetectEngineCtx *de_ctx, const char *string)
1315
{
3,485✔
1316
    BUG_ON(string == NULL || strlen(string) >= 32);
3,485✔
1317

1318
    DetectBufferType *map = SCCalloc(1, sizeof(*map));
3,485✔
1319
    if (map == NULL)
3,485✔
1320
        return -1;
×
1321

1322
    strlcpy(map->name, string, sizeof(map->name));
3,485✔
1323
    map->id = de_ctx->buffer_type_id++;
3,485✔
1324

1325
    BUG_ON(HashListTableAdd(de_ctx->buffer_type_hash_name, (void *)map, 0) != 0);
3,485✔
1326
    BUG_ON(HashListTableAdd(de_ctx->buffer_type_hash_id, (void *)map, 0) != 0);
3,485✔
1327
    SCLogDebug("buffer %s registered with id %d", map->name, map->id);
3,485✔
1328
    return map->id;
3,485✔
1329
}
3,485✔
1330

1331
int DetectEngineBufferTypeRegisterWithFrameEngines(DetectEngineCtx *de_ctx, const char *name,
1332
        const int direction, const AppProto alproto, const uint8_t frame_type)
1333
{
36,478✔
1334
    DetectBufferType *exists = DetectEngineBufferTypeLookupByName(de_ctx, name);
36,478✔
1335
    if (exists) {
36,478✔
1336
        return exists->id;
32,993✔
1337
    }
32,993✔
1338

1339
    const int buffer_id = DetectEngineBufferTypeAdd(de_ctx, name);
3,485✔
1340
    if (buffer_id < 0) {
3,485✔
1341
        return -1;
×
1342
    }
×
1343

1344
    /* TODO hack we need the map to get the name. Should we return the map at reg? */
1345
    const DetectBufferType *map = DetectEngineBufferTypeGetById(de_ctx, buffer_id);
3,485✔
1346
    BUG_ON(!map);
3,485✔
1347

1348
    /* register MPM/inspect engines */
1349
    if (direction & SIG_FLAG_TOSERVER) {
3,485✔
1350
        DetectEngineFrameMpmRegister(de_ctx, map->name, SIG_FLAG_TOSERVER, 2,
3,485✔
1351
                PrefilterGenericMpmFrameRegister, alproto, frame_type);
3,485✔
1352
        DetectEngineFrameInspectEngineRegister(de_ctx, map->name, SIG_FLAG_TOSERVER,
3,485✔
1353
                DetectEngineInspectFrameBufferGeneric, alproto, frame_type);
3,485✔
1354
    }
3,485✔
1355
    if (direction & SIG_FLAG_TOCLIENT) {
3,485✔
1356
        DetectEngineFrameMpmRegister(de_ctx, map->name, SIG_FLAG_TOCLIENT, 2,
3,485✔
1357
                PrefilterGenericMpmFrameRegister, alproto, frame_type);
3,485✔
1358
        DetectEngineFrameInspectEngineRegister(de_ctx, map->name, SIG_FLAG_TOCLIENT,
3,485✔
1359
                DetectEngineInspectFrameBufferGeneric, alproto, frame_type);
3,485✔
1360
    }
3,485✔
1361

1362
    return buffer_id;
3,485✔
1363
}
3,485✔
1364

1365
int DetectEngineBufferTypeRegister(DetectEngineCtx *de_ctx, const char *name)
1366
{
13,940✔
1367
    DetectBufferType *exists = DetectEngineBufferTypeLookupByName(de_ctx, name);
13,940✔
1368
    if (!exists) {
13,940✔
1369
        return DetectEngineBufferTypeAdd(de_ctx, name);
×
1370
    } else {
13,940✔
1371
        return exists->id;
13,940✔
1372
    }
13,940✔
1373
}
13,940✔
1374

1375
void DetectBufferTypeSetDescriptionByName(const char *name, const char *desc)
1376
{
469✔
1377
    BUG_ON(desc == NULL || strlen(desc) >= 128);
469✔
1378

1379
    DetectBufferType *exists = DetectBufferTypeLookupByName(name);
469✔
1380
    if (!exists) {
469✔
1381
        return;
6✔
1382
    }
6✔
1383
    strlcpy(exists->description, desc, sizeof(exists->description));
463✔
1384
}
463✔
1385

1386
const char *DetectEngineBufferTypeGetDescriptionById(const DetectEngineCtx *de_ctx, const int id)
1387
{
797✔
1388
    const DetectBufferType *exists = DetectEngineBufferTypeGetById(de_ctx, id);
797✔
1389
    if (!exists) {
797✔
1390
        return NULL;
×
1391
    }
×
1392
    return exists->description;
797✔
1393
}
797✔
1394

1395
void DetectEngineBufferTypeSupportsFrames(DetectEngineCtx *de_ctx, const char *name)
1396
{
6,970✔
1397
    DetectBufferType *exists = DetectEngineBufferTypeLookupByName(de_ctx, name);
6,970✔
1398
    BUG_ON(!exists);
6,970✔
1399
    exists->frame = true;
6,970✔
1400
    SCLogDebug("%p %s -- %d supports frame inspection", exists, name, exists->id);
6,970✔
1401
}
6,970✔
1402

1403
void DetectEngineBufferTypeSupportsPacket(DetectEngineCtx *de_ctx, const char *name)
1404
{
×
1405
    DetectBufferType *exists = DetectEngineBufferTypeLookupByName(de_ctx, name);
×
1406
    BUG_ON(!exists);
×
1407
    exists->packet = true;
×
1408
    SCLogDebug("%p %s -- %d supports packet inspection", exists, name, exists->id);
×
1409
}
×
1410

1411
void DetectEngineBufferTypeSupportsMpm(DetectEngineCtx *de_ctx, const char *name)
1412
{
6,970✔
1413
    DetectBufferType *exists = DetectEngineBufferTypeLookupByName(de_ctx, name);
6,970✔
1414
    BUG_ON(!exists);
6,970✔
1415
    exists->mpm = true;
6,970✔
1416
    SCLogDebug("%p %s -- %d supports mpm", exists, name, exists->id);
6,970✔
1417
}
6,970✔
1418

1419
void DetectEngineBufferTypeSupportsTransformations(DetectEngineCtx *de_ctx, const char *name)
1420
{
6,970✔
1421
    DetectBufferType *exists = DetectEngineBufferTypeLookupByName(de_ctx, name);
6,970✔
1422
    BUG_ON(!exists);
6,970✔
1423
    exists->supports_transforms = true;
6,970✔
1424
    SCLogDebug("%p %s -- %d supports transformations", exists, name, exists->id);
6,970✔
1425
}
6,970✔
1426

1427
bool DetectEngineBufferTypeSupportsMultiInstanceGetById(const DetectEngineCtx *de_ctx, const int id)
1428
{
554,055✔
1429
    const DetectBufferType *map = DetectEngineBufferTypeGetById(de_ctx, id);
554,055✔
1430
    if (map == NULL)
554,055✔
1431
        return false;
×
1432
    SCLogDebug("map %p id %d multi_instance? %s", map, id, BOOL2STR(map->multi_instance));
554,055✔
1433
    return map->multi_instance;
554,055✔
1434
}
554,055✔
1435

1436
bool DetectEngineBufferTypeSupportsPacketGetById(const DetectEngineCtx *de_ctx, const int id)
1437
{
578,424✔
1438
    const DetectBufferType *map = DetectEngineBufferTypeGetById(de_ctx, id);
578,424✔
1439
    if (map == NULL)
578,424✔
1440
        return false;
×
1441
    SCLogDebug("map %p id %d packet? %d", map, id, map->packet);
578,424✔
1442
    return map->packet;
578,424✔
1443
}
578,424✔
1444

1445
bool DetectEngineBufferTypeSupportsMpmGetById(const DetectEngineCtx *de_ctx, const int id)
1446
{
846,119✔
1447
    const DetectBufferType *map = DetectEngineBufferTypeGetById(de_ctx, id);
846,119✔
1448
    if (map == NULL)
846,119✔
1449
        return false;
202,699✔
1450
    SCLogDebug("map %p id %d mpm? %d", map, id, map->mpm);
643,420✔
1451
    return map->mpm;
643,420✔
1452
}
846,119✔
1453

1454
bool DetectEngineBufferTypeSupportsFramesGetById(const DetectEngineCtx *de_ctx, const int id)
1455
{
568,413✔
1456
    const DetectBufferType *map = DetectEngineBufferTypeGetById(de_ctx, id);
568,413✔
1457
    if (map == NULL)
568,413✔
1458
        return false;
×
1459
    SCLogDebug("map %p id %d frame? %d", map, id, map->frame);
568,413✔
1460
    return map->frame;
568,413✔
1461
}
568,413✔
1462

1463
void DetectBufferTypeRegisterSetupCallback(const char *name,
1464
        void (*SetupCallback)(const DetectEngineCtx *, Signature *, const DetectBufferType *))
1465
{
36✔
1466
    BUG_ON(g_buffer_type_reg_closed);
36✔
1467
    DetectBufferTypeRegister(name);
36✔
1468
    DetectBufferType *exists = DetectBufferTypeLookupByName(name);
36✔
1469
    BUG_ON(!exists);
36✔
1470
    exists->SetupCallback = SetupCallback;
36✔
1471
}
36✔
1472

1473
void DetectEngineBufferRunSetupCallback(const DetectEngineCtx *de_ctx, const int id, Signature *s)
1474
{
1,203,044✔
1475
    const DetectBufferType *map = DetectEngineBufferTypeGetById(de_ctx, id);
1,203,044✔
1476
    if (map && map->SetupCallback) {
1,203,044✔
1477
        map->SetupCallback(de_ctx, s, map);
121,630✔
1478
    }
121,630✔
1479
}
1,203,044✔
1480

1481
void DetectBufferTypeRegisterValidateCallback(
1482
        const char *name, bool (*ValidateCallback)(const Signature *, const char **sigerror,
1483
                                  const DetectBufferType *))
1484
{
48✔
1485
    BUG_ON(g_buffer_type_reg_closed);
48✔
1486
    DetectBufferTypeRegister(name);
48✔
1487
    DetectBufferType *exists = DetectBufferTypeLookupByName(name);
48✔
1488
    BUG_ON(!exists);
48✔
1489
    exists->ValidateCallback = ValidateCallback;
48✔
1490
}
48✔
1491

1492
bool DetectEngineBufferRunValidateCallback(
1493
        const DetectEngineCtx *de_ctx, const int id, const Signature *s, const char **sigerror)
1494
{
603,818✔
1495
    const DetectBufferType *map = DetectEngineBufferTypeGetById(de_ctx, id);
603,818✔
1496
    // only run validation if the buffer is not transformed
1497
    if (map && map->ValidateCallback && map->transforms.cnt == 0) {
603,818✔
1498
        return map->ValidateCallback(s, sigerror, map);
169,535✔
1499
    }
169,535✔
1500
    return true;
434,283✔
1501
}
603,818✔
1502

1503
bool DetectBufferIsPresent(const Signature *s, const uint32_t buf_id)
UNCOV
1504
{
×
UNCOV
1505
    for (uint32_t i = 0; i < s->init_data->buffer_index; i++) {
×
1506
        if (buf_id == s->init_data->buffers[i].id) {
×
1507
            return true;
×
1508
        }
×
1509
    }
×
UNCOV
1510
    return false;
×
UNCOV
1511
}
×
1512

1513
/** \brief Check content byte array compatibility with transforms
1514
 *
1515
 *  The "content" array is presented to the transforms so that each
1516
 *  transform may validate that it's compatible with the transform.
1517
 *
1518
 *  When a transform indicates the byte array is incompatible, none of the
1519
 *  subsequent transforms, if any, are invoked. This means the first validation
1520
 *  failure terminates the loop.
1521
 *
1522
 *  \param de_ctx Detection engine context.
1523
 *  \param sm_list The SM list id.
1524
 *  \param content The byte array being validated
1525
 *  \param namestr returns the name of the transform that is incompatible with
1526
 *  content.
1527
 *
1528
 *  \retval true (false) If any of the transforms indicate the byte array is
1529
 *  (is not) compatible.
1530
 **/
1531
bool DetectEngineBufferTypeValidateTransform(DetectEngineCtx *de_ctx, int sm_list,
1532
        const uint8_t *content, uint16_t content_len, const char **namestr)
1533
{
301,858✔
1534
    const DetectBufferType *dbt = DetectEngineBufferTypeGetById(de_ctx, sm_list);
301,858✔
1535
    BUG_ON(dbt == NULL);
301,858✔
1536

1537
    for (int i = 0; i < dbt->transforms.cnt; i++) {
425,274✔
1538
        const TransformData *t = &dbt->transforms.transforms[i];
128,908✔
1539
        if (!sigmatch_table[t->transform].TransformValidate)
128,908✔
1540
            continue;
91,082✔
1541

1542
        if (sigmatch_table[t->transform].TransformValidate(content, content_len, t->options)) {
37,826✔
1543
            continue;
32,334✔
1544
        }
32,334✔
1545

1546
        if (namestr) {
5,492✔
1547
            *namestr = sigmatch_table[t->transform].name;
5,492✔
1548
        }
5,492✔
1549

1550
        return false;
5,492✔
1551
    }
37,826✔
1552

1553
    return true;
296,366✔
1554
}
301,858✔
1555

1556
static void DetectBufferTypeSetupDetectEngine(DetectEngineCtx *de_ctx)
1557
{
35,469✔
1558
    const int size = g_buffer_type_id;
35,469✔
1559
    BUG_ON(!(size > 0));
35,469✔
1560

1561
    de_ctx->buffer_type_hash_name = HashListTableInit(256, DetectBufferTypeHashNameFunc,
35,469✔
1562
            DetectBufferTypeCompareNameFunc, DetectBufferTypeFreeFunc);
35,469✔
1563
    BUG_ON(de_ctx->buffer_type_hash_name == NULL);
35,469✔
1564
    de_ctx->buffer_type_hash_id =
35,469✔
1565
            HashListTableInit(256, DetectBufferTypeHashIdFunc, DetectBufferTypeCompareIdFunc,
35,469✔
1566
                    NULL); // entries owned by buffer_type_hash_name
35,469✔
1567
    BUG_ON(de_ctx->buffer_type_hash_id == NULL);
35,469✔
1568
    de_ctx->buffer_type_id = g_buffer_type_id;
35,469✔
1569

1570
    SCLogDebug("DETECT_SM_LIST_DYNAMIC_START %u", DETECT_SM_LIST_DYNAMIC_START);
35,469✔
1571
    HashListTableBucket *b = HashListTableGetListHead(g_buffer_type_hash);
35,469✔
1572
    while (b) {
14,613,197✔
1573
        DetectBufferType *map = HashListTableGetListData(b);
14,577,728✔
1574

1575
        DetectBufferType *copy = SCCalloc(1, sizeof(*copy));
14,577,728✔
1576
        BUG_ON(!copy);
14,577,728✔
1577
        memcpy(copy, map, sizeof(*copy));
14,577,728✔
1578
        int r = HashListTableAdd(de_ctx->buffer_type_hash_name, (void *)copy, 0);
14,577,728✔
1579
        BUG_ON(r != 0);
14,577,728✔
1580
        r = HashListTableAdd(de_ctx->buffer_type_hash_id, (void *)copy, 0);
14,577,728✔
1581
        BUG_ON(r != 0);
14,577,728✔
1582

1583
        SCLogDebug("name %s id %d mpm %s packet %s -- %s. "
14,577,728✔
1584
                   "Callbacks: Setup %p Validate %p",
14,577,728✔
1585
                map->name, map->id, map->mpm ? "true" : "false", map->packet ? "true" : "false",
14,577,728✔
1586
                map->description, map->SetupCallback, map->ValidateCallback);
14,577,728✔
1587
        b = HashListTableGetListNext(b);
14,577,728✔
1588
    }
14,577,728✔
1589

1590
    PrefilterInit(de_ctx);
35,469✔
1591
    DetectMpmInitializeAppMpms(de_ctx);
35,469✔
1592
    DetectAppLayerInspectEngineCopyListToDetectCtx(de_ctx);
35,469✔
1593
    DetectMpmInitializeFrameMpms(de_ctx);
35,469✔
1594
    DetectFrameInspectEngineCopyListToDetectCtx(de_ctx);
35,469✔
1595
    DetectMpmInitializePktMpms(de_ctx);
35,469✔
1596
    DetectPktInspectEngineCopyListToDetectCtx(de_ctx);
35,469✔
1597
}
35,469✔
1598

1599
static void DetectBufferTypeFreeDetectEngine(DetectEngineCtx *de_ctx)
1600
{
35,466✔
1601
    if (de_ctx) {
35,466✔
1602
        if (de_ctx->buffer_type_hash_name)
35,466✔
1603
            HashListTableFree(de_ctx->buffer_type_hash_name);
35,466✔
1604
        if (de_ctx->buffer_type_hash_id)
35,466✔
1605
            HashListTableFree(de_ctx->buffer_type_hash_id);
35,466✔
1606

1607
        DetectEngineAppInspectionEngine *ilist = de_ctx->app_inspect_engines;
35,466✔
1608
        while (ilist) {
24,968,247✔
1609
            DetectEngineAppInspectionEngine *next = ilist->next;
24,932,781✔
1610
            SCFree(ilist);
24,932,781✔
1611
            ilist = next;
24,932,781✔
1612
        }
24,932,781✔
1613
        DetectBufferMpmRegistry *mlist = de_ctx->app_mpms_list;
35,466✔
1614
        while (mlist) {
13,796,457✔
1615
            DetectBufferMpmRegistry *next = mlist->next;
13,760,991✔
1616
            SCFree(mlist);
13,760,991✔
1617
            mlist = next;
13,760,991✔
1618
        }
13,760,991✔
1619
        DetectEnginePktInspectionEngine *plist = de_ctx->pkt_inspect_engines;
35,466✔
1620
        while (plist) {
356,191✔
1621
            DetectEnginePktInspectionEngine *next = plist->next;
320,725✔
1622
            SCFree(plist);
320,725✔
1623
            plist = next;
320,725✔
1624
        }
320,725✔
1625
        DetectBufferMpmRegistry *pmlist = de_ctx->pkt_mpms_list;
35,466✔
1626
        while (pmlist) {
356,191✔
1627
            DetectBufferMpmRegistry *next = pmlist->next;
320,725✔
1628
            SCFree(pmlist);
320,725✔
1629
            pmlist = next;
320,725✔
1630
        }
320,725✔
1631
        DetectEngineFrameInspectionEngine *framelist = de_ctx->frame_inspect_engines;
35,466✔
1632
        while (framelist) {
43,182✔
1633
            DetectEngineFrameInspectionEngine *next = framelist->next;
7,716✔
1634
            SCFree(framelist);
7,716✔
1635
            framelist = next;
7,716✔
1636
        }
7,716✔
1637
        DetectBufferMpmRegistry *framemlist = de_ctx->frame_mpms_list;
35,466✔
1638
        while (framemlist) {
43,182✔
1639
            DetectBufferMpmRegistry *next = framemlist->next;
7,716✔
1640
            SCFree(framemlist);
7,716✔
1641
            framemlist = next;
7,716✔
1642
        }
7,716✔
1643
        PrefilterDeinit(de_ctx);
35,466✔
1644
    }
35,466✔
1645
}
35,466✔
1646

1647
void DetectBufferTypeCloseRegistration(void)
1648
{
3✔
1649
    BUG_ON(g_buffer_type_hash == NULL);
3✔
1650

1651
    g_buffer_type_reg_closed = 1;
3✔
1652
}
3✔
1653

1654
int DetectEngineBufferTypeGetByIdTransforms(
1655
        DetectEngineCtx *de_ctx, const int id, TransformData *transforms, int transform_cnt)
1656
{
99,028✔
1657
    const DetectBufferType *base_map = DetectEngineBufferTypeGetById(de_ctx, id);
99,028✔
1658
    if (!base_map) {
99,028✔
1659
        return -1;
×
1660
    }
×
1661
    if (!base_map->supports_transforms) {
99,028✔
1662
        SCLogError("buffer '%s' does not support transformations", base_map->name);
×
1663
        return -1;
×
1664
    }
×
1665

1666
    SCLogDebug("base_map %s", base_map->name);
99,028✔
1667

1668
    DetectEngineTransforms t;
99,028✔
1669
    memset(&t, 0, sizeof(t));
99,028✔
1670
    for (int i = 0; i < transform_cnt; i++) {
229,108✔
1671
        t.transforms[i] = transforms[i];
130,080✔
1672
    }
130,080✔
1673
    t.cnt = transform_cnt;
99,028✔
1674

1675
    DetectBufferType lookup_map;
99,028✔
1676
    memset(&lookup_map, 0, sizeof(lookup_map));
99,028✔
1677
    strlcpy(lookup_map.name, base_map->name, sizeof(lookup_map.name));
99,028✔
1678
    lookup_map.transforms = t;
99,028✔
1679

1680
    /* Add transform identity data from transforms */
1681
    if (t.cnt) {
99,028✔
1682
        DetectBufferAddTransformData(&lookup_map);
99,028✔
1683
    }
99,028✔
1684
    DetectBufferType *res = HashListTableLookup(de_ctx->buffer_type_hash_name, &lookup_map, 0);
99,028✔
1685

1686
    SCLogDebug("res %p", res);
99,028✔
1687
    if (res != NULL) {
99,028✔
1688
        return res->id;
33,488✔
1689
    }
33,488✔
1690

1691
    DetectBufferType *map = SCCalloc(1, sizeof(*map));
65,540✔
1692
    if (map == NULL)
65,540✔
1693
        return -1;
×
1694

1695
    strlcpy(map->name, base_map->name, sizeof(map->name));
65,540✔
1696
    map->id = de_ctx->buffer_type_id++;
65,540✔
1697
    map->parent_id = base_map->id;
65,540✔
1698
    map->transforms = t;
65,540✔
1699
    map->mpm = base_map->mpm;
65,540✔
1700
    map->packet = base_map->packet;
65,540✔
1701
    map->frame = base_map->frame;
65,540✔
1702
    map->SetupCallback = base_map->SetupCallback;
65,540✔
1703
    map->ValidateCallback = base_map->ValidateCallback;
65,540✔
1704
    if (map->frame) {
65,540✔
1705
        DetectFrameMpmRegisterByParentId(de_ctx, map->id, map->parent_id, &map->transforms);
375✔
1706
    } else if (map->packet) {
65,165✔
1707
        DetectPktMpmRegisterByParentId(de_ctx,
1,531✔
1708
                map->id, map->parent_id, &map->transforms);
1,531✔
1709
    } else {
63,634✔
1710
        DetectAppLayerMpmRegisterByParentId(de_ctx,
63,634✔
1711
                map->id, map->parent_id, &map->transforms);
63,634✔
1712
    }
63,634✔
1713

1714
    BUG_ON(HashListTableAdd(de_ctx->buffer_type_hash_name, (void *)map, 0) != 0);
65,540✔
1715
    BUG_ON(HashListTableAdd(de_ctx->buffer_type_hash_id, (void *)map, 0) != 0);
65,540✔
1716
    SCLogDebug("buffer %s registered with id %d, parent %d", map->name, map->id, map->parent_id);
65,540✔
1717

1718
    if (map->frame) {
65,540✔
1719
        DetectFrameInspectEngineCopy(de_ctx, map->parent_id, map->id, &map->transforms);
375✔
1720
    } else if (map->packet) {
65,165✔
1721
        DetectPktInspectEngineCopy(de_ctx, map->parent_id, map->id, &map->transforms);
1,531✔
1722
    } else {
63,634✔
1723
        DetectAppLayerInspectEngineCopy(de_ctx, map->parent_id, map->id, &map->transforms);
63,634✔
1724
    }
63,634✔
1725
    return map->id;
65,540✔
1726
}
65,540✔
1727

1728
/* returns false if no match, true if match */
1729
static int DetectEngineInspectRulePacketMatches(
1730
    DetectEngineThreadCtx *det_ctx,
1731
    const DetectEnginePktInspectionEngine *engine,
1732
    const Signature *s,
1733
    Packet *p, uint8_t *_alert_flags)
1734
{
285,632✔
1735
    SCEnter();
285,632✔
1736

1737
    /* run the packet match functions */
1738
    KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_MATCH);
285,632✔
1739
    const SigMatchData *smd = s->sm_arrays[DETECT_SM_LIST_MATCH];
285,632✔
1740

1741
    SCLogDebug("running match functions, sm %p", smd);
285,632✔
1742
    while (1) {
293,888✔
1743
        KEYWORD_PROFILING_START;
293,888✔
1744
        if (sigmatch_table[smd->type].Match(det_ctx, p, s, smd->ctx) <= 0) {
293,888✔
1745
            KEYWORD_PROFILING_END(det_ctx, smd->type, 0);
233,882✔
1746
            SCLogDebug("no match");
233,882✔
1747
            return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
233,882✔
1748
        }
233,882✔
1749
        KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
60,006✔
1750
        if (smd->is_last) {
60,006✔
1751
            SCLogDebug("match and is_last");
51,750✔
1752
            break;
51,750✔
1753
        }
51,750✔
1754
        smd++;
8,256✔
1755
    }
8,256✔
1756
    return DETECT_ENGINE_INSPECT_SIG_MATCH;
51,750✔
1757
}
285,632✔
1758

1759
static int DetectEngineInspectRulePayloadMatches(
1760
     DetectEngineThreadCtx *det_ctx,
1761
     const DetectEnginePktInspectionEngine *engine,
1762
     const Signature *s, Packet *p, uint8_t *alert_flags)
1763
{
43,318✔
1764
    SCEnter();
43,318✔
1765

1766
    DetectEngineCtx *de_ctx = det_ctx->de_ctx;
43,318✔
1767

1768
    KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_PMATCH);
43,318✔
1769
    /* if we have stream msgs, inspect against those first,
1770
     * but not for a "dsize" signature */
1771
    if (s->flags & SIG_FLAG_REQUIRE_STREAM) {
43,318✔
1772
        int pmatch = 0;
30,436✔
1773
        if (p->flags & PKT_DETECT_HAS_STREAMDATA) {
30,436✔
1774
            pmatch = DetectEngineInspectStreamPayload(de_ctx, det_ctx, s, p->flow, p);
8,084✔
1775
            if (pmatch) {
8,084✔
1776
                *alert_flags |= PACKET_ALERT_FLAG_STREAM_MATCH;
941✔
1777
            }
941✔
1778
        }
8,084✔
1779
        /* no match? then inspect packet payload */
1780
        if (pmatch == 0) {
30,436✔
1781
            SCLogDebug("no match in stream, fall back to packet payload");
29,495✔
1782

1783
            /* skip if we don't have to inspect the packet and segment was
1784
             * added to stream */
1785
            if (!(s->flags & SIG_FLAG_REQUIRE_PACKET) && (p->flags & PKT_STREAM_ADD)) {
29,495✔
1786
                return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
21,570✔
1787
            }
21,570✔
1788
            if (s->flags & SIG_FLAG_REQUIRE_STREAM_ONLY) {
7,925✔
1789
                SCLogDebug("SIG_FLAG_REQUIRE_STREAM_ONLY, so no match");
4✔
1790
                return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
4✔
1791
            }
4✔
1792
            if (DetectEngineInspectPacketPayload(de_ctx, det_ctx, s, p->flow, p) != 1) {
7,921✔
1793
                return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
6,262✔
1794
            }
6,262✔
1795
        }
7,921✔
1796
    } else {
30,436✔
1797
        if (DetectEngineInspectPacketPayload(de_ctx, det_ctx, s, p->flow, p) != 1) {
12,882✔
1798
            return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
9,894✔
1799
        }
9,894✔
1800
    }
12,882✔
1801
    return DETECT_ENGINE_INSPECT_SIG_MATCH;
5,588✔
1802
}
43,318✔
1803

1804
bool DetectEnginePktInspectionRun(ThreadVars *tv,
1805
        DetectEngineThreadCtx *det_ctx, const Signature *s,
1806
        Flow *f, Packet *p,
1807
        uint8_t *alert_flags)
1808
{
472,953✔
1809
    SCEnter();
472,953✔
1810

1811
    for (DetectEnginePktInspectionEngine *e = s->pkt_inspect; e != NULL; e = e->next) {
534,134✔
1812
        if (e->v1.Callback(det_ctx, e, s, p, alert_flags) != DETECT_ENGINE_INSPECT_SIG_MATCH) {
335,986✔
1813
            SCLogDebug("sid %u: e %p Callback returned no match", s->id, e);
274,805✔
1814
            return false;
274,805✔
1815
        }
274,805✔
1816
        SCLogDebug("sid %u: e %p Callback returned true", s->id, e);
61,181✔
1817
    }
61,181✔
1818

1819
    SCLogDebug("sid %u: returning true", s->id);
198,148✔
1820
    return true;
198,148✔
1821
}
472,953✔
1822

1823
/**
1824
 * \param data pointer to SigMatchData. Allowed to be NULL.
1825
 */
1826
static int DetectEnginePktInspectionAppend(Signature *s, InspectionBufferPktInspectFunc Callback,
1827
        SigMatchData *data, const int list_id)
1828
{
60,525✔
1829
    DetectEnginePktInspectionEngine *e = SCCalloc(1, sizeof(*e));
60,525✔
1830
    if (e == NULL)
60,525✔
1831
        return -1;
×
1832

1833
    e->mpm = s->init_data->mpm_sm_list == list_id;
60,525✔
1834
    DEBUG_VALIDATE_BUG_ON(list_id < 0 || list_id > UINT16_MAX);
60,525✔
1835
    e->sm_list = (uint16_t)list_id;
60,525✔
1836
    e->sm_list_base = (uint16_t)list_id;
60,525✔
1837
    e->v1.Callback = Callback;
60,525✔
1838
    e->smd = data;
60,525✔
1839

1840
    if (s->pkt_inspect == NULL) {
60,525✔
1841
        s->pkt_inspect = e;
56,624✔
1842
    } else {
56,624✔
1843
        DetectEnginePktInspectionEngine *a = s->pkt_inspect;
3,901✔
1844
        while (a->next != NULL) {
4,041✔
1845
            a = a->next;
140✔
1846
        }
140✔
1847
        a->next = e;
3,901✔
1848
    }
3,901✔
1849
    return 0;
60,525✔
1850
}
60,525✔
1851

1852
int DetectEnginePktInspectionSetup(Signature *s)
1853
{
119,313✔
1854
    /* only handle PMATCH here if we're not an app inspect rule */
1855
    if (s->sm_arrays[DETECT_SM_LIST_PMATCH] && (s->init_data->init_flags & SIG_FLAG_INIT_STATE_MATCH) == 0) {
119,313✔
1856
        if (DetectEnginePktInspectionAppend(
15,755✔
1857
                    s, DetectEngineInspectRulePayloadMatches, NULL, DETECT_SM_LIST_PMATCH) < 0)
15,755✔
1858
            return -1;
×
1859
        SCLogDebug("sid %u: DetectEngineInspectRulePayloadMatches appended", s->id);
15,755✔
1860
    }
15,755✔
1861

1862
    if (s->sm_arrays[DETECT_SM_LIST_MATCH]) {
119,313✔
1863
        if (DetectEnginePktInspectionAppend(
44,770✔
1864
                    s, DetectEngineInspectRulePacketMatches, NULL, DETECT_SM_LIST_MATCH) < 0)
44,770✔
1865
            return -1;
×
1866
        SCLogDebug("sid %u: DetectEngineInspectRulePacketMatches appended", s->id);
44,770✔
1867
    }
44,770✔
1868

1869
    return 0;
119,313✔
1870
}
119,313✔
1871

1872
/* code to control the main thread to do a reload */
1873

1874
enum DetectEngineSyncState {
1875
    IDLE,   /**< ready to start a reload */
1876
    RELOAD, /**< command main thread to do the reload */
1877
};
1878

1879

1880
typedef struct DetectEngineSyncer_ {
1881
    SCMutex m;
1882
    enum DetectEngineSyncState state;
1883
} DetectEngineSyncer;
1884

1885
static DetectEngineSyncer detect_sync = { SCMUTEX_INITIALIZER, IDLE };
1886

1887
/* tell main to start reloading */
1888
int DetectEngineReloadStart(void)
UNCOV
1889
{
×
UNCOV
1890
    int r = 0;
×
UNCOV
1891
    SCMutexLock(&detect_sync.m);
×
UNCOV
1892
    if (detect_sync.state == IDLE) {
×
UNCOV
1893
        detect_sync.state = RELOAD;
×
UNCOV
1894
    } else {
×
1895
        r = -1;
×
1896
    }
×
UNCOV
1897
    SCMutexUnlock(&detect_sync.m);
×
UNCOV
1898
    return r;
×
UNCOV
1899
}
×
1900

1901
/* main thread checks this to see if it should start */
1902
int DetectEngineReloadIsStart(void)
UNCOV
1903
{
×
UNCOV
1904
    int r = 0;
×
UNCOV
1905
    SCMutexLock(&detect_sync.m);
×
UNCOV
1906
    if (detect_sync.state == RELOAD) {
×
UNCOV
1907
        r = 1;
×
UNCOV
1908
    }
×
UNCOV
1909
    SCMutexUnlock(&detect_sync.m);
×
UNCOV
1910
    return r;
×
UNCOV
1911
}
×
1912

1913
/* main thread sets done when it's done */
1914
void DetectEngineReloadSetIdle(void)
UNCOV
1915
{
×
UNCOV
1916
    SCMutexLock(&detect_sync.m);
×
UNCOV
1917
    detect_sync.state = IDLE;
×
UNCOV
1918
    SCMutexUnlock(&detect_sync.m);
×
UNCOV
1919
}
×
1920

1921
/* caller loops this until it returns 1 */
1922
int DetectEngineReloadIsIdle(void)
UNCOV
1923
{
×
UNCOV
1924
    int r = 0;
×
UNCOV
1925
    SCMutexLock(&detect_sync.m);
×
UNCOV
1926
    if (detect_sync.state == IDLE) {
×
UNCOV
1927
        r = 1;
×
UNCOV
1928
    }
×
UNCOV
1929
    SCMutexUnlock(&detect_sync.m);
×
UNCOV
1930
    return r;
×
UNCOV
1931
}
×
1932

1933
/** \brief Do the content inspection & validation for a signature
1934
 *
1935
 *  \param de_ctx Detection engine context
1936
 *  \param det_ctx Detection engine thread context
1937
 *  \param s Signature to inspect
1938
 *  \param sm SigMatch to inspect
1939
 *  \param f Flow
1940
 *  \param flags app layer flags
1941
 *  \param state App layer state
1942
 *
1943
 *  \retval 0 no match
1944
 *  \retval 1 match
1945
 */
1946
uint8_t DetectEngineInspectGenericList(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
1947
        const struct DetectEngineAppInspectionEngine_ *engine, const Signature *s, Flow *f,
1948
        uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
1949
{
88,550✔
1950
    SigMatchData *smd = engine->smd;
88,550✔
1951
    SCLogDebug("running match functions, sm %p", smd);
88,550✔
1952
    if (smd != NULL) {
88,550✔
1953
        while (1) {
89,277✔
1954
            int match = 0;
89,277✔
1955
            KEYWORD_PROFILING_START;
89,277✔
1956
            match = sigmatch_table[smd->type].
89,277✔
1957
                AppLayerTxMatch(det_ctx, f, flags, alstate, txv, s, smd->ctx);
89,277✔
1958
            KEYWORD_PROFILING_END(det_ctx, smd->type, (match == 1));
89,277✔
1959
            if (match == 0)
89,277✔
1960
                return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
65,703✔
1961
            if (match == 2) {
23,574✔
1962
                return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
×
1963
            }
×
1964

1965
            if (smd->is_last)
23,574✔
1966
                break;
22,847✔
1967
            smd++;
727✔
1968
        }
727✔
1969
    }
88,550✔
1970

1971
    return DETECT_ENGINE_INSPECT_SIG_MATCH;
22,847✔
1972
}
88,550✔
1973

1974
/**
1975
 * \brief Do the content inspection & validation for a signature
1976
 *
1977
 * \param de_ctx Detection engine context
1978
 * \param det_ctx Detection engine thread context
1979
 * \param s Signature to inspect
1980
 * \param f Flow
1981
 * \param flags app layer flags
1982
 * \param state App layer state
1983
 *
1984
 * \retval 0 no match.
1985
 * \retval 1 match.
1986
 * \retval 2 Sig can't match.
1987
 */
1988
uint8_t DetectEngineInspectBufferSingle(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
1989
        const DetectEngineAppInspectionEngine *engine, const Signature *s, Flow *f, uint8_t flags,
1990
        void *alstate, void *txv, uint64_t tx_id)
1991
{
1,362✔
1992
    const int list_id = engine->sm_list;
1,362✔
1993
    SCLogDebug("running inspect on %d", list_id);
1,362✔
1994

1995
    const bool eof =
1,362✔
1996
            (AppLayerParserGetStateProgress(f->proto, f->alproto, txv, flags) > engine->progress);
1,362✔
1997

1998
    SCLogDebug("list %d mpm? %s transforms %p", engine->sm_list, engine->mpm ? "true" : "false",
1,362✔
1999
            engine->v2.transforms);
1,362✔
2000

2001
    /* if prefilter didn't already run, we need to consider transformations */
2002
    const DetectEngineTransforms *transforms = NULL;
1,362✔
2003
    if (!engine->mpm) {
1,362✔
2004
        transforms = engine->v2.transforms;
177✔
2005
    }
177✔
2006

2007
    const InspectionBuffer *buffer = DetectGetSingleData(
1,362✔
2008
            det_ctx, transforms, f, flags, txv, list_id, engine->v2.GetDataSingle);
1,362✔
2009
    if (unlikely(buffer == NULL)) {
1,362✔
2010
        if (eof && engine->match_on_null) {
92✔
2011
            return DETECT_ENGINE_INSPECT_SIG_MATCH;
×
2012
        }
×
2013
        return eof ? DETECT_ENGINE_INSPECT_SIG_CANT_MATCH : DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
92✔
2014
    }
92✔
2015

2016
    const uint32_t data_len = buffer->inspect_len;
1,270✔
2017
    const uint8_t *data = buffer->inspect;
1,270✔
2018
    const uint64_t offset = buffer->inspect_offset;
1,270✔
2019

2020
    uint8_t ci_flags = eof ? DETECT_CI_FLAGS_END : 0;
1,270✔
2021
    ci_flags |= (offset == 0 ? DETECT_CI_FLAGS_START : 0);
1,270✔
2022
    ci_flags |= buffer->flags;
1,270✔
2023

2024
    /* Inspect all the uricontents fetched on each
2025
     * transaction at the app layer */
2026
    const bool match = DetectEngineContentInspection(de_ctx, det_ctx, s, engine->smd, NULL, f, data,
1,270✔
2027
            data_len, offset, ci_flags, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE);
1,270✔
2028
    if (match) {
1,270✔
2029
        return DETECT_ENGINE_INSPECT_SIG_MATCH;
1,265✔
2030
    } else {
1,265✔
2031
        return eof ? DETECT_ENGINE_INSPECT_SIG_CANT_MATCH : DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
5✔
2032
    }
5✔
2033
}
1,270✔
2034

2035
/**
2036
 * \brief Do the content inspection & validation for a signature
2037
 *
2038
 * \param de_ctx Detection engine context
2039
 * \param det_ctx Detection engine thread context
2040
 * \param s Signature to inspect
2041
 * \param f Flow
2042
 * \param flags app layer flags
2043
 * \param state App layer state
2044
 *
2045
 * \retval 0 no match.
2046
 * \retval 1 match.
2047
 * \retval 2 Sig can't match.
2048
 */
2049
uint8_t DetectEngineInspectBufferGeneric(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
2050
        const DetectEngineAppInspectionEngine *engine, const Signature *s, Flow *f, uint8_t flags,
2051
        void *alstate, void *txv, uint64_t tx_id)
2052
{
4,894✔
2053
    const int list_id = engine->sm_list;
4,894✔
2054
    SCLogDebug("running inspect on %d", list_id);
4,894✔
2055

2056
    const bool eof = (AppLayerParserGetStateProgress(f->proto, f->alproto, txv, flags) > engine->progress);
4,894✔
2057

2058
    SCLogDebug("list %d mpm? %s transforms %p",
4,894✔
2059
            engine->sm_list, engine->mpm ? "true" : "false", engine->v2.transforms);
4,894✔
2060

2061
    /* if prefilter didn't already run, we need to consider transformations */
2062
    const DetectEngineTransforms *transforms = NULL;
4,894✔
2063
    if (!engine->mpm) {
4,894✔
2064
        transforms = engine->v2.transforms;
1,470✔
2065
    }
1,470✔
2066

2067
    const InspectionBuffer *buffer = engine->v2.GetData(det_ctx, transforms,
4,894✔
2068
            f, flags, txv, list_id);
4,894✔
2069
    if (unlikely(buffer == NULL)) {
4,894✔
2070
        if (eof && engine->match_on_null) {
412✔
UNCOV
2071
            return DETECT_ENGINE_INSPECT_SIG_MATCH;
×
UNCOV
2072
        }
×
2073
        return eof ? DETECT_ENGINE_INSPECT_SIG_CANT_MATCH :
412✔
2074
                     DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
412✔
2075
    }
412✔
2076

2077
    const uint32_t data_len = buffer->inspect_len;
4,482✔
2078
    const uint8_t *data = buffer->inspect;
4,482✔
2079
    const uint64_t offset = buffer->inspect_offset;
4,482✔
2080

2081
    uint8_t ci_flags = eof ? DETECT_CI_FLAGS_END : 0;
4,482✔
2082
    ci_flags |= (offset == 0 ? DETECT_CI_FLAGS_START : 0);
4,482✔
2083
    ci_flags |= buffer->flags;
4,482✔
2084

2085
    /* Inspect all the uricontents fetched on each
2086
     * transaction at the app layer */
2087
    const bool match = DetectEngineContentInspection(de_ctx, det_ctx, s, engine->smd, NULL, f, data,
4,482✔
2088
            data_len, offset, ci_flags, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE);
4,482✔
2089
    if (match) {
4,482✔
2090
        return DETECT_ENGINE_INSPECT_SIG_MATCH;
3,799✔
2091
    } else {
3,799✔
2092
        return eof ? DETECT_ENGINE_INSPECT_SIG_CANT_MATCH :
683✔
2093
                     DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
683✔
2094
    }
683✔
2095
}
4,482✔
2096

2097
// wrapper for both DetectAppLayerInspectEngineRegister and DetectAppLayerMpmRegister
2098
// with cast of callback function
2099
void DetectAppLayerMultiRegister(const char *name, AppProto alproto, uint32_t dir, int progress,
2100
        InspectionMultiBufferGetDataPtr GetData, int priority)
2101
{
228✔
2102
    AppLayerInspectEngineRegisterInternal(name, alproto, dir, progress,
228✔
2103
            DetectEngineInspectMultiBufferGeneric, NULL, NULL, GetData);
228✔
2104
    DetectAppLayerMpmMultiRegister(
228✔
2105
            name, dir, priority, PrefilterMultiGenericMpmRegister, GetData, alproto, progress);
228✔
2106
}
228✔
2107

2108
InspectionBuffer *DetectGetSingleData(struct DetectEngineThreadCtx_ *det_ctx,
2109
        const DetectEngineTransforms *transforms, Flow *f, const uint8_t flow_flags, void *txv,
2110
        const int list_id, InspectionSingleBufferGetDataPtr GetBuf)
2111
{
5,838✔
2112
    InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
5,838✔
2113
    if (buffer->inspect == NULL) {
5,838✔
2114
        const uint8_t *b = NULL;
4,650✔
2115
        uint32_t b_len = 0;
4,650✔
2116

2117
        if (!GetBuf(txv, flow_flags, &b, &b_len))
4,650✔
2118
            return NULL;
2,551✔
2119

2120
        InspectionBufferSetupAndApplyTransforms(det_ctx, list_id, buffer, b, b_len, transforms);
2,099✔
2121
    }
2,099✔
2122
    return buffer;
3,287✔
2123
}
5,838✔
2124

2125
InspectionBuffer *DetectGetMultiData(struct DetectEngineThreadCtx_ *det_ctx,
2126
        const DetectEngineTransforms *transforms, Flow *f, const uint8_t flow_flags, void *txv,
2127
        const int list_id, uint32_t index, InspectionMultiBufferGetDataPtr GetBuf)
2128
{
10,630✔
2129
    InspectionBuffer *buffer = InspectionBufferMultipleForListGet(det_ctx, list_id, index);
10,630✔
2130
    if (buffer == NULL) {
10,630✔
2131
        return NULL;
×
2132
    }
×
2133
    if (buffer->initialized) {
10,630✔
2134
        return buffer;
1,920✔
2135
    }
1,920✔
2136

2137
    const uint8_t *data = NULL;
8,710✔
2138
    uint32_t data_len = 0;
8,710✔
2139

2140
    if (!GetBuf(det_ctx, txv, flow_flags, index, &data, &data_len)) {
8,710✔
2141
        InspectionBufferSetupMultiEmpty(buffer);
2,661✔
2142
        return NULL;
2,661✔
2143
    }
2,661✔
2144
    InspectionBufferSetupMulti(det_ctx, buffer, transforms, data, data_len);
6,049✔
2145
    buffer->flags = DETECT_CI_FLAGS_SINGLE;
6,049✔
2146
    return buffer;
6,049✔
2147
}
8,710✔
2148

2149
uint8_t DetectEngineInspectMultiBufferGeneric(DetectEngineCtx *de_ctx,
2150
        DetectEngineThreadCtx *det_ctx, const DetectEngineAppInspectionEngine *engine,
2151
        const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
2152
{
1,338✔
2153
    uint32_t local_id = 0;
1,338✔
2154
    const DetectEngineTransforms *transforms = NULL;
1,338✔
2155
    if (!engine->mpm) {
1,338✔
2156
        transforms = engine->v2.transforms;
450✔
2157
    }
450✔
2158

2159
    do {
2,458✔
2160
        InspectionBuffer *buffer = DetectGetMultiData(det_ctx, transforms, f, flags, txv,
2,458✔
2161
                engine->sm_list, local_id, engine->v2.GetMultiData);
2,458✔
2162

2163
        if (buffer == NULL || buffer->inspect == NULL)
2,458✔
2164
            break;
353✔
2165

2166
        // The GetData functions set buffer->flags to DETECT_CI_FLAGS_SINGLE
2167
        // This is not meant for streaming buffers
2168
        const bool match = DetectEngineContentInspectionBuffer(de_ctx, det_ctx, s, engine->smd,
2,105✔
2169
                NULL, f, buffer, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE);
2,105✔
2170
        if (match) {
2,105✔
2171
            return DETECT_ENGINE_INSPECT_SIG_MATCH;
985✔
2172
        }
985✔
2173
        local_id++;
1,120✔
2174
    } while (1);
1,120✔
2175
    if (local_id == 0) {
353✔
2176
        // That means we did not get even one buffer value from the multi-buffer
2177
        const bool eof = (AppLayerParserGetStateProgress(f->proto, f->alproto, txv, flags) >
55✔
2178
                          engine->progress);
55✔
2179
        if (eof && engine->match_on_null) {
55✔
2180
            return DETECT_ENGINE_INSPECT_SIG_MATCH;
20✔
2181
        }
20✔
2182
    }
55✔
2183
    return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
333✔
2184
}
353✔
2185

2186
/**
2187
 * \brief Do the content inspection & validation for a signature
2188
 *
2189
 * \param de_ctx Detection engine context
2190
 * \param det_ctx Detection engine thread context
2191
 * \param s Signature to inspect
2192
 * \param p Packet
2193
 *
2194
 * \retval 0 no match.
2195
 * \retval 1 match.
2196
 */
2197
int DetectEngineInspectPktBufferGeneric(
2198
        DetectEngineThreadCtx *det_ctx,
2199
        const DetectEnginePktInspectionEngine *engine,
2200
        const Signature *s, Packet *p, uint8_t *_alert_flags)
2201
{
7,036✔
2202
    const int list_id = engine->sm_list;
7,036✔
2203
    SCLogDebug("running inspect on %d", list_id);
7,036✔
2204

2205
    SCLogDebug("list %d transforms %p",
7,036✔
2206
            engine->sm_list, engine->v1.transforms);
7,036✔
2207

2208
    /* if prefilter didn't already run, we need to consider transformations */
2209
    const DetectEngineTransforms *transforms = NULL;
7,036✔
2210
    if (!engine->mpm) {
7,036✔
2211
        transforms = engine->v1.transforms;
5,172✔
2212
    }
5,172✔
2213

2214
    const InspectionBuffer *buffer = engine->v1.GetData(det_ctx, transforms, p,
7,036✔
2215
            list_id);
7,036✔
2216
    if (unlikely(buffer == NULL)) {
7,036✔
2217
        return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
1✔
2218
    }
1✔
2219

2220
    uint8_t ci_flags = DETECT_CI_FLAGS_START|DETECT_CI_FLAGS_END;
7,035✔
2221
    ci_flags |= buffer->flags;
7,035✔
2222

2223
    /* Inspect all the uricontents fetched on each
2224
     * transaction at the app layer */
2225
    const bool match = DetectEngineContentInspection(det_ctx->de_ctx, det_ctx, s, engine->smd, p,
7,035✔
2226
            p->flow, buffer->inspect, buffer->inspect_len, 0, ci_flags,
7,035✔
2227
            DETECT_ENGINE_CONTENT_INSPECTION_MODE_HEADER);
7,035✔
2228
    if (match) {
7,035✔
2229
        return DETECT_ENGINE_INSPECT_SIG_MATCH;
3,843✔
2230
    } else {
3,843✔
2231
        return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
3,192✔
2232
    }
3,192✔
2233
}
7,035✔
2234

2235
/** \internal
2236
 *  \brief inject a pseudo packet into each detect thread
2237
 *         if the thread should flush its output logs.
2238
 */
2239
void InjectPacketsForFlush(ThreadVars **detect_tvs, int no_of_detect_tvs)
2240
{
×
2241
    /* inject a fake packet if the detect thread that needs it. This function
2242
     * is called when a heartbeat log-flush request has been made
2243
     * and it should process a pseudo packet and flush its output logs
2244
     * to speed the process. */
2245
#if DEBUG
2246
    int count = 0;
2247
#endif
2248
    for (int i = 0; i < no_of_detect_tvs; i++) {
×
2249
        if (detect_tvs[i]) { // && detect_tvs[i]->inq != NULL) {
×
2250
            Packet *p = PacketGetFromAlloc();
×
2251
            if (p != NULL) {
×
2252
                SCLogDebug("Injecting pkt for tv %s[i=%d] %d", detect_tvs[i]->name, i, count++);
×
2253
                p->flags |= PKT_PSEUDO_STREAM_END;
×
2254
                p->flags |= PKT_PSEUDO_LOG_FLUSH;
×
2255
                PKT_SET_SRC(p, PKT_SRC_DETECT_RELOAD_FLUSH);
×
2256
                PacketQueue *q = detect_tvs[i]->stream_pq;
×
2257
                SCMutexLock(&q->mutex_q);
×
2258
                PacketEnqueue(q, p);
×
2259
                SCCondSignal(&q->cond_q);
×
2260
                SCMutexUnlock(&q->mutex_q);
×
2261
            }
×
2262
        }
×
2263
    }
×
2264
    SCLogDebug("leaving: thread notification count = %d", count);
×
2265
}
×
2266

2267
/** \internal
2268
 *  \brief inject a pseudo packet into each detect thread
2269
 *      -that doesn't use the new det_ctx yet
2270
 *      -*or*, if the thread should flush its output logs.
2271
 */
2272
static void InjectPackets(
2273
        ThreadVars **detect_tvs, DetectEngineThreadCtx **new_det_ctx, int no_of_detect_tvs)
UNCOV
2274
{
×
2275
    /* inject a fake packet if the detect thread that needs it. This function
2276
     * is called if
2277
     *  - A thread isn't using a DE ctx and should
2278
     *  - Or, it should process a pseudo packet and flush its output logs.
2279
     * to speed the process. */
UNCOV
2280
    for (int i = 0; i < no_of_detect_tvs; i++) {
×
UNCOV
2281
        if (SC_ATOMIC_GET(new_det_ctx[i]->so_far_used_by_detect) != 1) {
×
UNCOV
2282
            if (detect_tvs[i]->inq != NULL) {
×
UNCOV
2283
                Packet *p = PacketGetFromAlloc();
×
UNCOV
2284
                if (p != NULL) {
×
UNCOV
2285
                    p->flags |= PKT_PSEUDO_STREAM_END;
×
UNCOV
2286
                    PKT_SET_SRC(p, PKT_SRC_DETECT_RELOAD_FLUSH);
×
UNCOV
2287
                    PacketQueue *q = detect_tvs[i]->inq->pq;
×
UNCOV
2288
                    SCMutexLock(&q->mutex_q);
×
UNCOV
2289
                    PacketEnqueue(q, p);
×
UNCOV
2290
                    SCCondSignal(&q->cond_q);
×
UNCOV
2291
                    SCMutexUnlock(&q->mutex_q);
×
UNCOV
2292
                }
×
UNCOV
2293
            }
×
UNCOV
2294
        }
×
UNCOV
2295
    }
×
UNCOV
2296
}
×
2297

2298
/** \internal
2299
 *  \brief Update detect threads with new detect engine
2300
 *
2301
 *  Atomically update each detect thread with a new thread context
2302
 *  that is associated to the new detection engine(s).
2303
 *
2304
 *  If called in unix socket mode, it's possible that we don't have
2305
 *  detect threads yet.
2306
 *  NOTE: master MUST be locked before calling this
2307
 *
2308
 *  \retval -1 error
2309
 *  \retval 0 no detection threads
2310
 *  \retval 1 successful reload
2311
 */
2312
static int DetectEngineReloadThreads(DetectEngineCtx *new_de_ctx)
2313
{
35,437✔
2314
    SCEnter();
35,437✔
2315
    uint32_t i = 0;
35,437✔
2316

2317
    /* count detect threads in use */
2318
    uint32_t no_of_detect_tvs = TmThreadCountThreadsByTmmFlags(TM_FLAG_FLOWWORKER_TM);
35,437✔
2319
    /* can be zero in unix socket mode */
2320
    if (no_of_detect_tvs == 0) {
35,437✔
2321
        return 0;
35,437✔
2322
    }
35,437✔
2323

2324
    /* prepare swap structures */
UNCOV
2325
    DetectEngineThreadCtx *old_det_ctx[no_of_detect_tvs];
×
UNCOV
2326
    DetectEngineThreadCtx *new_det_ctx[no_of_detect_tvs];
×
UNCOV
2327
    ThreadVars *detect_tvs[no_of_detect_tvs];
×
UNCOV
2328
    memset(old_det_ctx, 0x00, (no_of_detect_tvs * sizeof(DetectEngineThreadCtx *)));
×
UNCOV
2329
    memset(new_det_ctx, 0x00, (no_of_detect_tvs * sizeof(DetectEngineThreadCtx *)));
×
UNCOV
2330
    memset(detect_tvs, 0x00, (no_of_detect_tvs * sizeof(ThreadVars *)));
×
2331

2332
    /* start the process of swapping detect threads ctxs */
2333

2334
    /* get reference to tv's and setup new_det_ctx array */
UNCOV
2335
    SCMutexLock(&tv_root_lock);
×
UNCOV
2336
    for (ThreadVars *tv = tv_root[TVT_PPT]; tv != NULL; tv = tv->next) {
×
UNCOV
2337
        if ((tv->tmm_flags & TM_FLAG_FLOWWORKER_TM) == 0) {
×
UNCOV
2338
            continue;
×
UNCOV
2339
        }
×
UNCOV
2340
        for (TmSlot *s = tv->tm_slots; s != NULL; s = s->slot_next) {
×
UNCOV
2341
            TmModule *tm = TmModuleGetById(s->tm_id);
×
UNCOV
2342
            if (!(tm->flags & TM_FLAG_FLOWWORKER_TM)) {
×
UNCOV
2343
                continue;
×
UNCOV
2344
            }
×
2345

UNCOV
2346
            if (suricata_ctl_flags != 0) {
×
2347
                SCMutexUnlock(&tv_root_lock);
×
2348
                goto error;
×
2349
            }
×
2350

UNCOV
2351
            old_det_ctx[i] = FlowWorkerGetDetectCtxPtr(SC_ATOMIC_GET(s->slot_data));
×
UNCOV
2352
            detect_tvs[i] = tv;
×
2353

UNCOV
2354
            new_det_ctx[i] = DetectEngineThreadCtxInitForReload(tv, new_de_ctx, 1);
×
UNCOV
2355
            if (new_det_ctx[i] == NULL) {
×
2356
                SCLogError("Detect engine thread init "
×
2357
                           "failure in live rule swap.  Let's get out of here");
×
2358
                SCMutexUnlock(&tv_root_lock);
×
2359
                goto error;
×
2360
            }
×
UNCOV
2361
            SCLogDebug("live rule swap created new det_ctx - %p and de_ctx "
×
UNCOV
2362
                       "- %p\n", new_det_ctx[i], new_de_ctx);
×
UNCOV
2363
            i++;
×
UNCOV
2364
            break;
×
UNCOV
2365
        }
×
UNCOV
2366
    }
×
UNCOV
2367
    BUG_ON(i != no_of_detect_tvs);
×
2368

2369
    /* atomically replace the det_ctx data */
UNCOV
2370
    i = 0;
×
UNCOV
2371
    for (ThreadVars *tv = tv_root[TVT_PPT]; tv != NULL; tv = tv->next) {
×
UNCOV
2372
        if ((tv->tmm_flags & TM_FLAG_FLOWWORKER_TM) == 0) {
×
UNCOV
2373
            continue;
×
UNCOV
2374
        }
×
UNCOV
2375
        for (TmSlot *s = tv->tm_slots; s != NULL; s = s->slot_next) {
×
UNCOV
2376
            TmModule *tm = TmModuleGetById(s->tm_id);
×
UNCOV
2377
            if (!(tm->flags & TM_FLAG_FLOWWORKER_TM)) {
×
UNCOV
2378
                continue;
×
UNCOV
2379
            }
×
UNCOV
2380
            SCLogDebug("swapping new det_ctx - %p with older one - %p",
×
UNCOV
2381
                       new_det_ctx[i], SC_ATOMIC_GET(s->slot_data));
×
UNCOV
2382
            FlowWorkerReplaceDetectCtx(SC_ATOMIC_GET(s->slot_data), new_det_ctx[i++]);
×
UNCOV
2383
            break;
×
UNCOV
2384
        }
×
UNCOV
2385
    }
×
UNCOV
2386
    SCMutexUnlock(&tv_root_lock);
×
2387

2388
    /* threads now all have new data, however they may not have started using
2389
     * it and may still use the old data */
2390

UNCOV
2391
    SCLogDebug("Live rule swap has swapped %d old det_ctx's with new ones, "
×
UNCOV
2392
               "along with the new de_ctx", no_of_detect_tvs);
×
2393

UNCOV
2394
    InjectPackets(detect_tvs, new_det_ctx, no_of_detect_tvs);
×
2395

2396
    /* loop waiting for detect threads to switch to the new det_ctx. Try to
2397
     * wake up capture if needed (break loop). */
UNCOV
2398
    uint32_t threads_done = 0;
×
UNCOV
2399
retry:
×
UNCOV
2400
    for (i = 0; i < no_of_detect_tvs; i++) {
×
UNCOV
2401
        if (suricata_ctl_flags != 0) {
×
2402
            threads_done = no_of_detect_tvs;
×
2403
            break;
×
2404
        }
×
UNCOV
2405
        SleepMsec(1);
×
UNCOV
2406
        if (SC_ATOMIC_GET(new_det_ctx[i]->so_far_used_by_detect) == 1) {
×
UNCOV
2407
            SCLogDebug("new_det_ctx - %p used by detect engine", new_det_ctx[i]);
×
UNCOV
2408
            threads_done++;
×
UNCOV
2409
        } else {
×
UNCOV
2410
            TmThreadsCaptureBreakLoop(detect_tvs[i]);
×
UNCOV
2411
        }
×
UNCOV
2412
    }
×
UNCOV
2413
    if (threads_done < no_of_detect_tvs) {
×
UNCOV
2414
        threads_done = 0;
×
UNCOV
2415
        SleepMsec(250);
×
UNCOV
2416
        goto retry;
×
UNCOV
2417
    }
×
2418

2419
    /* this is to make sure that if someone initiated shutdown during a live
2420
     * rule swap, the live rule swap won't clean up the old det_ctx and
2421
     * de_ctx, till all detect threads have stopped working and sitting
2422
     * silently after setting RUNNING_DONE flag and while waiting for
2423
     * THV_DEINIT flag */
UNCOV
2424
    if (i != no_of_detect_tvs) { // not all threads we swapped
×
2425
        for (ThreadVars *tv = tv_root[TVT_PPT]; tv != NULL; tv = tv->next) {
×
2426
            if ((tv->tmm_flags & TM_FLAG_FLOWWORKER_TM) == 0) {
×
2427
                continue;
×
2428
            }
×
2429

2430
            while (!TmThreadsCheckFlag(tv, THV_RUNNING_DONE)) {
×
2431
                SleepUsec(100);
×
2432
            }
×
2433
        }
×
2434
    }
×
2435

2436
    /* free all the ctxs */
UNCOV
2437
    for (i = 0; i < no_of_detect_tvs; i++) {
×
UNCOV
2438
        SCLogDebug("Freeing old_det_ctx - %p used by detect",
×
UNCOV
2439
                   old_det_ctx[i]);
×
UNCOV
2440
        DetectEngineThreadCtxDeinit(NULL, old_det_ctx[i]);
×
UNCOV
2441
    }
×
2442

UNCOV
2443
    SRepReloadComplete();
×
2444

UNCOV
2445
    return 1;
×
2446

2447
 error:
×
2448
    for (i = 0; i < no_of_detect_tvs; i++) {
×
2449
        if (new_det_ctx[i] != NULL)
×
2450
            DetectEngineThreadCtxDeinit(NULL, new_det_ctx[i]);
×
2451
    }
×
2452
    return -1;
×
UNCOV
2453
}
×
2454

2455
bool DetectEngineMpmCachingEnabled(void)
UNCOV
2456
{
×
UNCOV
2457
    int sgh_mpm_caching = 0;
×
UNCOV
2458
    if (SCConfGetBool("detect.sgh-mpm-caching", &sgh_mpm_caching) != 1) {
×
UNCOV
2459
        return false;
×
UNCOV
2460
    }
×
UNCOV
2461
    return (bool)sgh_mpm_caching;
×
UNCOV
2462
}
×
2463

2464
const char *DetectEngineMpmCachingGetPath(void)
UNCOV
2465
{
×
UNCOV
2466
    if (DetectEngineMpmCachingEnabled() == false) {
×
2467
        return NULL;
×
2468
    }
×
2469

UNCOV
2470
    char yamlpath[] = "detect.sgh-mpm-caching-path";
×
UNCOV
2471
    const char *strval = NULL;
×
UNCOV
2472
    if (SCConfGet(yamlpath, &strval) == 1 && strval != NULL) {
×
UNCOV
2473
        return strval;
×
UNCOV
2474
    }
×
2475

2476
    static bool notified = false;
×
2477
    if (!notified) {
×
2478
        SCLogInfo("%s has no path specified, using %s", yamlpath, SGH_CACHE_DIR);
×
2479
        notified = true;
×
2480
    }
×
2481
    return SGH_CACHE_DIR;
×
UNCOV
2482
}
×
2483

2484
void DetectEngineMpmCacheService(uint32_t op_flags)
2485
{
35,438✔
2486
    DetectEngineCtx *de_ctx = DetectEngineGetCurrent();
35,438✔
2487
    if (!de_ctx) {
35,438✔
2488
        return;
×
2489
    }
×
2490

2491
    if (!de_ctx->mpm_cfg || !de_ctx->mpm_cfg->cache_dir_path) {
35,438✔
2492
        goto error;
35,438✔
2493
    }
35,438✔
2494

UNCOV
2495
    if (mpm_table[de_ctx->mpm_matcher].CacheStatsInit != NULL) {
×
UNCOV
2496
        de_ctx->mpm_cfg->cache_stats = mpm_table[de_ctx->mpm_matcher].CacheStatsInit();
×
UNCOV
2497
        if (de_ctx->mpm_cfg->cache_stats == NULL) {
×
2498
            goto error;
×
2499
        }
×
UNCOV
2500
    }
×
2501

UNCOV
2502
    if (op_flags & DETECT_ENGINE_MPM_CACHE_OP_SAVE) {
×
UNCOV
2503
        if (mpm_table[de_ctx->mpm_matcher].CacheRuleset != NULL) {
×
UNCOV
2504
            mpm_table[de_ctx->mpm_matcher].CacheRuleset(de_ctx->mpm_cfg);
×
UNCOV
2505
        }
×
UNCOV
2506
    }
×
2507

UNCOV
2508
    if (op_flags & DETECT_ENGINE_MPM_CACHE_OP_PRUNE) {
×
UNCOV
2509
        if (mpm_table[de_ctx->mpm_matcher].CachePrune != NULL) {
×
UNCOV
2510
            mpm_table[de_ctx->mpm_matcher].CachePrune(de_ctx->mpm_cfg);
×
UNCOV
2511
        }
×
UNCOV
2512
    }
×
2513

UNCOV
2514
    if (mpm_table[de_ctx->mpm_matcher].CacheStatsPrint != NULL) {
×
UNCOV
2515
        mpm_table[de_ctx->mpm_matcher].CacheStatsPrint(de_ctx->mpm_cfg->cache_stats);
×
UNCOV
2516
    }
×
2517

UNCOV
2518
    if (mpm_table[de_ctx->mpm_matcher].CacheStatsDeinit != NULL) {
×
UNCOV
2519
        mpm_table[de_ctx->mpm_matcher].CacheStatsDeinit(de_ctx->mpm_cfg->cache_stats);
×
UNCOV
2520
        de_ctx->mpm_cfg->cache_stats = NULL;
×
UNCOV
2521
    }
×
2522

2523
error:
35,438✔
2524
    DetectEngineDeReference(&de_ctx);
35,438✔
2525
}
35,438✔
2526

2527
static DetectEngineCtx *DetectEngineCtxInitReal(
2528
        enum DetectEngineType type, const char *prefix, uint32_t tenant_id)
2529
{
35,469✔
2530
    DetectEngineCtx *de_ctx = SCCalloc(1, sizeof(DetectEngineCtx));
35,469✔
2531
    if (unlikely(de_ctx == NULL))
35,469✔
2532
        goto error;
×
2533

2534
    memset(&de_ctx->sig_stat, 0, sizeof(SigFileLoaderStat));
35,469✔
2535
    TAILQ_INIT(&de_ctx->sig_stat.failed_sigs);
35,469✔
2536
    de_ctx->sigerror = NULL;
35,469✔
2537
    de_ctx->type = type;
35,469✔
2538
    de_ctx->filemagic_thread_ctx_id = -1;
35,469✔
2539
    de_ctx->tenant_id = tenant_id;
35,469✔
2540

2541
    de_ctx->mpm_matcher = PatternMatchDefaultMatcher();
35,469✔
2542
    de_ctx->spm_matcher = SinglePatternMatchDefaultMatcher();
35,469✔
2543

2544
    if (mpm_table[de_ctx->mpm_matcher].ConfigInit) {
35,469✔
UNCOV
2545
        de_ctx->mpm_cfg = mpm_table[de_ctx->mpm_matcher].ConfigInit();
×
UNCOV
2546
        if (de_ctx->mpm_cfg == NULL) {
×
2547
            goto error;
×
2548
        }
×
2549

UNCOV
2550
        if (DetectEngineMpmCachingEnabled() && mpm_table[de_ctx->mpm_matcher].ConfigCacheDirSet) {
×
UNCOV
2551
            mpm_table[de_ctx->mpm_matcher].ConfigCacheDirSet(
×
UNCOV
2552
                    de_ctx->mpm_cfg, DetectEngineMpmCachingGetPath());
×
2553

UNCOV
2554
            if (mpm_table[de_ctx->mpm_matcher].CachePrune) {
×
UNCOV
2555
                if (SCConfGetTime("detect.sgh-mpm-caching-max-age",
×
UNCOV
2556
                            &de_ctx->mpm_cfg->cache_max_age_seconds) != 1) {
×
UNCOV
2557
                    de_ctx->mpm_cfg->cache_max_age_seconds = 7ULL * 24ULL * 60ULL * 60ULL;
×
UNCOV
2558
                }
×
UNCOV
2559
            }
×
UNCOV
2560
        }
×
UNCOV
2561
    }
×
2562

2563
    if (type == DETECT_ENGINE_TYPE_DD_STUB || type == DETECT_ENGINE_TYPE_MT_STUB) {
35,469✔
UNCOV
2564
        de_ctx->version = DetectEngineGetVersion();
×
UNCOV
2565
        SCLogDebug("stub %u with version %u", type, de_ctx->version);
×
UNCOV
2566
        return de_ctx;
×
UNCOV
2567
    }
×
2568

2569
    if (prefix != NULL) {
35,469✔
UNCOV
2570
        strlcpy(de_ctx->config_prefix, prefix, sizeof(de_ctx->config_prefix));
×
UNCOV
2571
    }
×
2572

2573
    int failure_fatal = 0;
35,469✔
2574
    if (SCConfGetBool("engine.init-failure-fatal", (int *)&failure_fatal) != 1) {
35,469✔
2575
        SCLogDebug("ConfGetBool could not load the value.");
35,469✔
2576
    }
35,469✔
2577
    de_ctx->failure_fatal = (failure_fatal == 1);
35,469✔
2578

2579
    SCLogConfig("pattern matchers: MPM: %s, SPM: %s", mpm_table[de_ctx->mpm_matcher].name,
35,469✔
2580
            spm_table[de_ctx->spm_matcher].name);
35,469✔
2581
    de_ctx->spm_global_thread_ctx = SpmInitGlobalThreadCtx(de_ctx->spm_matcher);
35,469✔
2582
    if (de_ctx->spm_global_thread_ctx == NULL) {
35,469✔
2583
        SCLogDebug("Unable to alloc SpmGlobalThreadCtx.");
×
2584
        goto error;
×
2585
    }
×
2586

2587
    de_ctx->sm_types_prefilter = SCCalloc(DETECT_TBLSIZE, sizeof(bool));
35,469✔
2588
    if (de_ctx->sm_types_prefilter == NULL) {
35,469✔
2589
        goto error;
×
2590
    }
×
2591
    de_ctx->sm_types_silent_error = SCCalloc(DETECT_TBLSIZE, sizeof(bool));
35,469✔
2592
    if (de_ctx->sm_types_silent_error == NULL) {
35,469✔
2593
        goto error;
×
2594
    }
×
2595
    if (DetectEngineCtxLoadConf(de_ctx) == -1) {
35,469✔
2596
        goto error;
×
2597
    }
×
2598

2599
    SigGroupHeadHashInit(de_ctx);
35,469✔
2600
    MpmStoreInit(de_ctx);
35,469✔
2601
    DetectParseDupSigHashInit(de_ctx);
35,469✔
2602
    DetectAddressMapInit(de_ctx);
35,469✔
2603
    DetectMetadataHashInit(de_ctx);
35,469✔
2604
    DetectBufferTypeSetupDetectEngine(de_ctx);
35,469✔
2605
    DetectEngineInitializeFastPatternList(de_ctx);
35,469✔
2606

2607
    /* init iprep... ignore errors for now */
2608
    (void)SRepInit(de_ctx);
35,469✔
2609

2610
    SCClassSCConfInit(de_ctx);
35,469✔
2611
    if (!SCClassConfLoadClassificationConfigFile(de_ctx, NULL)) {
35,469✔
2612
        if (SCRunmodeGet() == RUNMODE_CONF_TEST)
35,469✔
UNCOV
2613
            goto error;
×
2614
    }
35,469✔
2615

2616
    if (ActionInitConfig() < 0) {
35,469✔
2617
        goto error;
×
2618
    }
×
2619
    SCReferenceSCConfInit(de_ctx);
35,469✔
2620
    if (SCRConfLoadReferenceConfigFile(de_ctx, NULL) < 0) {
35,469✔
2621
        if (SCRunmodeGet() == RUNMODE_CONF_TEST)
35,469✔
UNCOV
2622
            goto error;
×
2623
    }
35,469✔
2624

2625
    de_ctx->version = DetectEngineGetVersion();
35,469✔
2626
    SCLogDebug("dectx with version %u", de_ctx->version);
35,469✔
2627
    return de_ctx;
35,469✔
UNCOV
2628
error:
×
UNCOV
2629
    if (de_ctx != NULL) {
×
UNCOV
2630
        DetectEngineCtxFree(de_ctx);
×
UNCOV
2631
    }
×
UNCOV
2632
    return NULL;
×
2633
}
35,469✔
2634

2635
DetectEngineCtx *DetectEngineCtxInitStubForMT(void)
UNCOV
2636
{
×
UNCOV
2637
    return DetectEngineCtxInitReal(DETECT_ENGINE_TYPE_MT_STUB, NULL, 0);
×
UNCOV
2638
}
×
2639

2640
DetectEngineCtx *DetectEngineCtxInitStubForDD(void)
2641
{
×
2642
    return DetectEngineCtxInitReal(DETECT_ENGINE_TYPE_DD_STUB, NULL, 0);
×
2643
}
×
2644

2645
DetectEngineCtx *DetectEngineCtxInit(void)
2646
{
35,469✔
2647
    return DetectEngineCtxInitReal(DETECT_ENGINE_TYPE_NORMAL, NULL, 0);
35,469✔
2648
}
35,469✔
2649

2650
DetectEngineCtx *DetectEngineCtxInitWithPrefix(const char *prefix, uint32_t tenant_id)
2651
{
35,437✔
2652
    if (prefix == NULL || strlen(prefix) == 0)
35,437✔
2653
        return DetectEngineCtxInit();
35,437✔
UNCOV
2654
    else
×
UNCOV
2655
        return DetectEngineCtxInitReal(DETECT_ENGINE_TYPE_NORMAL, prefix, tenant_id);
×
2656
}
35,437✔
2657

2658
static void DetectEngineCtxFreeThreadKeywordData(DetectEngineCtx *de_ctx)
2659
{
35,466✔
2660
    HashListTableFree(de_ctx->keyword_hash);
35,466✔
2661
}
35,466✔
2662

2663
static void DetectEngineCtxFreeFailedSigs(DetectEngineCtx *de_ctx)
2664
{
35,466✔
2665
    SigString *item = NULL;
35,466✔
2666
    SigString *sitem;
35,466✔
2667

2668
    TAILQ_FOREACH_SAFE(item, &de_ctx->sig_stat.failed_sigs, next, sitem) {
4,829,256✔
2669
        SCFree(item->filename);
4,829,256✔
2670
        SCFree(item->sig_str);
4,829,256✔
2671
        if (item->sig_error) {
4,829,256✔
2672
            SCFree(item->sig_error);
12,215✔
2673
        }
12,215✔
2674
        TAILQ_REMOVE(&de_ctx->sig_stat.failed_sigs, item, next);
4,829,256✔
2675
        SCFree(item);
4,829,256✔
2676
    }
4,829,256✔
2677
}
35,466✔
2678

2679
/**
2680
 * \brief Free a DetectEngineCtx::
2681
 *
2682
 * \param de_ctx DetectEngineCtx:: to be freed
2683
 */
2684
void DetectEngineCtxFree(DetectEngineCtx *de_ctx)
2685
{
35,466✔
2686

2687
    if (de_ctx == NULL)
35,466✔
2688
        return;
×
2689

2690
#ifdef PROFILE_RULES
2691
    if (de_ctx->profile_ctx != NULL) {
2692
        SCProfilingRuleDestroyCtx(de_ctx->profile_ctx);
2693
        de_ctx->profile_ctx = NULL;
2694
    }
2695
#endif
2696
#ifdef PROFILING
2697
    if (de_ctx->profile_keyword_ctx != NULL) {
2698
        SCProfilingKeywordDestroyCtx(de_ctx);//->profile_keyword_ctx);
2699
//        de_ctx->profile_keyword_ctx = NULL;
2700
    }
2701
    if (de_ctx->profile_sgh_ctx != NULL) {
2702
        SCProfilingSghDestroyCtx(de_ctx);
2703
    }
2704
    SCProfilingPrefilterDestroyCtx(de_ctx);
2705
#endif
2706

2707
    if (mpm_table[de_ctx->mpm_matcher].ConfigDeinit) {
35,466✔
UNCOV
2708
        mpm_table[de_ctx->mpm_matcher].ConfigDeinit(&de_ctx->mpm_cfg);
×
UNCOV
2709
    }
×
2710
    /* Normally the hashes are freed elsewhere, but
2711
     * to be sure look at them again here.
2712
     */
2713
    SigGroupHeadHashFree(de_ctx);
35,466✔
2714
    MpmStoreFree(de_ctx);
35,466✔
2715
    DetectParseDupSigHashFree(de_ctx);
35,466✔
2716
    SCSigSignatureOrderingModuleCleanup(de_ctx);
35,466✔
2717
    SigCleanSignatures(de_ctx);
35,466✔
2718
    if (de_ctx->sig_array)
35,466✔
2719
        SCFree(de_ctx->sig_array);
35,436✔
2720

2721
    if (de_ctx->filedata_config)
35,466✔
2722
        SCFree(de_ctx->filedata_config);
2,814✔
2723

2724
    DetectEngineFreeFastPatternList(de_ctx);
35,466✔
2725
    SCClassConfDeInitContext(de_ctx);
35,466✔
2726
    SCRConfDeInitContext(de_ctx);
35,466✔
2727

2728
    SigGroupCleanup(de_ctx);
35,466✔
2729

2730
    SpmDestroyGlobalThreadCtx(de_ctx->spm_global_thread_ctx);
35,466✔
2731
    SCFree(de_ctx->sm_types_prefilter);
35,466✔
2732
    SCFree(de_ctx->sm_types_silent_error);
35,466✔
2733

2734
    MpmFactoryDeRegisterAllMpmCtxProfiles(de_ctx);
35,466✔
2735

2736
    DetectEngineCtxFreeThreadKeywordData(de_ctx);
35,466✔
2737
    SRepDestroy(de_ctx);
35,466✔
2738
    DetectEngineCtxFreeFailedSigs(de_ctx);
35,466✔
2739

2740
    DetectAddressMapFree(de_ctx);
35,466✔
2741
    DetectMetadataHashFree(de_ctx);
35,466✔
2742

2743
    /* if we have a config prefix, remove the config from the tree */
2744
    if (strlen(de_ctx->config_prefix) > 0) {
35,466✔
2745
        /* remove config */
UNCOV
2746
        SCConfNode *node = SCConfGetNode(de_ctx->config_prefix);
×
UNCOV
2747
        if (node != NULL) {
×
UNCOV
2748
            SCConfNodeRemove(node); /* frees node */
×
UNCOV
2749
        }
×
2750
#if 0
2751
        SCConfDump();
2752
#endif
UNCOV
2753
    }
×
2754

2755
    DetectPortCleanupList(de_ctx, de_ctx->tcp_priorityports);
35,466✔
2756
    DetectPortCleanupList(de_ctx, de_ctx->udp_priorityports);
35,466✔
2757

2758
    DetectBufferTypeFreeDetectEngine(de_ctx);
35,466✔
2759
    SCClassConfDeinit(de_ctx);
35,466✔
2760
    SCReferenceConfDeinit(de_ctx);
35,466✔
2761

2762
    if (de_ctx->tenant_path) {
35,466✔
UNCOV
2763
        SCFree(de_ctx->tenant_path);
×
UNCOV
2764
    }
×
2765

2766
    if (de_ctx->requirements) {
35,466✔
2767
        SCDetectRequiresStatusFree(de_ctx->requirements);
721✔
2768
    }
721✔
2769

2770
    if (de_ctx->non_pf_engine_names) {
35,466✔
2771
        HashTableFree(de_ctx->non_pf_engine_names);
19,747✔
2772
    }
19,747✔
2773
    SCFree(de_ctx);
35,466✔
2774
    //DetectAddressGroupPrintMemory();
2775
    //DetectSigGroupPrintMemory();
2776
    //DetectPortPrintMemory();
2777
}
35,466✔
2778

2779
/** \brief  Function that load DetectEngineCtx config for grouping sigs
2780
 *          used by the engine
2781
 *  \retval 0 if no config provided, 1 if config was provided
2782
 *          and loaded successfully
2783
 */
2784
static int DetectEngineCtxLoadConf(DetectEngineCtx *de_ctx)
2785
{
35,469✔
2786
    uint8_t profile = ENGINE_PROFILE_MEDIUM;
35,469✔
2787
    const char *max_uniq_toclient_groups_str = NULL;
35,469✔
2788
    const char *max_uniq_toserver_groups_str = NULL;
35,469✔
2789
    const char *sgh_mpm_context = NULL;
35,469✔
2790
    const char *de_ctx_profile = NULL;
35,469✔
2791

2792
    (void)SCConfGet("detect.profile", &de_ctx_profile);
35,469✔
2793
    (void)SCConfGet("detect.sgh-mpm-context", &sgh_mpm_context);
35,469✔
2794

2795
    SCConfNode *de_ctx_custom = SCConfGetNode("detect-engine");
35,469✔
2796
    SCConfNode *opt = NULL;
35,469✔
2797

2798
    if (de_ctx_custom != NULL) {
35,469✔
UNCOV
2799
        TAILQ_FOREACH(opt, &de_ctx_custom->head, next) {
×
UNCOV
2800
            if (de_ctx_profile == NULL) {
×
UNCOV
2801
                if (opt->val && strcmp(opt->val, "profile") == 0) {
×
UNCOV
2802
                    de_ctx_profile = opt->head.tqh_first->val;
×
UNCOV
2803
                }
×
UNCOV
2804
            }
×
2805

UNCOV
2806
            if (sgh_mpm_context == NULL) {
×
UNCOV
2807
                if (opt->val && strcmp(opt->val, "sgh-mpm-context") == 0) {
×
2808
                    sgh_mpm_context = opt->head.tqh_first->val;
×
2809
                }
×
UNCOV
2810
            }
×
UNCOV
2811
        }
×
UNCOV
2812
    }
×
2813

2814
    if (de_ctx_profile != NULL) {
35,469✔
UNCOV
2815
        if (strcmp(de_ctx_profile, "low") == 0 ||
×
UNCOV
2816
            strcmp(de_ctx_profile, "lowest") == 0) {        // legacy
×
2817
            profile = ENGINE_PROFILE_LOW;
×
UNCOV
2818
        } else if (strcmp(de_ctx_profile, "medium") == 0) {
×
UNCOV
2819
            profile = ENGINE_PROFILE_MEDIUM;
×
UNCOV
2820
        } else if (strcmp(de_ctx_profile, "high") == 0 ||
×
UNCOV
2821
                   strcmp(de_ctx_profile, "highest") == 0) { // legacy
×
2822
            profile = ENGINE_PROFILE_HIGH;
×
UNCOV
2823
        } else if (strcmp(de_ctx_profile, "custom") == 0) {
×
UNCOV
2824
            profile = ENGINE_PROFILE_CUSTOM;
×
UNCOV
2825
        } else {
×
2826
            SCLogError("invalid value for detect.profile: '%s'. "
×
2827
                       "Valid options: low, medium, high and custom.",
×
2828
                    de_ctx_profile);
×
2829
            return -1;
×
2830
        }
×
2831

UNCOV
2832
        SCLogDebug("Profile for detection engine groups is \"%s\"", de_ctx_profile);
×
2833
    } else {
35,469✔
2834
        SCLogDebug("Profile for detection engine groups not provided "
35,469✔
2835
                   "at suricata.yaml. Using default (\"medium\").");
35,469✔
2836
    }
35,469✔
2837

2838
    /* detect-engine.sgh-mpm-context option parsing */
2839
    if (sgh_mpm_context == NULL || strcmp(sgh_mpm_context, "auto") == 0) {
35,469✔
2840
        /* for now, since we still haven't implemented any intelligence into
2841
         * understanding the patterns and distributing mpm_ctx across sgh */
2842
        if (de_ctx->mpm_matcher == MPM_AC || de_ctx->mpm_matcher == MPM_AC_KS ||
35,469✔
2843
                de_ctx->mpm_matcher == MPM_HS) {
35,469✔
2844
            de_ctx->sgh_mpm_ctx_cnf = ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE;
35,469✔
2845
        } else {
35,469✔
2846
            de_ctx->sgh_mpm_ctx_cnf = ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL;
×
2847
        }
×
2848
    } else {
35,469✔
UNCOV
2849
        if (strcmp(sgh_mpm_context, "single") == 0) {
×
2850
            de_ctx->sgh_mpm_ctx_cnf = ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE;
×
UNCOV
2851
        } else if (strcmp(sgh_mpm_context, "full") == 0) {
×
2852
            de_ctx->sgh_mpm_ctx_cnf = ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL;
×
UNCOV
2853
        } else {
×
UNCOV
2854
            SCLogError("You have supplied an "
×
UNCOV
2855
                       "invalid conf value for detect-engine.sgh-mpm-context-"
×
UNCOV
2856
                       "%s",
×
UNCOV
2857
                    sgh_mpm_context);
×
UNCOV
2858
            exit(EXIT_FAILURE);
×
UNCOV
2859
        }
×
UNCOV
2860
    }
×
2861

2862
    if (RunmodeIsUnittests()) {
35,469✔
UNCOV
2863
        de_ctx->sgh_mpm_ctx_cnf = ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL;
×
UNCOV
2864
    }
×
2865

2866
    /* parse profile custom-values */
2867
    opt = NULL;
35,469✔
2868
    switch (profile) {
35,469✔
2869
        case ENGINE_PROFILE_LOW:
×
2870
            de_ctx->max_uniq_toclient_groups = 15;
×
2871
            de_ctx->max_uniq_toserver_groups = 25;
×
2872
            break;
×
2873

2874
        case ENGINE_PROFILE_HIGH:
×
2875
            de_ctx->max_uniq_toclient_groups = 75;
×
2876
            de_ctx->max_uniq_toserver_groups = 75;
×
2877
            break;
×
2878

UNCOV
2879
        case ENGINE_PROFILE_CUSTOM:
×
UNCOV
2880
            (void)SCConfGet("detect.custom-values.toclient-groups", &max_uniq_toclient_groups_str);
×
UNCOV
2881
            (void)SCConfGet("detect.custom-values.toserver-groups", &max_uniq_toserver_groups_str);
×
2882

UNCOV
2883
            if (de_ctx_custom != NULL) {
×
UNCOV
2884
                TAILQ_FOREACH(opt, &de_ctx_custom->head, next) {
×
UNCOV
2885
                    if (opt->val && strcmp(opt->val, "custom-values") == 0) {
×
UNCOV
2886
                        if (max_uniq_toclient_groups_str == NULL) {
×
UNCOV
2887
                            max_uniq_toclient_groups_str = (char *)SCConfNodeLookupChildValue(
×
UNCOV
2888
                                    opt->head.tqh_first, "toclient-sp-groups");
×
UNCOV
2889
                        }
×
UNCOV
2890
                        if (max_uniq_toclient_groups_str == NULL) {
×
UNCOV
2891
                            max_uniq_toclient_groups_str = (char *)SCConfNodeLookupChildValue(
×
UNCOV
2892
                                    opt->head.tqh_first, "toclient-groups");
×
UNCOV
2893
                        }
×
UNCOV
2894
                        if (max_uniq_toserver_groups_str == NULL) {
×
UNCOV
2895
                            max_uniq_toserver_groups_str = (char *)SCConfNodeLookupChildValue(
×
UNCOV
2896
                                    opt->head.tqh_first, "toserver-dp-groups");
×
UNCOV
2897
                        }
×
UNCOV
2898
                        if (max_uniq_toserver_groups_str == NULL) {
×
UNCOV
2899
                            max_uniq_toserver_groups_str = (char *)SCConfNodeLookupChildValue(
×
UNCOV
2900
                                    opt->head.tqh_first, "toserver-groups");
×
UNCOV
2901
                        }
×
UNCOV
2902
                    }
×
UNCOV
2903
                }
×
UNCOV
2904
            }
×
UNCOV
2905
            if (max_uniq_toclient_groups_str != NULL) {
×
UNCOV
2906
                if (StringParseUint16(&de_ctx->max_uniq_toclient_groups, 10,
×
UNCOV
2907
                            (uint16_t)strlen(max_uniq_toclient_groups_str),
×
UNCOV
2908
                            (const char *)max_uniq_toclient_groups_str) <= 0) {
×
UNCOV
2909
                    de_ctx->max_uniq_toclient_groups = 20;
×
2910

UNCOV
2911
                    SCLogWarning("parsing '%s' for "
×
UNCOV
2912
                                 "toclient-groups failed, using %u",
×
UNCOV
2913
                            max_uniq_toclient_groups_str, de_ctx->max_uniq_toclient_groups);
×
UNCOV
2914
                }
×
UNCOV
2915
            } else {
×
UNCOV
2916
                de_ctx->max_uniq_toclient_groups = 20;
×
UNCOV
2917
            }
×
UNCOV
2918
            SCLogConfig("toclient-groups %u", de_ctx->max_uniq_toclient_groups);
×
2919

UNCOV
2920
            if (max_uniq_toserver_groups_str != NULL) {
×
UNCOV
2921
                if (StringParseUint16(&de_ctx->max_uniq_toserver_groups, 10,
×
UNCOV
2922
                            (uint16_t)strlen(max_uniq_toserver_groups_str),
×
UNCOV
2923
                            (const char *)max_uniq_toserver_groups_str) <= 0) {
×
UNCOV
2924
                    de_ctx->max_uniq_toserver_groups = 40;
×
2925

UNCOV
2926
                    SCLogWarning("parsing '%s' for "
×
UNCOV
2927
                                 "toserver-groups failed, using %u",
×
UNCOV
2928
                            max_uniq_toserver_groups_str, de_ctx->max_uniq_toserver_groups);
×
UNCOV
2929
                }
×
UNCOV
2930
            } else {
×
2931
                de_ctx->max_uniq_toserver_groups = 40;
×
2932
            }
×
UNCOV
2933
            SCLogConfig("toserver-groups %u", de_ctx->max_uniq_toserver_groups);
×
UNCOV
2934
            break;
×
2935

2936
        /* Default (or no config provided) is profile medium */
2937
        case ENGINE_PROFILE_MEDIUM:
35,469✔
2938
        case ENGINE_PROFILE_UNKNOWN:
35,469✔
2939
        default:
35,469✔
2940
            de_ctx->max_uniq_toclient_groups = 20;
35,469✔
2941
            de_ctx->max_uniq_toserver_groups = 40;
35,469✔
2942
            break;
35,469✔
2943
    }
35,469✔
2944

2945
    intmax_t value = 0;
35,469✔
2946
    de_ctx->inspection_recursion_limit = DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT;
35,469✔
2947
    if (SCConfGetInt("detect.inspection-recursion-limit", &value) == 1) {
35,469✔
2948
        if (value >= 0 && value <= INT_MAX) {
35,438✔
2949
            de_ctx->inspection_recursion_limit = (int)value;
35,438✔
2950
        }
35,438✔
2951

2952
    /* fall back to old config parsing */
2953
    } else {
35,438✔
2954
        SCConfNode *insp_recursion_limit_node = NULL;
31✔
2955
        char *insp_recursion_limit = NULL;
31✔
2956

2957
        if (de_ctx_custom != NULL) {
31✔
UNCOV
2958
            opt = NULL;
×
UNCOV
2959
            TAILQ_FOREACH(opt, &de_ctx_custom->head, next) {
×
UNCOV
2960
                if (opt->val && strcmp(opt->val, "inspection-recursion-limit") != 0)
×
UNCOV
2961
                    continue;
×
2962

UNCOV
2963
                insp_recursion_limit_node = SCConfNodeLookupChild(opt, opt->val);
×
UNCOV
2964
                if (insp_recursion_limit_node == NULL) {
×
2965
                    SCLogError("Error retrieving conf "
×
2966
                               "entry for detect-engine:inspection-recursion-limit");
×
2967
                    break;
×
2968
                }
×
UNCOV
2969
                insp_recursion_limit = insp_recursion_limit_node->val;
×
UNCOV
2970
                SCLogDebug("Found detect-engine.inspection-recursion-limit - %s:%s",
×
UNCOV
2971
                        insp_recursion_limit_node->name, insp_recursion_limit_node->val);
×
UNCOV
2972
                break;
×
UNCOV
2973
            }
×
2974

UNCOV
2975
            if (insp_recursion_limit != NULL) {
×
UNCOV
2976
                if (StringParseInt32(&de_ctx->inspection_recursion_limit, 10,
×
UNCOV
2977
                                     0, (const char *)insp_recursion_limit) < 0) {
×
2978
                    SCLogWarning("Invalid value for "
×
2979
                                 "detect-engine.inspection-recursion-limit: %s "
×
2980
                                 "resetting to %d",
×
2981
                            insp_recursion_limit, DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT);
×
2982
                    de_ctx->inspection_recursion_limit =
×
2983
                        DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT;
×
2984
                }
×
UNCOV
2985
            }
×
UNCOV
2986
        }
×
2987
    }
31✔
2988

2989
    if (de_ctx->inspection_recursion_limit == 0)
35,469✔
2990
        de_ctx->inspection_recursion_limit = -1;
35,438✔
2991

2992
    SCLogDebug("de_ctx->inspection_recursion_limit: %d",
35,469✔
2993
               de_ctx->inspection_recursion_limit);
35,469✔
2994

2995
    // default value is 4
2996
    de_ctx->guess_applayer_log_limit = 4;
35,469✔
2997
    if (SCConfGetInt("detect.stream-tx-log-limit", &value) == 1) {
35,469✔
UNCOV
2998
        if (value >= 0 && value <= UINT8_MAX) {
×
UNCOV
2999
            de_ctx->guess_applayer_log_limit = (uint8_t)value;
×
UNCOV
3000
        } else {
×
3001
            SCLogWarning("Invalid value for detect-engine.stream-tx-log-limit: must be between 0 "
×
3002
                         "and 255, will default to 4");
×
3003
        }
×
UNCOV
3004
    }
×
3005
    int guess_applayer = 0;
35,469✔
3006
    if ((SCConfGetBool("detect.guess-applayer-tx", &guess_applayer)) == 1) {
35,469✔
UNCOV
3007
        if (guess_applayer == 1) {
×
UNCOV
3008
            de_ctx->guess_applayer = true;
×
UNCOV
3009
        }
×
UNCOV
3010
    }
×
3011

3012
    /* parse port grouping priority settings */
3013

3014
    const char *ports = NULL;
35,469✔
3015
    (void)SCConfGet("detect.grouping.tcp-priority-ports", &ports);
35,469✔
3016
    if (ports) {
35,469✔
3017
        SCLogConfig("grouping: tcp-priority-ports %s", ports);
×
3018
    } else {
35,469✔
3019
        (void)SCConfGet("detect.grouping.tcp-whitelist", &ports);
35,469✔
3020
        if (ports) {
35,469✔
3021
            SCLogConfig(
×
3022
                    "grouping: tcp-priority-ports from legacy 'tcp-whitelist' setting: %s", ports);
×
3023
        } else {
35,469✔
3024
            ports = "53, 80, 139, 443, 445, 1433, 3306, 3389, 6666, 6667, 8080";
35,469✔
3025
            SCLogConfig("grouping: tcp-priority-ports (default) %s", ports);
35,469✔
3026
        }
35,469✔
3027
    }
35,469✔
3028
    if (DetectPortParse(de_ctx, &de_ctx->tcp_priorityports, ports) != 0) {
35,469✔
3029
        SCLogWarning("'%s' is not a valid value "
×
3030
                     "for detect.grouping.tcp-priority-ports",
×
3031
                ports);
×
3032
    }
×
3033
    DetectPort *x = de_ctx->tcp_priorityports;
35,469✔
3034
    for ( ; x != NULL;  x = x->next) {
425,628✔
3035
        if (x->port != x->port2) {
390,159✔
3036
            SCLogWarning("'%s' is not a valid value "
×
3037
                         "for detect.grouping.tcp-priority-ports: only single ports allowed",
×
3038
                    ports);
×
3039
            DetectPortCleanupList(de_ctx, de_ctx->tcp_priorityports);
×
3040
            de_ctx->tcp_priorityports = NULL;
×
3041
            break;
×
3042
        }
×
3043
    }
390,159✔
3044

3045
    ports = NULL;
35,469✔
3046
    (void)SCConfGet("detect.grouping.udp-priority-ports", &ports);
35,469✔
3047
    if (ports) {
35,469✔
3048
        SCLogConfig("grouping: udp-priority-ports %s", ports);
×
3049
    } else {
35,469✔
3050
        (void)SCConfGet("detect.grouping.udp-whitelist", &ports);
35,469✔
3051
        if (ports) {
35,469✔
3052
            SCLogConfig(
×
3053
                    "grouping: udp-priority-ports from legacy 'udp-whitelist' setting: %s", ports);
×
3054
        } else {
35,469✔
3055
            ports = "53, 135, 5060";
35,469✔
3056
            SCLogConfig("grouping: udp-priority-ports (default) %s", ports);
35,469✔
3057
        }
35,469✔
3058
    }
35,469✔
3059
    if (DetectPortParse(de_ctx, &de_ctx->udp_priorityports, ports) != 0) {
35,469✔
3060
        SCLogWarning("'%s' is not a valid value "
×
3061
                     "for detect.grouping.udp-priority-ports",
×
3062
                ports);
×
3063
    }
×
3064
    for (x = de_ctx->udp_priorityports; x != NULL; x = x->next) {
141,876✔
3065
        if (x->port != x->port2) {
106,407✔
3066
            SCLogWarning("'%s' is not a valid value "
×
3067
                         "for detect.grouping.udp-priority-ports: only single ports allowed",
×
3068
                    ports);
×
3069
            DetectPortCleanupList(de_ctx, de_ctx->udp_priorityports);
×
3070
            de_ctx->udp_priorityports = NULL;
×
3071
            break;
×
3072
        }
×
3073
    }
106,407✔
3074

3075
    de_ctx->prefilter_setting = DETECT_PREFILTER_MPM;
35,469✔
3076
    const char *pf_setting = NULL;
35,469✔
3077
    if (SCConfGet("detect.prefilter.default", &pf_setting) == 1 && pf_setting) {
35,469✔
UNCOV
3078
        if (strcasecmp(pf_setting, "mpm") == 0) {
×
UNCOV
3079
            de_ctx->prefilter_setting = DETECT_PREFILTER_MPM;
×
UNCOV
3080
        } else if (strcasecmp(pf_setting, "auto") == 0) {
×
UNCOV
3081
            de_ctx->prefilter_setting = DETECT_PREFILTER_AUTO;
×
UNCOV
3082
        }
×
UNCOV
3083
    }
×
3084
    switch (de_ctx->prefilter_setting) {
35,469✔
3085
        case DETECT_PREFILTER_MPM:
35,469✔
3086
            SCLogConfig("prefilter engines: MPM");
35,469✔
3087
            break;
35,469✔
UNCOV
3088
        case DETECT_PREFILTER_AUTO:
×
UNCOV
3089
            SCLogConfig("prefilter engines: MPM and keywords");
×
UNCOV
3090
            break;
×
3091
    }
35,469✔
3092

3093
    return 0;
35,469✔
3094
}
35,469✔
3095

3096
void DetectEngineResetMaxSigId(DetectEngineCtx *de_ctx)
3097
{
35,466✔
3098
    de_ctx->signum = 0;
35,466✔
3099
}
35,466✔
3100

3101
static int DetectEngineThreadCtxInitGlobalKeywords(DetectEngineThreadCtx *det_ctx)
3102
{
35,438✔
3103
    const DetectEngineMasterCtx *master = &g_master_de_ctx;
35,438✔
3104

3105
    if (master->keyword_id > 0) {
35,438✔
3106
        // coverity[suspicious_sizeof : FALSE]
3107
        det_ctx->global_keyword_ctxs_array = (void **)SCCalloc(master->keyword_id, sizeof(void *));
35,438✔
3108
        if (det_ctx->global_keyword_ctxs_array == NULL) {
35,438✔
3109
            SCLogError("setting up thread local detect ctx");
×
3110
            return TM_ECODE_FAILED;
×
3111
        }
×
3112
        det_ctx->global_keyword_ctxs_size = master->keyword_id;
35,438✔
3113

3114
        const DetectEngineThreadKeywordCtxItem *item = master->keyword_list;
35,438✔
3115
        while (item) {
850,512✔
3116
            det_ctx->global_keyword_ctxs_array[item->id] = item->InitFunc(item->data);
815,074✔
3117
            if (det_ctx->global_keyword_ctxs_array[item->id] == NULL) {
815,074✔
3118
                SCLogError("setting up thread local detect ctx "
×
3119
                           "for keyword \"%s\" failed",
×
3120
                        item->name);
×
3121
                return TM_ECODE_FAILED;
×
3122
            }
×
3123
            item = item->next;
815,074✔
3124
        }
815,074✔
3125
    }
35,438✔
3126
    return TM_ECODE_OK;
35,438✔
3127
}
35,438✔
3128

3129
static void DetectEngineThreadCtxDeinitGlobalKeywords(DetectEngineThreadCtx *det_ctx)
3130
{
35,437✔
3131
    if (det_ctx->global_keyword_ctxs_array == NULL ||
35,437✔
3132
        det_ctx->global_keyword_ctxs_size == 0) {
35,437✔
UNCOV
3133
        return;
×
UNCOV
3134
    }
×
3135

3136
    const DetectEngineMasterCtx *master = &g_master_de_ctx;
35,437✔
3137
    if (master->keyword_id > 0) {
35,437✔
3138
        const DetectEngineThreadKeywordCtxItem *item = master->keyword_list;
35,437✔
3139
        while (item) {
850,488✔
3140
            if (det_ctx->global_keyword_ctxs_array[item->id] != NULL)
815,051✔
3141
                item->FreeFunc(det_ctx->global_keyword_ctxs_array[item->id]);
815,051✔
3142

3143
            item = item->next;
815,051✔
3144
        }
815,051✔
3145
        det_ctx->global_keyword_ctxs_size = 0;
35,437✔
3146
        SCFree(det_ctx->global_keyword_ctxs_array);
35,437✔
3147
        det_ctx->global_keyword_ctxs_array = NULL;
35,437✔
3148
    }
35,437✔
3149
}
35,437✔
3150

3151
static int DetectEngineThreadCtxInitKeywords(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
3152
{
35,438✔
3153
    if (de_ctx->keyword_id > 0) {
35,438✔
3154
        // coverity[suspicious_sizeof : FALSE]
3155
        det_ctx->keyword_ctxs_array = SCCalloc(de_ctx->keyword_id, sizeof(void *));
6,748✔
3156
        if (det_ctx->keyword_ctxs_array == NULL) {
6,748✔
3157
            SCLogError("setting up thread local detect ctx");
×
3158
            return TM_ECODE_FAILED;
×
3159
        }
×
3160

3161
        det_ctx->keyword_ctxs_size = de_ctx->keyword_id;
6,748✔
3162

3163
        HashListTableBucket *hb = HashListTableGetListHead(de_ctx->keyword_hash);
6,748✔
3164
        for (; hb != NULL; hb = HashListTableGetListNext(hb)) {
25,328✔
3165
            DetectEngineThreadKeywordCtxItem *item = HashListTableGetListData(hb);
18,580✔
3166

3167
            det_ctx->keyword_ctxs_array[item->id] = item->InitFunc(item->data);
18,580✔
3168
            if (det_ctx->keyword_ctxs_array[item->id] == NULL) {
18,580✔
3169
                SCLogError("setting up thread local detect ctx "
×
3170
                           "for keyword \"%s\" failed",
×
3171
                        item->name);
×
3172
                return TM_ECODE_FAILED;
×
3173
            }
×
3174
        }
18,580✔
3175
    }
6,748✔
3176
    return TM_ECODE_OK;
35,438✔
3177
}
35,438✔
3178

3179
static void DetectEngineThreadCtxDeinitKeywords(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
3180
{
35,437✔
3181
    if (de_ctx->keyword_id > 0) {
35,437✔
3182
        HashListTableBucket *hb = HashListTableGetListHead(de_ctx->keyword_hash);
6,748✔
3183
        for (; hb != NULL; hb = HashListTableGetListNext(hb)) {
25,328✔
3184
            DetectEngineThreadKeywordCtxItem *item = HashListTableGetListData(hb);
18,580✔
3185

3186
            if (det_ctx->keyword_ctxs_array[item->id] != NULL)
18,580✔
3187
                item->FreeFunc(det_ctx->keyword_ctxs_array[item->id]);
18,580✔
3188
        }
18,580✔
3189
        det_ctx->keyword_ctxs_size = 0;
6,748✔
3190
        SCFree(det_ctx->keyword_ctxs_array);
6,748✔
3191
        det_ctx->keyword_ctxs_array = NULL;
6,748✔
3192
    }
6,748✔
3193
}
35,437✔
3194

3195
/** NOTE: master MUST be locked before calling this */
3196
static TmEcode DetectEngineThreadCtxInitForMT(ThreadVars *tv, DetectEngineThreadCtx *det_ctx)
UNCOV
3197
{
×
UNCOV
3198
    DetectEngineMasterCtx *master = &g_master_de_ctx;
×
3199

UNCOV
3200
    DetectEngineTenantMapping *map_array = NULL;
×
UNCOV
3201
    uint32_t map_array_size = 0;
×
UNCOV
3202
    uint32_t map_cnt = 0;
×
UNCOV
3203
    uint32_t max_tenant_id = 0;
×
UNCOV
3204
    DetectEngineCtx *list = master->list;
×
3205

UNCOV
3206
    DEBUG_VALIDATE_BUG_ON(!SCMutexIsLocked(&master->lock));
×
3207

3208
    /* coverity[missing_lock] */
UNCOV
3209
    if (master->tenant_selector == TENANT_SELECTOR_UNKNOWN) {
×
3210
        SCLogError("no tenant selector set: "
×
3211
                   "set using multi-detect.selector");
×
3212
        return TM_ECODE_FAILED;
×
3213
    }
×
3214

UNCOV
3215
    uint32_t tcnt = 0;
×
UNCOV
3216
    while (list) {
×
UNCOV
3217
        if (list->tenant_id > max_tenant_id)
×
UNCOV
3218
            max_tenant_id = list->tenant_id;
×
3219

UNCOV
3220
        list = list->next;
×
UNCOV
3221
        tcnt++;
×
UNCOV
3222
    }
×
3223

UNCOV
3224
    HashTable *mt_det_ctxs_hash =
×
UNCOV
3225
            HashTableInit(tcnt * 2, TenantIdHash, TenantIdCompare, TenantIdFree);
×
UNCOV
3226
    if (mt_det_ctxs_hash == NULL) {
×
3227
        goto error;
×
3228
    }
×
3229

UNCOV
3230
    if (tcnt == 0) {
×
3231
        SCLogInfo("no tenants left, or none registered yet");
×
UNCOV
3232
    } else {
×
UNCOV
3233
        max_tenant_id++;
×
3234

UNCOV
3235
        DetectEngineTenantMapping *map = master->tenant_mapping_list;
×
UNCOV
3236
        while (map) {
×
UNCOV
3237
            map_cnt++;
×
UNCOV
3238
            map = map->next;
×
UNCOV
3239
        }
×
3240

UNCOV
3241
        if (map_cnt > 0) {
×
UNCOV
3242
            map_array_size = map_cnt + 1;
×
3243

UNCOV
3244
            map_array = SCCalloc(map_array_size, sizeof(*map_array));
×
UNCOV
3245
            if (map_array == NULL)
×
3246
                goto error;
×
3247

3248
            /* fill the array */
UNCOV
3249
            map_cnt = 0;
×
UNCOV
3250
            map = master->tenant_mapping_list;
×
UNCOV
3251
            while (map) {
×
UNCOV
3252
                if (map_cnt >= map_array_size) {
×
3253
                    goto error;
×
3254
                }
×
UNCOV
3255
                map_array[map_cnt].traffic_id = map->traffic_id;
×
UNCOV
3256
                map_array[map_cnt].tenant_id = map->tenant_id;
×
UNCOV
3257
                map_cnt++;
×
UNCOV
3258
                map = map->next;
×
UNCOV
3259
            }
×
3260

UNCOV
3261
        }
×
3262

3263
        /* set up hash for tenant lookup */
UNCOV
3264
        list = master->list;
×
UNCOV
3265
        while (list) {
×
UNCOV
3266
            SCLogDebug("tenant-id %u", list->tenant_id);
×
UNCOV
3267
            if (list->tenant_id != 0) {
×
UNCOV
3268
                DetectEngineThreadCtx *mt_det_ctx = DetectEngineThreadCtxInitForReload(tv, list, 0);
×
UNCOV
3269
                if (mt_det_ctx == NULL)
×
3270
                    goto error;
×
UNCOV
3271
                if (HashTableAdd(mt_det_ctxs_hash, mt_det_ctx, 0) != 0) {
×
3272
                    goto error;
×
3273
                }
×
UNCOV
3274
            }
×
UNCOV
3275
            list = list->next;
×
UNCOV
3276
        }
×
UNCOV
3277
    }
×
3278

UNCOV
3279
    det_ctx->mt_det_ctxs_hash = mt_det_ctxs_hash;
×
UNCOV
3280
    mt_det_ctxs_hash = NULL;
×
3281

UNCOV
3282
    det_ctx->mt_det_ctxs_cnt = max_tenant_id;
×
3283

UNCOV
3284
    det_ctx->tenant_array = map_array;
×
UNCOV
3285
    det_ctx->tenant_array_size = map_array_size;
×
3286

UNCOV
3287
    switch (master->tenant_selector) {
×
3288
        case TENANT_SELECTOR_UNKNOWN:
×
3289
            SCLogDebug("TENANT_SELECTOR_UNKNOWN");
×
3290
            break;
×
UNCOV
3291
        case TENANT_SELECTOR_VLAN:
×
UNCOV
3292
            det_ctx->TenantGetId = DetectEngineTenantGetIdFromVlanId;
×
UNCOV
3293
            SCLogDebug("TENANT_SELECTOR_VLAN");
×
UNCOV
3294
            break;
×
3295
        case TENANT_SELECTOR_LIVEDEV:
×
3296
            det_ctx->TenantGetId = DetectEngineTenantGetIdFromLivedev;
×
3297
            SCLogDebug("TENANT_SELECTOR_LIVEDEV");
×
3298
            break;
×
3299
        case TENANT_SELECTOR_DIRECT:
×
3300
            det_ctx->TenantGetId = DetectEngineTenantGetIdFromPcap;
×
3301
            SCLogDebug("TENANT_SELECTOR_DIRECT");
×
3302
            break;
×
UNCOV
3303
    }
×
3304

UNCOV
3305
    return TM_ECODE_OK;
×
3306
error:
×
3307
    if (map_array != NULL)
×
3308
        SCFree(map_array);
×
3309
    if (mt_det_ctxs_hash != NULL)
×
3310
        HashTableFree(mt_det_ctxs_hash);
×
3311

3312
    return TM_ECODE_FAILED;
×
UNCOV
3313
}
×
3314

3315
/** \internal
3316
 *  \brief Helper for DetectThread setup functions
3317
 */
3318
static TmEcode ThreadCtxDoInit (DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
3319
{
35,438✔
3320
    PatternMatchThreadPrepare(&det_ctx->mtc, NULL, de_ctx->mpm_matcher);
35,438✔
3321

3322
    PmqSetup(&det_ctx->pmq);
35,438✔
3323

3324
    det_ctx->spm_thread_ctx = SpmMakeThreadCtx(de_ctx->spm_global_thread_ctx);
35,438✔
3325
    if (det_ctx->spm_thread_ctx == NULL) {
35,438✔
3326
        return TM_ECODE_FAILED;
×
3327
    }
×
3328

3329
    /* DeState */
3330
    if (de_ctx->sig_array_len > 0) {
35,438✔
3331
        det_ctx->match_array_len = de_ctx->sig_array_len;
20,527✔
3332
        det_ctx->match_array = SCCalloc(det_ctx->match_array_len, sizeof(Signature *));
20,527✔
3333
        if (det_ctx->match_array == NULL) {
20,527✔
3334
            return TM_ECODE_FAILED;
×
3335
        }
×
3336
        det_ctx->replace = SCCalloc(de_ctx->sig_array_len, sizeof(Signature *));
20,527✔
3337
        if (det_ctx->replace == NULL) {
20,527✔
3338
            return TM_ECODE_FAILED;
×
3339
        }
×
3340

3341
        RuleMatchCandidateTxArrayInit(det_ctx, de_ctx->sig_array_len);
20,527✔
3342
    }
20,527✔
3343

3344
    /* Alert processing queue */
3345
    AlertQueueInit(det_ctx);
35,438✔
3346

3347
    /* byte_extract storage */
3348
    det_ctx->byte_values = SCMalloc(sizeof(*det_ctx->byte_values) *
35,438✔
3349
                                  (de_ctx->byte_extract_max_local_id + 1));
35,438✔
3350
    if (det_ctx->byte_values == NULL) {
35,438✔
3351
        return TM_ECODE_FAILED;
×
3352
    }
×
3353

3354
    /* Allocate space for base64 decoded data. */
3355
    if (de_ctx->base64_decode_max_len) {
35,438✔
3356
        det_ctx->base64_decoded = SCMalloc(de_ctx->base64_decode_max_len);
447✔
3357
        if (det_ctx->base64_decoded == NULL) {
447✔
3358
            return TM_ECODE_FAILED;
×
3359
        }
×
3360
        det_ctx->base64_decoded_len = 0;
447✔
3361
    }
447✔
3362

3363
    det_ctx->inspect.buffers_size = de_ctx->buffer_type_id;
35,438✔
3364
    det_ctx->inspect.buffers = SCCalloc(det_ctx->inspect.buffers_size, sizeof(InspectionBuffer));
35,438✔
3365
    if (det_ctx->inspect.buffers == NULL) {
35,438✔
3366
        return TM_ECODE_FAILED;
×
3367
    }
×
3368
    det_ctx->inspect.to_clear_queue = SCCalloc(det_ctx->inspect.buffers_size, sizeof(uint32_t));
35,438✔
3369
    if (det_ctx->inspect.to_clear_queue == NULL) {
35,438✔
3370
        return TM_ECODE_FAILED;
×
3371
    }
×
3372
    det_ctx->inspect.to_clear_idx = 0;
35,438✔
3373

3374
    det_ctx->multi_inspect.buffers_size = de_ctx->buffer_type_id;
35,438✔
3375
    det_ctx->multi_inspect.buffers = SCCalloc(det_ctx->multi_inspect.buffers_size, sizeof(InspectionBufferMultipleForList));
35,438✔
3376
    if (det_ctx->multi_inspect.buffers == NULL) {
35,438✔
3377
        return TM_ECODE_FAILED;
×
3378
    }
×
3379
    det_ctx->multi_inspect.to_clear_queue = SCCalloc(det_ctx->multi_inspect.buffers_size, sizeof(uint32_t));
35,438✔
3380
    if (det_ctx->multi_inspect.to_clear_queue == NULL) {
35,438✔
3381
        return TM_ECODE_FAILED;
×
3382
    }
×
3383
    det_ctx->multi_inspect.to_clear_idx = 0;
35,438✔
3384

3385

3386
    DetectEngineThreadCtxInitKeywords(de_ctx, det_ctx);
35,438✔
3387
    DetectEngineThreadCtxInitGlobalKeywords(det_ctx);
35,438✔
3388
#ifdef PROFILE_RULES
3389
    SCProfilingRuleThreadSetup(de_ctx->profile_ctx, det_ctx);
3390
#endif
3391
#ifdef PROFILING
3392
    SCProfilingKeywordThreadSetup(de_ctx->profile_keyword_ctx, det_ctx);
3393
    SCProfilingPrefilterThreadSetup(de_ctx->profile_prefilter_ctx, det_ctx);
3394
    SCProfilingSghThreadSetup(de_ctx->profile_sgh_ctx, det_ctx);
3395
#endif
3396
    SC_ATOMIC_INIT(det_ctx->so_far_used_by_detect);
35,438✔
3397

3398
    return TM_ECODE_OK;
35,438✔
3399
}
35,438✔
3400

3401
/** \brief initialize thread specific detection engine context
3402
 *
3403
 *  \note there is a special case when using delayed detect. In this case the
3404
 *        function is called twice per thread. The first time the rules are not
3405
 *        yet loaded. de_ctx->delayed_detect_initialized will be 0. The 2nd
3406
 *        time they will be loaded. de_ctx->delayed_detect_initialized will be 1.
3407
 *        This is needed to do the per thread counter registration before the
3408
 *        packet runtime starts. In delayed detect mode, the first call will
3409
 *        return a NULL ptr through the data ptr.
3410
 *
3411
 *  \param tv ThreadVars for this thread
3412
 *  \param initdata pointer to de_ctx
3413
 *  \param data[out] pointer to store our thread detection ctx
3414
 *
3415
 *  \retval TM_ECODE_OK if all went well
3416
 *  \retval TM_ECODE_FAILED on serious errors
3417
 */
3418
TmEcode DetectEngineThreadCtxInit(ThreadVars *tv, void *initdata, void **data)
3419
{
1✔
3420
    DetectEngineThreadCtx *det_ctx = SCCalloc(1, sizeof(DetectEngineThreadCtx));
1✔
3421
    if (unlikely(det_ctx == NULL))
1✔
3422
        return TM_ECODE_FAILED;
×
3423

3424
    det_ctx->tv = tv;
1✔
3425
    det_ctx->de_ctx = DetectEngineGetCurrent();
1✔
3426
    if (det_ctx->de_ctx == NULL) {
1✔
3427
#ifdef UNITTESTS
3428
        if (RunmodeIsUnittests()) {
3429
            det_ctx->de_ctx = (DetectEngineCtx *)initdata;
3430
        } else {
3431
            DetectEngineThreadCtxDeinit(tv, det_ctx);
3432
            return TM_ECODE_FAILED;
3433
        }
3434
#else
UNCOV
3435
        DetectEngineThreadCtxDeinit(tv, det_ctx);
×
UNCOV
3436
        return TM_ECODE_FAILED;
×
UNCOV
3437
#endif
×
UNCOV
3438
    }
×
3439

3440
    if (det_ctx->de_ctx->type == DETECT_ENGINE_TYPE_NORMAL ||
1✔
3441
        det_ctx->de_ctx->type == DETECT_ENGINE_TYPE_TENANT)
1✔
3442
    {
1✔
3443
        if (ThreadCtxDoInit(det_ctx->de_ctx, det_ctx) != TM_ECODE_OK) {
1✔
3444
            DetectEngineThreadCtxDeinit(tv, det_ctx);
×
3445
            return TM_ECODE_FAILED;
×
3446
        }
×
3447
    }
1✔
3448

3449
    /** alert counter setup */
3450
    det_ctx->counter_alerts = StatsRegisterCounter("detect.alert", &tv->stats);
1✔
3451
    det_ctx->counter_alerts_overflow =
1✔
3452
            StatsRegisterCounter("detect.alert_queue_overflow", &tv->stats);
1✔
3453
    det_ctx->counter_alerts_suppressed =
1✔
3454
            StatsRegisterCounter("detect.alerts_suppressed", &tv->stats);
1✔
3455

3456
    /* Register counter for Lua rule errors. */
3457
    det_ctx->lua_rule_errors = StatsRegisterCounter("detect.lua.errors", &tv->stats);
1✔
3458

3459
    /* Register a counter for Lua blocked function attempts. */
3460
    det_ctx->lua_blocked_function_errors =
1✔
3461
            StatsRegisterCounter("detect.lua.blocked_function_errors", &tv->stats);
1✔
3462

3463
    /* Register a counter for Lua instruction limit errors. */
3464
    det_ctx->lua_instruction_limit_errors =
1✔
3465
            StatsRegisterCounter("detect.lua.instruction_limit_errors", &tv->stats);
1✔
3466

3467
    /* Register a counter for Lua memory limit errors. */
3468
    det_ctx->lua_memory_limit_errors =
1✔
3469
            StatsRegisterCounter("detect.lua.memory_limit_errors", &tv->stats);
1✔
3470

3471
    det_ctx->json_content = NULL;
1✔
3472
    det_ctx->json_content_capacity = 0;
1✔
3473
    det_ctx->json_content_len = 0;
1✔
3474

3475
#ifdef PROFILING
3476
    det_ctx->counter_mpm_list = StatsRegisterAvgCounter("detect.mpm_list", &tv->stats);
3477
    det_ctx->counter_match_list = StatsRegisterAvgCounter("detect.match_list", &tv->stats);
3478
#endif
3479

3480
    if (DetectEngineMultiTenantEnabled()) {
1✔
UNCOV
3481
        DetectEngineMasterCtx *master = &g_master_de_ctx;
×
UNCOV
3482
        SCMutexLock(&master->lock);
×
UNCOV
3483
        if (DetectEngineThreadCtxInitForMT(tv, det_ctx) != TM_ECODE_OK) {
×
3484
            DetectEngineThreadCtxDeinit(tv, det_ctx);
×
3485
            SCMutexUnlock(&master->lock);
×
3486
            return TM_ECODE_FAILED;
×
3487
        }
×
UNCOV
3488
        SCMutexUnlock(&master->lock);
×
UNCOV
3489
    }
×
3490

3491
    /* pass thread data back to caller */
3492
    *data = (void *)det_ctx;
1✔
3493

3494
    return TM_ECODE_OK;
1✔
3495
}
1✔
3496

3497
/**
3498
 * \internal
3499
 * \brief initialize a det_ctx for reload cases
3500
 * \param new_de_ctx the new detection engine
3501
 * \param mt flag to indicate if MT should be set up for this det_ctx
3502
 *           this should only be done for the 'root' det_ctx
3503
 *
3504
 * \retval det_ctx detection engine thread ctx or NULL in case of error
3505
 */
3506
DetectEngineThreadCtx *DetectEngineThreadCtxInitForReload(
3507
        ThreadVars *tv, DetectEngineCtx *new_de_ctx, int mt)
3508
{
35,437✔
3509
    DetectEngineThreadCtx *det_ctx = SCCalloc(1, sizeof(DetectEngineThreadCtx));
35,437✔
3510
    if (unlikely(det_ctx == NULL))
35,437✔
3511
        return NULL;
×
3512

3513
    det_ctx->tenant_id = new_de_ctx->tenant_id;
35,437✔
3514
    det_ctx->tv = tv;
35,437✔
3515
    det_ctx->de_ctx = DetectEngineReference(new_de_ctx);
35,437✔
3516
    if (det_ctx->de_ctx == NULL) {
35,437✔
3517
        SCFree(det_ctx);
×
3518
        return NULL;
×
3519
    }
×
3520

3521
    /* most of the init happens here */
3522
    if (det_ctx->de_ctx->type == DETECT_ENGINE_TYPE_NORMAL ||
35,437✔
3523
        det_ctx->de_ctx->type == DETECT_ENGINE_TYPE_TENANT)
35,437✔
3524
    {
35,437✔
3525
        if (ThreadCtxDoInit(det_ctx->de_ctx, det_ctx) != TM_ECODE_OK) {
35,437✔
3526
            DetectEngineDeReference(&det_ctx->de_ctx);
×
3527
            SCFree(det_ctx);
×
3528
            return NULL;
×
3529
        }
×
3530
    }
35,437✔
3531

3532
    /** alert counter setup */
3533
    det_ctx->counter_alerts = StatsRegisterCounter("detect.alert", &tv->stats);
35,437✔
3534
    det_ctx->counter_alerts_overflow =
35,437✔
3535
            StatsRegisterCounter("detect.alert_queue_overflow", &tv->stats);
35,437✔
3536
    det_ctx->counter_alerts_suppressed =
35,437✔
3537
            StatsRegisterCounter("detect.alerts_suppressed", &tv->stats);
35,437✔
3538
#ifdef PROFILING
3539
    det_ctx->counter_mpm_list = StatsRegisterAvgCounter("detect.mpm_list", &tv->stats);
3540
    det_ctx->counter_match_list = StatsRegisterAvgCounter("detect.match_list", &tv->stats);
3541
#endif
3542

3543
    if (mt && DetectEngineMultiTenantEnabledWithLock()) {
35,437✔
UNCOV
3544
        if (DetectEngineThreadCtxInitForMT(tv, det_ctx) != TM_ECODE_OK) {
×
3545
            DetectEngineDeReference(&det_ctx->de_ctx);
×
3546
            SCFree(det_ctx);
×
3547
            return NULL;
×
3548
        }
×
UNCOV
3549
    }
×
3550

3551
    return det_ctx;
35,437✔
3552
}
35,437✔
3553

3554
static void DetectEngineThreadCtxFree(DetectEngineThreadCtx *det_ctx)
3555
{
35,437✔
3556
#if  DEBUG
3557
    SCLogDebug("PACKET PKT_STREAM_ADD: %"PRIu64, det_ctx->pkt_stream_add_cnt);
3558

3559
    SCLogDebug("PAYLOAD MPM %"PRIu64"/%"PRIu64, det_ctx->payload_mpm_cnt, det_ctx->payload_mpm_size);
3560
    SCLogDebug("STREAM  MPM %"PRIu64"/%"PRIu64, det_ctx->stream_mpm_cnt, det_ctx->stream_mpm_size);
3561

3562
    SCLogDebug("PAYLOAD SIG %"PRIu64"/%"PRIu64, det_ctx->payload_persig_cnt, det_ctx->payload_persig_size);
3563
    SCLogDebug("STREAM  SIG %"PRIu64"/%"PRIu64, det_ctx->stream_persig_cnt, det_ctx->stream_persig_size);
3564
#endif
3565

3566
    if (det_ctx->tenant_array != NULL) {
35,437✔
UNCOV
3567
        SCFree(det_ctx->tenant_array);
×
UNCOV
3568
        det_ctx->tenant_array = NULL;
×
UNCOV
3569
    }
×
3570

3571
#ifdef PROFILE_RULES
3572
    SCProfilingRuleThreadCleanup(det_ctx);
3573
#endif
3574
#ifdef PROFILING
3575
    SCProfilingKeywordThreadCleanup(det_ctx);
3576
    SCProfilingPrefilterThreadCleanup(det_ctx);
3577
    SCProfilingSghThreadCleanup(det_ctx);
3578
#endif
3579

3580
    /** \todo get rid of this static */
3581
    if (det_ctx->de_ctx != NULL) {
35,437✔
3582
        PatternMatchThreadDestroy(&det_ctx->mtc, NULL, det_ctx->de_ctx->mpm_matcher);
35,437✔
3583
    }
35,437✔
3584

3585
    PmqFree(&det_ctx->pmq);
35,437✔
3586

3587
    if (det_ctx->spm_thread_ctx != NULL) {
35,437✔
3588
        SpmDestroyThreadCtx(det_ctx->spm_thread_ctx);
35,437✔
3589
    }
35,437✔
3590
    if (det_ctx->match_array != NULL)
35,437✔
3591
        SCFree(det_ctx->match_array);
20,527✔
3592
    if (det_ctx->replace != NULL)
35,437✔
3593
        SCFree(det_ctx->replace);
20,527✔
3594

3595
    RuleMatchCandidateTxArrayFree(det_ctx);
35,437✔
3596

3597
    AlertQueueFree(det_ctx);
35,437✔
3598

3599
    if (det_ctx->post_rule_work_queue.q)
35,437✔
3600
        SCFree(det_ctx->post_rule_work_queue.q);
13✔
3601

3602
    if (det_ctx->byte_values != NULL)
35,437✔
3603
        SCFree(det_ctx->byte_values);
35,437✔
3604

3605
    /* Decoded base64 data. */
3606
    if (det_ctx->base64_decoded != NULL) {
35,437✔
3607
        SCFree(det_ctx->base64_decoded);
447✔
3608
    }
447✔
3609

3610
    if (det_ctx->inspect.buffers) {
35,437✔
3611
        for (uint32_t i = 0; i < det_ctx->inspect.buffers_size; i++) {
14,915,098✔
3612
            InspectionBufferFree(&det_ctx->inspect.buffers[i]);
14,879,661✔
3613
        }
14,879,661✔
3614
        SCFree(det_ctx->inspect.buffers);
35,437✔
3615
    }
35,437✔
3616
    if (det_ctx->inspect.to_clear_queue) {
35,437✔
3617
        SCFree(det_ctx->inspect.to_clear_queue);
35,437✔
3618
    }
35,437✔
3619
    if (det_ctx->multi_inspect.buffers) {
35,437✔
3620
        for (uint32_t i = 0; i < det_ctx->multi_inspect.buffers_size; i++) {
14,915,098✔
3621
            InspectionBufferMultipleForList *fb = &det_ctx->multi_inspect.buffers[i];
14,879,661✔
3622
            for (uint32_t x = 0; x < fb->size; x++) {
14,884,940✔
3623
                InspectionBufferFree(&fb->inspection_buffers[x]);
5,279✔
3624
            }
5,279✔
3625
            SCFree(fb->inspection_buffers);
14,879,661✔
3626
        }
14,879,661✔
3627
        SCFree(det_ctx->multi_inspect.buffers);
35,437✔
3628
    }
35,437✔
3629
    if (det_ctx->multi_inspect.to_clear_queue) {
35,437✔
3630
        SCFree(det_ctx->multi_inspect.to_clear_queue);
35,437✔
3631
    }
35,437✔
3632

3633
    DetectEngineThreadCtxDeinitGlobalKeywords(det_ctx);
35,437✔
3634
    if (det_ctx->de_ctx != NULL) {
35,437✔
3635
        DetectEngineThreadCtxDeinitKeywords(det_ctx->de_ctx, det_ctx);
35,437✔
3636
#ifdef UNITTESTS
3637
        if (!RunmodeIsUnittests() || det_ctx->de_ctx->ref_cnt > 0)
3638
            DetectEngineDeReference(&det_ctx->de_ctx);
3639
#else
3640
        DetectEngineDeReference(&det_ctx->de_ctx);
35,437✔
3641
#endif
35,437✔
3642
    }
35,437✔
3643

3644
    if (det_ctx->json_content) {
35,437✔
UNCOV
3645
        SCFree(det_ctx->json_content);
×
UNCOV
3646
        det_ctx->json_content = NULL;
×
UNCOV
3647
        det_ctx->json_content_capacity = 0;
×
UNCOV
3648
    }
×
3649

3650
    SCAppLayerDecoderEventsFreeEvents(&det_ctx->decoder_events);
35,437✔
3651
    PrefilterPktNonPFStatsDump();
35,437✔
3652
    SCFree(det_ctx);
35,437✔
3653

3654
    ThresholdCacheThreadFree();
35,437✔
3655
}
35,437✔
3656

3657
TmEcode DetectEngineThreadCtxDeinit(ThreadVars *tv, void *data)
3658
{
35,437✔
3659
    DetectEngineThreadCtx *det_ctx = (DetectEngineThreadCtx *)data;
35,437✔
3660

3661
    if (det_ctx == NULL) {
35,437✔
3662
        SCLogWarning("argument \"data\" NULL");
×
3663
        return TM_ECODE_OK;
×
3664
    }
×
3665

3666
    if (det_ctx->mt_det_ctxs_hash != NULL) {
35,437✔
UNCOV
3667
        HashTableFree(det_ctx->mt_det_ctxs_hash);
×
UNCOV
3668
        det_ctx->mt_det_ctxs_hash = NULL;
×
UNCOV
3669
    }
×
3670
    DetectEngineThreadCtxFree(det_ctx);
35,437✔
3671

3672
    return TM_ECODE_OK;
35,437✔
3673
}
35,437✔
3674

3675
static uint32_t DetectKeywordCtxHashFunc(HashListTable *ht, void *data, uint16_t datalen)
3676
{
734,712✔
3677
    DetectEngineThreadKeywordCtxItem *ctx = data;
734,712✔
3678
    const char *name = ctx->name;
734,712✔
3679
    uint64_t hash =
734,712✔
3680
            StringHashDjb2((const uint8_t *)name, (uint32_t)strlen(name)) + (ptrdiff_t)ctx->data;
734,712✔
3681
    hash %= ht->array_size;
734,712✔
3682
    return (uint32_t)hash;
734,712✔
3683
}
734,712✔
3684

3685
static char DetectKeywordCtxCompareFunc(void *data1, uint16_t len1, void *data2, uint16_t len2)
3686
{
354,102✔
3687
    DetectEngineThreadKeywordCtxItem *ctx1 = data1;
354,102✔
3688
    DetectEngineThreadKeywordCtxItem *ctx2 = data2;
354,102✔
3689
    const char *name1 = ctx1->name;
354,102✔
3690
    const char *name2 = ctx2->name;
354,102✔
3691
    return (strcmp(name1, name2) == 0 && ctx1->data == ctx2->data);
354,102✔
3692
}
354,102✔
3693

3694
static void DetectKeywordCtxFreeFunc(void *ptr)
3695
{
344,007✔
3696
    SCFree(ptr);
344,007✔
3697
}
344,007✔
3698

3699
/** \brief Register Thread keyword context Funcs
3700
 *
3701
 *  \param de_ctx detection engine to register in
3702
 *  \param name keyword name for error printing
3703
 *  \param InitFunc function ptr
3704
 *  \param data keyword init data to pass to Func. Can be NULL.
3705
 *  \param FreeFunc function ptr
3706
 *  \param mode 0 normal (ctx per keyword instance) 1 shared (one ctx per det_ct)
3707
 *
3708
 *  \retval id for retrieval of ctx at runtime
3709
 *  \retval -1 on error
3710
 *
3711
 *  \note make sure "data" remains valid and it free'd elsewhere. It's
3712
 *        recommended to store it in the keywords global ctx so that
3713
 *        it's freed when the de_ctx is freed.
3714
 */
3715
int DetectRegisterThreadCtxFuncs(DetectEngineCtx *de_ctx, const char *name, void *(*InitFunc)(void *), void *data, void (*FreeFunc)(void *), int mode)
3716
{
344,007✔
3717
    BUG_ON(de_ctx == NULL || InitFunc == NULL || FreeFunc == NULL);
344,007✔
3718

3719
    if (de_ctx->keyword_hash == NULL) {
344,007✔
3720
        de_ctx->keyword_hash = HashListTableInit(4096, // TODO
6,779✔
3721
                DetectKeywordCtxHashFunc, DetectKeywordCtxCompareFunc, DetectKeywordCtxFreeFunc);
6,779✔
3722
        BUG_ON(de_ctx->keyword_hash == NULL);
6,779✔
3723
    }
6,779✔
3724

3725
    if (mode) {
344,007✔
3726
        DetectEngineThreadKeywordCtxItem search = { .data = data, .name = name };
460✔
3727

3728
        DetectEngineThreadKeywordCtxItem *item =
460✔
3729
                HashListTableLookup(de_ctx->keyword_hash, (void *)&search, 0);
460✔
3730
        if (item)
460✔
3731
            return item->id;
×
3732

3733
        /* fall through */
3734
    }
460✔
3735

3736
    DetectEngineThreadKeywordCtxItem *item = SCCalloc(1, sizeof(DetectEngineThreadKeywordCtxItem));
344,007✔
3737
    if (unlikely(item == NULL))
344,007✔
3738
        return -1;
×
3739

3740
    item->InitFunc = InitFunc;
344,007✔
3741
    item->FreeFunc = FreeFunc;
344,007✔
3742
    item->data = data;
344,007✔
3743
    item->name = name;
344,007✔
3744
    item->id = de_ctx->keyword_id++;
344,007✔
3745

3746
    if (HashListTableAdd(de_ctx->keyword_hash, (void *)item, 0) < 0) {
344,007✔
3747
        SCFree(item);
×
3748
        return -1;
×
3749
    }
×
3750
    return item->id;
344,007✔
3751
}
344,007✔
3752

3753
/** \brief Remove Thread keyword context registration
3754
 *
3755
 *  \param de_ctx detection engine to deregister from
3756
 *  \param det_ctx detection engine thread context to deregister from
3757
 *  \param data keyword init data to pass to Func. Can be NULL.
3758
 *  \param name keyword name for error printing
3759
 *
3760
 *  \retval 1 Item unregistered
3761
 *  \retval 0 otherwise
3762
 *
3763
 *  \note make sure "data" remains valid and it free'd elsewhere. It's
3764
 *        recommended to store it in the keywords global ctx so that
3765
 *        it's freed when the de_ctx is freed.
3766
 */
3767
int DetectUnregisterThreadCtxFuncs(DetectEngineCtx *de_ctx, void *data, const char *name)
3768
{
402,388✔
3769
    /* might happen if we call this before a call to *Register* */
3770
    if (de_ctx->keyword_hash == NULL)
402,388✔
3771
        return 1;
12,143✔
3772
    DetectEngineThreadKeywordCtxItem remove = { .data = data, .name = name };
390,245✔
3773
    if (HashListTableRemove(de_ctx->keyword_hash, (void *)&remove, 0) == 0)
390,245✔
3774
        return 1;
343,547✔
3775
    return 0;
46,698✔
3776
}
390,245✔
3777
/** \brief Retrieve thread local keyword ctx by id
3778
 *
3779
 *  \param det_ctx detection engine thread ctx to retrieve the ctx from
3780
 *  \param id id of the ctx returned by DetectRegisterThreadCtxInitFunc at
3781
 *            keyword init.
3782
 *
3783
 *  \retval ctx or NULL on error
3784
 */
3785
void *DetectThreadCtxGetKeywordThreadCtx(DetectEngineThreadCtx *det_ctx, int id)
3786
{
7,301✔
3787
    if (id < 0 || id > det_ctx->keyword_ctxs_size || det_ctx->keyword_ctxs_array == NULL)
7,301✔
3788
        return NULL;
×
3789

3790
    return det_ctx->keyword_ctxs_array[id];
7,301✔
3791
}
7,301✔
3792

3793

3794
/** \brief Register Thread keyword context Funcs (Global)
3795
 *
3796
 *  IDs stay static over reloads and between tenants
3797
 *
3798
 *  \param name keyword name for error printing
3799
 *  \param InitFunc function ptr
3800
 *  \param FreeFunc function ptr
3801
 *
3802
 *  \retval id for retrieval of ctx at runtime
3803
 *  \retval -1 on error
3804
 */
3805
int DetectRegisterThreadCtxGlobalFuncs(const char *name,
3806
        void *(*InitFunc)(void *), void *data, void (*FreeFunc)(void *))
3807
{
69✔
3808
    int id;
69✔
3809
    BUG_ON(InitFunc == NULL || FreeFunc == NULL);
69✔
3810

3811
    DetectEngineMasterCtx *master = &g_master_de_ctx;
69✔
3812

3813
    /* if already registered, return existing id */
3814
    DetectEngineThreadKeywordCtxItem *item = master->keyword_list;
69✔
3815
    while (item != NULL) {
828✔
3816
        if (strcmp(name, item->name) == 0) {
759✔
3817
            id = item->id;
×
3818
            return id;
×
3819
        }
×
3820

3821
        item = item->next;
759✔
3822
    }
759✔
3823

3824
    item = SCCalloc(1, sizeof(*item));
69✔
3825
    if (unlikely(item == NULL)) {
69✔
3826
        return -1;
×
3827
    }
×
3828
    item->InitFunc = InitFunc;
69✔
3829
    item->FreeFunc = FreeFunc;
69✔
3830
    item->name = name;
69✔
3831
    item->data = data;
69✔
3832

3833
    item->next = master->keyword_list;
69✔
3834
    master->keyword_list = item;
69✔
3835
    item->id = master->keyword_id++;
69✔
3836

3837
    id = item->id;
69✔
3838
    return id;
69✔
3839
}
69✔
3840

3841
/** \brief Retrieve thread local keyword ctx by id
3842
 *
3843
 *  \param det_ctx detection engine thread ctx to retrieve the ctx from
3844
 *  \param id id of the ctx returned by DetectRegisterThreadCtxInitFunc at
3845
 *            keyword init.
3846
 *
3847
 *  \retval ctx or NULL on error
3848
 */
3849
void *DetectThreadCtxGetGlobalKeywordThreadCtx(DetectEngineThreadCtx *det_ctx, int id)
3850
{
10,154✔
3851
    if (id < 0 || id > det_ctx->global_keyword_ctxs_size ||
10,154✔
3852
        det_ctx->global_keyword_ctxs_array == NULL) {
10,154✔
3853
        return NULL;
×
3854
    }
×
3855

3856
    return det_ctx->global_keyword_ctxs_array[id];
10,154✔
3857
}
10,154✔
3858

3859
/** \brief Check if detection is enabled
3860
 *  \retval bool true or false */
3861
int DetectEngineEnabled(void)
3862
{
1✔
3863
    DetectEngineMasterCtx *master = &g_master_de_ctx;
1✔
3864
    SCMutexLock(&master->lock);
1✔
3865

3866
    if (master->list == NULL) {
1✔
UNCOV
3867
        SCMutexUnlock(&master->lock);
×
UNCOV
3868
        return 0;
×
UNCOV
3869
    }
×
3870

3871
    SCMutexUnlock(&master->lock);
1✔
3872
    return 1;
1✔
3873
}
1✔
3874

3875
uint32_t DetectEngineGetVersion(void)
3876
{
35,469✔
3877
    uint32_t version;
35,469✔
3878
    DetectEngineMasterCtx *master = &g_master_de_ctx;
35,469✔
3879
    SCMutexLock(&master->lock);
35,469✔
3880
    version = master->version;
35,469✔
3881
    SCMutexUnlock(&master->lock);
35,469✔
3882
    return version;
35,469✔
3883
}
35,469✔
3884

3885
void DetectEngineBumpVersion(void)
3886
{
35,438✔
3887
    DetectEngineMasterCtx *master = &g_master_de_ctx;
35,438✔
3888
    SCMutexLock(&master->lock);
35,438✔
3889
    master->version++;
35,438✔
3890
    SCLogDebug("master version now %u", master->version);
35,438✔
3891
    SCMutexUnlock(&master->lock);
35,438✔
3892
}
35,438✔
3893

3894
DetectEngineCtx *DetectEngineGetCurrent(void)
3895
{
106,313✔
3896
    DetectEngineMasterCtx *master = &g_master_de_ctx;
106,313✔
3897
    SCMutexLock(&master->lock);
106,313✔
3898

3899
    DetectEngineCtx *de_ctx = master->list;
106,313✔
3900
    while (de_ctx) {
106,313✔
3901
        if (de_ctx->type == DETECT_ENGINE_TYPE_NORMAL ||
106,313✔
3902
            de_ctx->type == DETECT_ENGINE_TYPE_DD_STUB ||
106,313✔
3903
            de_ctx->type == DETECT_ENGINE_TYPE_MT_STUB)
106,313✔
3904
        {
106,313✔
3905
            de_ctx->ref_cnt++;
106,313✔
3906
            SCLogDebug("de_ctx %p ref_cnt %u", de_ctx, de_ctx->ref_cnt);
106,313✔
3907
            SCMutexUnlock(&master->lock);
106,313✔
3908
            return de_ctx;
106,313✔
3909
        }
106,313✔
UNCOV
3910
        de_ctx = de_ctx->next;
×
UNCOV
3911
    }
×
3912

UNCOV
3913
    SCMutexUnlock(&master->lock);
×
UNCOV
3914
    return NULL;
×
3915
}
106,313✔
3916

3917
DetectEngineCtx *DetectEngineReference(DetectEngineCtx *de_ctx)
3918
{
35,437✔
3919
    if (de_ctx == NULL)
35,437✔
3920
        return NULL;
×
3921
    de_ctx->ref_cnt++;
35,437✔
3922
    return de_ctx;
35,437✔
3923
}
35,437✔
3924

3925
static bool DetectEngineMultiTenantEnabledWithLock(void)
3926
{
70,876✔
3927
    DetectEngineMasterCtx *master = &g_master_de_ctx;
70,876✔
3928
    return master->multi_tenant_enabled;
70,876✔
3929
}
70,876✔
3930

3931
bool DetectEngineMultiTenantEnabled(void)
3932
{
35,439✔
3933
    DetectEngineMasterCtx *master = &g_master_de_ctx;
35,439✔
3934
    SCMutexLock(&master->lock);
35,439✔
3935
    bool enabled = DetectEngineMultiTenantEnabledWithLock();
35,439✔
3936
    SCMutexUnlock(&master->lock);
35,439✔
3937
    return enabled;
35,439✔
3938
}
35,439✔
3939

3940
/** \internal
3941
 *  \brief load a tenant from a yaml file
3942
 *
3943
 *  \param tenant_id the tenant id by which the config is known
3944
 *  \param filename full path of a yaml file
3945
 *  \param loader_id id of loader thread or -1
3946
 *
3947
 *  \retval 0 ok
3948
 *  \retval -1 failed
3949
 */
3950
static int DetectEngineMultiTenantLoadTenant(uint32_t tenant_id, const char *filename, int loader_id)
UNCOV
3951
{
×
UNCOV
3952
    DetectEngineCtx *de_ctx = NULL;
×
UNCOV
3953
    char prefix[64];
×
3954

UNCOV
3955
    snprintf(prefix, sizeof(prefix), "multi-detect.%u", tenant_id);
×
3956

UNCOV
3957
    SCStat st;
×
UNCOV
3958
    if (SCStatFn(filename, &st) != 0) {
×
3959
        SCLogError("failed to stat file %s", filename);
×
3960
        goto error;
×
3961
    }
×
3962

UNCOV
3963
    de_ctx = DetectEngineGetByTenantId(tenant_id);
×
UNCOV
3964
    if (de_ctx != NULL) {
×
3965
        SCLogError("tenant %u already registered", tenant_id);
×
3966
        DetectEngineDeReference(&de_ctx);
×
3967
        goto error;
×
3968
    }
×
3969

UNCOV
3970
    SCConfNode *node = SCConfGetNode(prefix);
×
UNCOV
3971
    if (node == NULL) {
×
3972
        SCLogError("failed to properly setup yaml %s", filename);
×
3973
        goto error;
×
3974
    }
×
3975

UNCOV
3976
    de_ctx = DetectEngineCtxInitWithPrefix(prefix, tenant_id);
×
UNCOV
3977
    if (de_ctx == NULL) {
×
3978
        SCLogError("initializing detection engine "
×
3979
                   "context failed.");
×
3980
        goto error;
×
3981
    }
×
UNCOV
3982
    SCLogDebug("de_ctx %p with prefix %s", de_ctx, de_ctx->config_prefix);
×
3983

UNCOV
3984
    de_ctx->type = DETECT_ENGINE_TYPE_TENANT;
×
UNCOV
3985
    de_ctx->tenant_id = tenant_id;
×
UNCOV
3986
    de_ctx->loader_id = loader_id;
×
UNCOV
3987
    de_ctx->tenant_path = SCStrdup(filename);
×
UNCOV
3988
    if (de_ctx->tenant_path == NULL) {
×
3989
        SCLogError("Failed to duplicate path");
×
3990
        goto error;
×
3991
    }
×
3992

UNCOV
3993
    if (SigLoadSignatures(de_ctx, NULL, false) < 0) {
×
3994
        SCLogError("Loading signatures failed.");
×
3995
        goto error;
×
3996
    }
×
3997

UNCOV
3998
    DetectEngineAddToMaster(de_ctx);
×
3999

UNCOV
4000
    return 0;
×
4001

4002
error:
×
4003
    if (de_ctx != NULL) {
×
4004
        DetectEngineCtxFree(de_ctx);
×
4005
    }
×
4006
    return -1;
×
UNCOV
4007
}
×
4008

4009
static int DetectEngineMultiTenantReloadTenant(uint32_t tenant_id, const char *filename, int reload_cnt)
UNCOV
4010
{
×
UNCOV
4011
    DetectEngineCtx *old_de_ctx = DetectEngineGetByTenantId(tenant_id);
×
UNCOV
4012
    if (old_de_ctx == NULL) {
×
4013
        SCLogError("tenant detect engine not found");
×
4014
        return -1;
×
4015
    }
×
4016

UNCOV
4017
    if (filename == NULL)
×
UNCOV
4018
        filename = old_de_ctx->tenant_path;
×
4019

UNCOV
4020
    char prefix[64];
×
UNCOV
4021
    snprintf(prefix, sizeof(prefix), "multi-detect.%u.reload.%d", tenant_id, reload_cnt);
×
UNCOV
4022
    reload_cnt++;
×
UNCOV
4023
    SCLogDebug("prefix %s", prefix);
×
4024

UNCOV
4025
    if (SCConfYamlLoadFileWithPrefix(filename, prefix) != 0) {
×
4026
        SCLogError("failed to load yaml");
×
4027
        goto error;
×
4028
    }
×
4029

UNCOV
4030
    SCConfNode *node = SCConfGetNode(prefix);
×
UNCOV
4031
    if (node == NULL) {
×
4032
        SCLogError("failed to properly setup yaml %s", filename);
×
4033
        goto error;
×
4034
    }
×
4035

UNCOV
4036
    DetectEngineCtx *new_de_ctx = DetectEngineCtxInitWithPrefix(prefix, tenant_id);
×
UNCOV
4037
    if (new_de_ctx == NULL) {
×
4038
        SCLogError("initializing detection engine "
×
4039
                   "context failed.");
×
4040
        goto error;
×
4041
    }
×
UNCOV
4042
    SCLogDebug("de_ctx %p with prefix %s", new_de_ctx, new_de_ctx->config_prefix);
×
4043

UNCOV
4044
    new_de_ctx->type = DETECT_ENGINE_TYPE_TENANT;
×
UNCOV
4045
    new_de_ctx->tenant_id = tenant_id;
×
UNCOV
4046
    new_de_ctx->loader_id = old_de_ctx->loader_id;
×
UNCOV
4047
    new_de_ctx->tenant_path = SCStrdup(filename);
×
UNCOV
4048
    if (new_de_ctx->tenant_path == NULL) {
×
4049
        SCLogError("Failed to duplicate path");
×
4050
        goto new_de_ctx_error;
×
4051
    }
×
4052

UNCOV
4053
    if (SigLoadSignatures(new_de_ctx, NULL, false) < 0) {
×
4054
        SCLogError("Loading signatures failed.");
×
4055
        goto new_de_ctx_error;
×
4056
    }
×
4057

UNCOV
4058
    DetectEngineAddToMaster(new_de_ctx);
×
4059

4060
    /* move to free list */
UNCOV
4061
    DetectEngineMoveToFreeList(old_de_ctx);
×
UNCOV
4062
    DetectEngineDeReference(&old_de_ctx);
×
UNCOV
4063
    return 0;
×
4064

4065
new_de_ctx_error:
×
4066
    DetectEngineCtxFree(new_de_ctx);
×
4067

4068
error:
×
4069
    DetectEngineDeReference(&old_de_ctx);
×
4070
    return -1;
×
4071
}
×
4072

4073

4074
typedef struct TenantLoaderCtx_ {
4075
    uint32_t tenant_id;
4076
    int reload_cnt; /**< used by reload */
4077
    char *yaml;     /**< heap alloc'd copy of file path for the yaml */
4078
} TenantLoaderCtx;
4079

4080
static void DetectLoaderFreeTenant(void *ctx)
UNCOV
4081
{
×
UNCOV
4082
    TenantLoaderCtx *t = (TenantLoaderCtx *)ctx;
×
UNCOV
4083
    if (t->yaml != NULL) {
×
UNCOV
4084
        SCFree(t->yaml);
×
UNCOV
4085
    }
×
UNCOV
4086
    SCFree(t);
×
UNCOV
4087
}
×
4088

4089
static int DetectLoaderFuncLoadTenant(void *vctx, int loader_id)
UNCOV
4090
{
×
UNCOV
4091
    TenantLoaderCtx *ctx = (TenantLoaderCtx *)vctx;
×
4092

UNCOV
4093
    SCLogDebug("loader %d", loader_id);
×
UNCOV
4094
    if (DetectEngineMultiTenantLoadTenant(ctx->tenant_id, ctx->yaml, loader_id) != 0) {
×
4095
        return -1;
×
4096
    }
×
UNCOV
4097
    return 0;
×
UNCOV
4098
}
×
4099

4100
static int DetectLoaderSetupLoadTenant(uint32_t tenant_id, const char *yaml)
UNCOV
4101
{
×
UNCOV
4102
    TenantLoaderCtx *t = SCCalloc(1, sizeof(*t));
×
UNCOV
4103
    if (t == NULL)
×
4104
        return -ENOMEM;
×
4105

UNCOV
4106
    t->tenant_id = tenant_id;
×
UNCOV
4107
    t->yaml = SCStrdup(yaml);
×
UNCOV
4108
    if (t->yaml == NULL) {
×
4109
        SCFree(t);
×
4110
        return -ENOMEM;
×
4111
    }
×
4112

UNCOV
4113
    return DetectLoaderQueueTask(-1, DetectLoaderFuncLoadTenant, t, DetectLoaderFreeTenant);
×
UNCOV
4114
}
×
4115

4116
static int DetectLoaderFuncReloadTenant(void *vctx, int loader_id)
UNCOV
4117
{
×
UNCOV
4118
    TenantLoaderCtx *ctx = (TenantLoaderCtx *)vctx;
×
4119

UNCOV
4120
    SCLogDebug("loader_id %d", loader_id);
×
4121

UNCOV
4122
    if (DetectEngineMultiTenantReloadTenant(ctx->tenant_id, ctx->yaml, ctx->reload_cnt) != 0) {
×
4123
        return -1;
×
4124
    }
×
UNCOV
4125
    return 0;
×
UNCOV
4126
}
×
4127

4128
static int DetectLoaderSetupReloadTenants(const int reload_cnt)
UNCOV
4129
{
×
UNCOV
4130
    int ret = 0;
×
UNCOV
4131
    DetectEngineMasterCtx *master = &g_master_de_ctx;
×
UNCOV
4132
    SCMutexLock(&master->lock);
×
4133

UNCOV
4134
    DetectEngineCtx *de_ctx = master->list;
×
UNCOV
4135
    while (de_ctx) {
×
UNCOV
4136
        if (de_ctx->type == DETECT_ENGINE_TYPE_TENANT) {
×
UNCOV
4137
            TenantLoaderCtx *t = SCCalloc(1, sizeof(*t));
×
UNCOV
4138
            if (t == NULL) {
×
4139
                ret = -1;
×
4140
                goto error;
×
4141
            }
×
UNCOV
4142
            t->tenant_id = de_ctx->tenant_id;
×
UNCOV
4143
            t->reload_cnt = reload_cnt;
×
UNCOV
4144
            int loader_id = de_ctx->loader_id;
×
4145

UNCOV
4146
            int r = DetectLoaderQueueTask(
×
UNCOV
4147
                    loader_id, DetectLoaderFuncReloadTenant, t, DetectLoaderFreeTenant);
×
UNCOV
4148
            if (r < 0) {
×
4149
                ret = -2;
×
4150
                goto error;
×
4151
            }
×
UNCOV
4152
        }
×
4153

UNCOV
4154
        de_ctx = de_ctx->next;
×
UNCOV
4155
    }
×
UNCOV
4156
error:
×
UNCOV
4157
    SCMutexUnlock(&master->lock);
×
UNCOV
4158
    return ret;
×
UNCOV
4159
}
×
4160

4161
static int DetectLoaderSetupReloadTenant(uint32_t tenant_id, const char *yaml, int reload_cnt)
4162
{
×
4163
    DetectEngineCtx *old_de_ctx = DetectEngineGetByTenantId(tenant_id);
×
4164
    if (old_de_ctx == NULL)
×
4165
        return -ENOENT;
×
4166
    int loader_id = old_de_ctx->loader_id;
×
4167
    DetectEngineDeReference(&old_de_ctx);
×
4168

4169
    TenantLoaderCtx *t = SCCalloc(1, sizeof(*t));
×
4170
    if (t == NULL)
×
4171
        return -ENOMEM;
×
4172

4173
    t->tenant_id = tenant_id;
×
4174
    if (yaml != NULL) {
×
4175
        t->yaml = SCStrdup(yaml);
×
4176
        if (t->yaml == NULL) {
×
4177
            SCFree(t);
×
4178
            return -ENOMEM;
×
4179
        }
×
4180
    }
×
4181
    t->reload_cnt = reload_cnt;
×
4182

4183
    SCLogDebug("loader_id %d", loader_id);
×
4184

4185
    return DetectLoaderQueueTask(
×
4186
            loader_id, DetectLoaderFuncReloadTenant, t, DetectLoaderFreeTenant);
×
4187
}
×
4188

4189
/** \brief Load a tenant and wait for loading to complete
4190
 */
4191
int DetectEngineLoadTenantBlocking(uint32_t tenant_id, const char *yaml)
UNCOV
4192
{
×
UNCOV
4193
    int r = DetectLoaderSetupLoadTenant(tenant_id, yaml);
×
UNCOV
4194
    if (r < 0)
×
4195
        return r;
×
4196

UNCOV
4197
    if (DetectLoadersSync() != 0)
×
4198
        return -1;
×
4199

UNCOV
4200
    return 0;
×
UNCOV
4201
}
×
4202

4203
/** \brief Reload a tenant and wait for loading to complete
4204
 */
4205
int DetectEngineReloadTenantBlocking(uint32_t tenant_id, const char *yaml, int reload_cnt)
4206
{
×
4207
    int r = DetectLoaderSetupReloadTenant(tenant_id, yaml, reload_cnt);
×
4208
    if (r < 0)
×
4209
        return r;
×
4210

4211
    if (DetectLoadersSync() != 0)
×
4212
        return -1;
×
4213

4214
    return 0;
×
4215
}
×
4216

4217
/** \brief Reload all tenants and wait for loading to complete
4218
 */
4219
int DetectEngineReloadTenantsBlocking(const int reload_cnt)
UNCOV
4220
{
×
UNCOV
4221
    int r = DetectLoaderSetupReloadTenants(reload_cnt);
×
UNCOV
4222
    if (r < 0)
×
4223
        return r;
×
4224

UNCOV
4225
    if (DetectLoadersSync() != 0)
×
4226
        return -1;
×
4227

UNCOV
4228
    return 0;
×
UNCOV
4229
}
×
4230

4231
static int DetectEngineMultiTenantSetupLoadLivedevMappings(
4232
        const SCConfNode *mappings_root_node, bool failure_fatal)
4233
{
×
4234
    SCConfNode *mapping_node = NULL;
×
4235

4236
    int mapping_cnt = 0;
×
4237
    if (mappings_root_node != NULL) {
×
4238
        TAILQ_FOREACH(mapping_node, &mappings_root_node->head, next) {
×
4239
            SCConfNode *tenant_id_node = SCConfNodeLookupChild(mapping_node, "tenant-id");
×
4240
            if (tenant_id_node == NULL)
×
4241
                goto bad_mapping;
×
4242
            SCConfNode *device_node = SCConfNodeLookupChild(mapping_node, "device");
×
4243
            if (device_node == NULL)
×
4244
                goto bad_mapping;
×
4245

4246
            uint32_t tenant_id = 0;
×
4247
            if (StringParseUint32(&tenant_id, 10, (uint16_t)strlen(tenant_id_node->val),
×
4248
                        tenant_id_node->val) < 0) {
×
4249
                SCLogError("tenant-id  "
×
4250
                           "of %s is invalid",
×
4251
                        tenant_id_node->val);
×
4252
                goto bad_mapping;
×
4253
            }
×
4254

4255
            const char *dev = device_node->val;
×
4256
            LiveDevice *ld = LiveGetDevice(dev);
×
4257
            if (ld == NULL) {
×
4258
                SCLogWarning("device %s not found", dev);
×
4259
                goto bad_mapping;
×
4260
            }
×
4261

4262
            if (ld->tenant_id_set) {
×
4263
                SCLogWarning("device %s already mapped to tenant-id %u", dev, ld->tenant_id);
×
4264
                goto bad_mapping;
×
4265
            }
×
4266

4267
            ld->tenant_id = tenant_id;
×
4268
            ld->tenant_id_set = true;
×
4269

4270
            if (DetectEngineTenantRegisterLivedev(tenant_id, ld->id) != 0) {
×
4271
                goto error;
×
4272
            }
×
4273

4274
            SCLogConfig("device %s connected to tenant-id %u", dev, tenant_id);
×
4275
            mapping_cnt++;
×
4276
            continue;
×
4277

4278
        bad_mapping:
×
4279
            if (failure_fatal)
×
4280
                goto error;
×
4281
        }
×
4282
    }
×
4283
    SCLogConfig("%d device - tenant-id mappings defined", mapping_cnt);
×
4284
    return mapping_cnt;
×
4285

4286
error:
×
4287
    return 0;
×
4288
}
×
4289

4290
static int DetectEngineMultiTenantSetupLoadVlanMappings(
4291
        const SCConfNode *mappings_root_node, bool failure_fatal)
UNCOV
4292
{
×
UNCOV
4293
    SCConfNode *mapping_node = NULL;
×
4294

UNCOV
4295
    int mapping_cnt = 0;
×
UNCOV
4296
    if (mappings_root_node != NULL) {
×
UNCOV
4297
        TAILQ_FOREACH(mapping_node, &mappings_root_node->head, next) {
×
UNCOV
4298
            SCConfNode *tenant_id_node = SCConfNodeLookupChild(mapping_node, "tenant-id");
×
UNCOV
4299
            if (tenant_id_node == NULL)
×
4300
                goto bad_mapping;
×
UNCOV
4301
            SCConfNode *vlan_id_node = SCConfNodeLookupChild(mapping_node, "vlan-id");
×
UNCOV
4302
            if (vlan_id_node == NULL)
×
4303
                goto bad_mapping;
×
4304

UNCOV
4305
            uint32_t tenant_id = 0;
×
UNCOV
4306
            if (StringParseUint32(&tenant_id, 10, (uint16_t)strlen(tenant_id_node->val),
×
UNCOV
4307
                        tenant_id_node->val) < 0) {
×
4308
                SCLogError("tenant-id  "
×
4309
                           "of %s is invalid",
×
4310
                        tenant_id_node->val);
×
4311
                goto bad_mapping;
×
4312
            }
×
4313

UNCOV
4314
            uint16_t vlan_id = 0;
×
UNCOV
4315
            if (StringParseUint16(
×
UNCOV
4316
                        &vlan_id, 10, (uint16_t)strlen(vlan_id_node->val), vlan_id_node->val) < 0) {
×
4317
                SCLogError("vlan-id  "
×
4318
                           "of %s is invalid",
×
4319
                        vlan_id_node->val);
×
4320
                goto bad_mapping;
×
4321
            }
×
UNCOV
4322
            if (vlan_id == 0 || vlan_id >= 4095) {
×
4323
                SCLogError("vlan-id  "
×
4324
                           "of %s is invalid. Valid range 1-4094.",
×
4325
                        vlan_id_node->val);
×
4326
                goto bad_mapping;
×
4327
            }
×
4328

UNCOV
4329
            if (DetectEngineTenantRegisterVlanId(tenant_id, vlan_id) != 0) {
×
4330
                goto error;
×
4331
            }
×
UNCOV
4332
            SCLogConfig("vlan %u connected to tenant-id %u", vlan_id, tenant_id);
×
UNCOV
4333
            mapping_cnt++;
×
UNCOV
4334
            continue;
×
4335

4336
        bad_mapping:
×
4337
            if (failure_fatal)
×
4338
                goto error;
×
4339
        }
×
UNCOV
4340
    }
×
UNCOV
4341
    return mapping_cnt;
×
4342

4343
error:
×
4344
    return 0;
×
UNCOV
4345
}
×
4346

4347
/**
4348
 *  \brief setup multi-detect / multi-tenancy
4349
 *
4350
 *  See if MT is enabled. If so, setup the selector, tenants and mappings.
4351
 *  Tenants and mappings are optional, and can also dynamically be added
4352
 *  and removed from the unix socket.
4353
 */
4354
int DetectEngineMultiTenantSetup(const bool unix_socket)
4355
{
1✔
4356
    enum DetectEngineTenantSelectors tenant_selector = TENANT_SELECTOR_UNKNOWN;
1✔
4357
    DetectEngineMasterCtx *master = &g_master_de_ctx;
1✔
4358
    int failure_fatal = 0;
1✔
4359
    (void)SCConfGetBool("engine.init-failure-fatal", &failure_fatal);
1✔
4360

4361
    int enabled = 0;
1✔
4362
    (void)SCConfGetBool("multi-detect.enabled", &enabled);
1✔
4363
    if (enabled == 1) {
1✔
UNCOV
4364
        DetectLoadersInit();
×
UNCOV
4365
        TmModuleDetectLoaderRegister();
×
UNCOV
4366
        DetectLoaderThreadSpawn();
×
UNCOV
4367
        TmThreadContinueDetectLoaderThreads();
×
4368

UNCOV
4369
        SCMutexLock(&master->lock);
×
UNCOV
4370
        master->multi_tenant_enabled = 1;
×
4371

UNCOV
4372
        const char *handler = NULL;
×
UNCOV
4373
        if (SCConfGet("multi-detect.selector", &handler) == 1) {
×
UNCOV
4374
            SCLogConfig("multi-tenant selector type %s", handler);
×
4375

UNCOV
4376
            if (strcmp(handler, "vlan") == 0) {
×
UNCOV
4377
                tenant_selector = master->tenant_selector = TENANT_SELECTOR_VLAN;
×
4378

UNCOV
4379
                int vlanbool = 0;
×
UNCOV
4380
                if ((SCConfGetBool("vlan.use-for-tracking", &vlanbool)) == 1 && vlanbool == 0) {
×
4381
                    SCLogError("vlan tracking is disabled, "
×
4382
                               "can't use multi-detect selector 'vlan'");
×
4383
                    SCMutexUnlock(&master->lock);
×
4384
                    goto error;
×
4385
                }
×
4386

UNCOV
4387
            } else if (strcmp(handler, "direct") == 0) {
×
4388
                tenant_selector = master->tenant_selector = TENANT_SELECTOR_DIRECT;
×
4389
            } else if (strcmp(handler, "device") == 0) {
×
4390
                tenant_selector = master->tenant_selector = TENANT_SELECTOR_LIVEDEV;
×
4391
                if (EngineModeIsIPS()) {
×
4392
                    SCLogWarning("multi-tenant 'device' mode not supported for IPS");
×
4393
                    SCMutexUnlock(&master->lock);
×
4394
                    goto error;
×
4395
                }
×
4396

4397
            } else {
×
4398
                SCLogError("unknown value %s "
×
4399
                           "multi-detect.selector",
×
4400
                        handler);
×
4401
                SCMutexUnlock(&master->lock);
×
4402
                goto error;
×
4403
            }
×
UNCOV
4404
        }
×
UNCOV
4405
        SCMutexUnlock(&master->lock);
×
UNCOV
4406
        SCLogConfig("multi-detect is enabled (multi tenancy). Selector: %s", handler);
×
4407

4408
        /* traffic -- tenant mappings */
UNCOV
4409
        SCConfNode *mappings_root_node = SCConfGetNode("multi-detect.mappings");
×
4410

UNCOV
4411
        if (tenant_selector == TENANT_SELECTOR_VLAN) {
×
UNCOV
4412
            int mapping_cnt = DetectEngineMultiTenantSetupLoadVlanMappings(mappings_root_node,
×
UNCOV
4413
                    failure_fatal);
×
UNCOV
4414
            if (mapping_cnt == 0) {
×
4415
                /* no mappings are valid when we're in unix socket mode,
4416
                 * they can be added on the fly. Otherwise warn/error
4417
                 * depending on failure_fatal */
4418

4419
                if (unix_socket) {
×
4420
                    SCLogNotice("no tenant traffic mappings defined, "
×
4421
                            "tenants won't be used until mappings are added");
×
4422
                } else {
×
4423
                    if (failure_fatal) {
×
4424
                        SCLogError("no multi-detect mappings defined");
×
4425
                        goto error;
×
4426
                    } else {
×
4427
                        SCLogWarning("no multi-detect mappings defined");
×
4428
                    }
×
4429
                }
×
4430
            }
×
UNCOV
4431
        } else if (tenant_selector == TENANT_SELECTOR_LIVEDEV) {
×
4432
            int mapping_cnt = DetectEngineMultiTenantSetupLoadLivedevMappings(mappings_root_node,
×
4433
                    failure_fatal);
×
4434
            if (mapping_cnt == 0) {
×
4435
                if (failure_fatal) {
×
4436
                    SCLogError("no multi-detect mappings defined");
×
4437
                    goto error;
×
4438
                } else {
×
4439
                    SCLogWarning("no multi-detect mappings defined");
×
4440
                }
×
4441
            }
×
4442
        }
×
4443

4444
        /* tenants */
UNCOV
4445
        SCConfNode *tenants_root_node = SCConfGetNode("multi-detect.tenants");
×
UNCOV
4446
        SCConfNode *tenant_node = NULL;
×
4447

UNCOV
4448
        if (tenants_root_node != NULL) {
×
UNCOV
4449
            const char *path = NULL;
×
UNCOV
4450
            SCConfNode *path_node = SCConfGetNode("multi-detect.config-path");
×
UNCOV
4451
            if (path_node) {
×
UNCOV
4452
                path = path_node->val;
×
UNCOV
4453
                SCLogConfig("tenants config path: %s", path);
×
UNCOV
4454
            }
×
4455

UNCOV
4456
            TAILQ_FOREACH(tenant_node, &tenants_root_node->head, next) {
×
UNCOV
4457
                SCConfNode *id_node = SCConfNodeLookupChild(tenant_node, "id");
×
UNCOV
4458
                if (id_node == NULL) {
×
4459
                    goto bad_tenant;
×
4460
                }
×
UNCOV
4461
                SCConfNode *yaml_node = SCConfNodeLookupChild(tenant_node, "yaml");
×
UNCOV
4462
                if (yaml_node == NULL) {
×
4463
                    goto bad_tenant;
×
4464
                }
×
4465

UNCOV
4466
                uint32_t tenant_id = 0;
×
UNCOV
4467
                if (StringParseUint32(
×
UNCOV
4468
                            &tenant_id, 10, (uint16_t)strlen(id_node->val), id_node->val) < 0) {
×
4469
                    SCLogError("tenant_id  "
×
4470
                               "of %s is invalid",
×
4471
                            id_node->val);
×
4472
                    goto bad_tenant;
×
4473
                }
×
UNCOV
4474
                SCLogDebug("tenant id: %u, %s", tenant_id, yaml_node->val);
×
4475

UNCOV
4476
                char yaml_path[PATH_MAX] = "";
×
UNCOV
4477
                if (path) {
×
UNCOV
4478
                    if (PathMerge(yaml_path, PATH_MAX, path, yaml_node->val) < 0)
×
4479
                        goto bad_tenant;
×
UNCOV
4480
                } else {
×
UNCOV
4481
                    size_t r = strlcpy(yaml_path, yaml_node->val, sizeof(yaml_path));
×
UNCOV
4482
                    if (r >= sizeof(yaml_path))
×
4483
                        goto bad_tenant;
×
UNCOV
4484
                }
×
UNCOV
4485
                SCLogDebug("tenant path: %s", yaml_path);
×
4486

4487
                /* setup the yaml in this loop so that it's not done by the loader
4488
                 * threads. SCConfYamlLoadFileWithPrefix is not thread safe. */
UNCOV
4489
                char prefix[64];
×
UNCOV
4490
                snprintf(prefix, sizeof(prefix), "multi-detect.%u", tenant_id);
×
UNCOV
4491
                if (SCConfYamlLoadFileWithPrefix(yaml_path, prefix) != 0) {
×
4492
                    SCLogError("failed to load yaml %s", yaml_path);
×
4493
                    goto bad_tenant;
×
4494
                }
×
4495

UNCOV
4496
                int r = DetectLoaderSetupLoadTenant(tenant_id, yaml_path);
×
UNCOV
4497
                if (r < 0) {
×
4498
                    /* error logged already */
4499
                    goto bad_tenant;
×
4500
                }
×
UNCOV
4501
                continue;
×
4502

UNCOV
4503
            bad_tenant:
×
4504
                if (failure_fatal)
×
4505
                    goto error;
×
4506
            }
×
UNCOV
4507
        }
×
4508

4509
        /* wait for our loaders to complete their tasks */
UNCOV
4510
        if (DetectLoadersSync() != 0) {
×
4511
            goto error;
×
4512
        }
×
4513

UNCOV
4514
        VarNameStoreActivate();
×
4515

4516
    } else {
1✔
4517
        SCLogDebug("multi-detect not enabled (multi tenancy)");
1✔
4518
    }
1✔
4519
    return 0;
1✔
4520
error:
×
4521
    return -1;
×
4522
}
1✔
4523

4524
static uint32_t DetectEngineTenantGetIdFromVlanId(const void *ctx, const Packet *p)
UNCOV
4525
{
×
UNCOV
4526
    const DetectEngineThreadCtx *det_ctx = ctx;
×
UNCOV
4527
    uint32_t x = 0;
×
UNCOV
4528
    uint32_t vlan_id = 0;
×
4529

UNCOV
4530
    if (p->vlan_idx == 0)
×
UNCOV
4531
        return 0;
×
4532

UNCOV
4533
    vlan_id = p->vlan_id[0];
×
4534

UNCOV
4535
    if (det_ctx == NULL || det_ctx->tenant_array == NULL || det_ctx->tenant_array_size == 0)
×
4536
        return 0;
×
4537

4538
    /* not very efficient, but for now we're targeting only limited amounts.
4539
     * Can use hash/tree approach later. */
UNCOV
4540
    for (x = 0; x < det_ctx->tenant_array_size; x++) {
×
UNCOV
4541
        if (det_ctx->tenant_array[x].traffic_id == vlan_id)
×
4542
            return det_ctx->tenant_array[x].tenant_id;
×
UNCOV
4543
    }
×
4544

UNCOV
4545
    return 0;
×
UNCOV
4546
}
×
4547

4548
static uint32_t DetectEngineTenantGetIdFromLivedev(const void *ctx, const Packet *p)
4549
{
×
4550
    const DetectEngineThreadCtx *det_ctx = ctx;
×
4551
    const LiveDevice *ld = p->livedev;
×
4552

4553
    if (ld == NULL || det_ctx == NULL)
×
4554
        return 0;
×
4555

4556
    SCLogDebug("using tenant-id %u for packet on device %s", ld->tenant_id, ld->dev);
×
4557
    return ld->tenant_id;
×
4558
}
×
4559

4560
static int DetectEngineTenantRegisterSelector(
4561
        enum DetectEngineTenantSelectors selector, uint32_t tenant_id, uint32_t traffic_id)
UNCOV
4562
{
×
UNCOV
4563
    DetectEngineMasterCtx *master = &g_master_de_ctx;
×
UNCOV
4564
    SCMutexLock(&master->lock);
×
4565

UNCOV
4566
    if (!(master->tenant_selector == TENANT_SELECTOR_UNKNOWN || master->tenant_selector == selector)) {
×
4567
        SCLogInfo("conflicting selector already set");
×
4568
        SCMutexUnlock(&master->lock);
×
4569
        return -1;
×
4570
    }
×
4571

UNCOV
4572
    DetectEngineTenantMapping *m = master->tenant_mapping_list;
×
UNCOV
4573
    while (m) {
×
UNCOV
4574
        if (m->traffic_id == traffic_id) {
×
4575
            SCLogInfo("traffic id already registered");
×
4576
            SCMutexUnlock(&master->lock);
×
4577
            return -1;
×
4578
        }
×
UNCOV
4579
        m = m->next;
×
UNCOV
4580
    }
×
4581

UNCOV
4582
    DetectEngineTenantMapping *map = SCCalloc(1, sizeof(*map));
×
UNCOV
4583
    if (map == NULL) {
×
4584
        SCLogInfo("memory fail");
×
4585
        SCMutexUnlock(&master->lock);
×
4586
        return -1;
×
4587
    }
×
UNCOV
4588
    map->traffic_id = traffic_id;
×
UNCOV
4589
    map->tenant_id = tenant_id;
×
4590

UNCOV
4591
    map->next = master->tenant_mapping_list;
×
UNCOV
4592
    master->tenant_mapping_list = map;
×
4593

UNCOV
4594
    master->tenant_selector = selector;
×
4595

UNCOV
4596
    SCLogDebug("tenant handler %u %u %u registered", selector, tenant_id, traffic_id);
×
UNCOV
4597
    SCMutexUnlock(&master->lock);
×
UNCOV
4598
    return 0;
×
UNCOV
4599
}
×
4600

4601
static int DetectEngineTenantUnregisterSelector(
4602
        enum DetectEngineTenantSelectors selector, uint32_t tenant_id, uint32_t traffic_id)
4603
{
×
4604
    DetectEngineMasterCtx *master = &g_master_de_ctx;
×
4605
    SCMutexLock(&master->lock);
×
4606

4607
    if (master->tenant_mapping_list == NULL) {
×
4608
        SCMutexUnlock(&master->lock);
×
4609
        return -1;
×
4610
    }
×
4611

4612
    DetectEngineTenantMapping *prev = NULL;
×
4613
    DetectEngineTenantMapping *map = master->tenant_mapping_list;
×
4614
    while (map) {
×
4615
        if (map->traffic_id == traffic_id &&
×
4616
            map->tenant_id == tenant_id)
×
4617
        {
×
4618
            if (prev != NULL)
×
4619
                prev->next = map->next;
×
4620
            else
×
4621
                master->tenant_mapping_list = map->next;
×
4622

4623
            map->next = NULL;
×
4624
            SCFree(map);
×
4625
            SCLogInfo("tenant handler %u %u %u unregistered", selector, tenant_id, traffic_id);
×
4626
            SCMutexUnlock(&master->lock);
×
4627
            return 0;
×
4628
        }
×
4629
        prev = map;
×
4630
        map = map->next;
×
4631
    }
×
4632

4633
    SCMutexUnlock(&master->lock);
×
4634
    return -1;
×
4635
}
×
4636

4637
int DetectEngineTenantRegisterLivedev(uint32_t tenant_id, int device_id)
4638
{
×
4639
    return DetectEngineTenantRegisterSelector(
×
4640
            TENANT_SELECTOR_LIVEDEV, tenant_id, (uint32_t)device_id);
×
4641
}
×
4642

4643
int DetectEngineTenantRegisterVlanId(uint32_t tenant_id, uint16_t vlan_id)
UNCOV
4644
{
×
UNCOV
4645
    return DetectEngineTenantRegisterSelector(TENANT_SELECTOR_VLAN, tenant_id, (uint32_t)vlan_id);
×
UNCOV
4646
}
×
4647

4648
int DetectEngineTenantUnregisterVlanId(uint32_t tenant_id, uint16_t vlan_id)
4649
{
×
4650
    return DetectEngineTenantUnregisterSelector(TENANT_SELECTOR_VLAN, tenant_id, (uint32_t)vlan_id);
×
4651
}
×
4652

4653
int DetectEngineTenantRegisterPcapFile(uint32_t tenant_id)
4654
{
×
4655
    SCLogInfo("registering %u %d 0", TENANT_SELECTOR_DIRECT, tenant_id);
×
4656
    return DetectEngineTenantRegisterSelector(TENANT_SELECTOR_DIRECT, tenant_id, 0);
×
4657
}
×
4658

4659
int DetectEngineTenantUnregisterPcapFile(uint32_t tenant_id)
4660
{
×
4661
    SCLogInfo("unregistering %u %d 0", TENANT_SELECTOR_DIRECT, tenant_id);
×
4662
    return DetectEngineTenantUnregisterSelector(TENANT_SELECTOR_DIRECT, tenant_id, 0);
×
4663
}
×
4664

4665
static uint32_t DetectEngineTenantGetIdFromPcap(const void *ctx, const Packet *p)
4666
{
×
4667
    return p->pcap_v.tenant_id;
×
4668
}
×
4669

4670
DetectEngineCtx *DetectEngineGetByTenantId(uint32_t tenant_id)
UNCOV
4671
{
×
UNCOV
4672
    DetectEngineMasterCtx *master = &g_master_de_ctx;
×
UNCOV
4673
    SCMutexLock(&master->lock);
×
4674

UNCOV
4675
    if (master->list == NULL) {
×
UNCOV
4676
        SCMutexUnlock(&master->lock);
×
UNCOV
4677
        return NULL;
×
UNCOV
4678
    }
×
4679

UNCOV
4680
    DetectEngineCtx *de_ctx = master->list;
×
UNCOV
4681
    while (de_ctx) {
×
UNCOV
4682
        if (de_ctx->type == DETECT_ENGINE_TYPE_TENANT &&
×
UNCOV
4683
                de_ctx->tenant_id == tenant_id)
×
UNCOV
4684
        {
×
UNCOV
4685
            de_ctx->ref_cnt++;
×
UNCOV
4686
            break;
×
UNCOV
4687
        }
×
4688

UNCOV
4689
        de_ctx = de_ctx->next;
×
UNCOV
4690
    }
×
4691

UNCOV
4692
    SCMutexUnlock(&master->lock);
×
UNCOV
4693
    return de_ctx;
×
UNCOV
4694
}
×
4695

4696
void DetectEngineDeReference(DetectEngineCtx **de_ctx)
4697
{
106,312✔
4698
    DEBUG_VALIDATE_BUG_ON((*de_ctx)->ref_cnt == 0);
106,312✔
4699
    (*de_ctx)->ref_cnt--;
106,312✔
4700
    *de_ctx = NULL;
106,312✔
4701
}
106,312✔
4702

4703
static int DetectEngineAddToList(DetectEngineCtx *instance)
4704
{
35,438✔
4705
    DetectEngineMasterCtx *master = &g_master_de_ctx;
35,438✔
4706

4707
    if (instance == NULL)
35,438✔
4708
        return -1;
×
4709

4710
    if (master->list == NULL) {
35,438✔
4711
        master->list = instance;
1✔
4712
    } else {
35,437✔
4713
        instance->next = master->list;
35,437✔
4714
        master->list = instance;
35,437✔
4715
    }
35,437✔
4716

4717
    return 0;
35,438✔
4718
}
35,438✔
4719

4720
int DetectEngineAddToMaster(DetectEngineCtx *de_ctx)
4721
{
35,438✔
4722
    int r;
35,438✔
4723

4724
    if (de_ctx == NULL)
35,438✔
4725
        return -1;
×
4726

4727
    SCLogDebug("adding de_ctx %p to master", de_ctx);
35,438✔
4728

4729
    DetectEngineMasterCtx *master = &g_master_de_ctx;
35,438✔
4730
    SCMutexLock(&master->lock);
35,438✔
4731
    r = DetectEngineAddToList(de_ctx);
35,438✔
4732
    SCMutexUnlock(&master->lock);
35,438✔
4733
    return r;
35,438✔
4734
}
35,438✔
4735

4736
static int DetectEngineMoveToFreeListNoLock(DetectEngineMasterCtx *master, DetectEngineCtx *de_ctx)
4737
{
35,437✔
4738
    DetectEngineCtx *instance = master->list;
35,437✔
4739
    if (instance == NULL) {
35,437✔
4740
        return -1;
×
4741
    }
×
4742

4743
    /* remove from active list */
4744
    if (instance == de_ctx) {
35,437✔
UNCOV
4745
        master->list = instance->next;
×
4746
    } else {
35,437✔
4747
        DetectEngineCtx *prev = instance;
35,437✔
4748
        instance = instance->next; /* already checked first element */
35,437✔
4749

4750
        while (instance) {
35,437✔
4751
            DetectEngineCtx *next = instance->next;
35,437✔
4752

4753
            if (instance == de_ctx) {
35,437✔
4754
                prev->next = instance->next;
35,437✔
4755
                break;
35,437✔
4756
            }
35,437✔
4757

UNCOV
4758
            prev = instance;
×
UNCOV
4759
            instance = next;
×
UNCOV
4760
        }
×
4761
        if (instance == NULL) {
35,437✔
4762
            return -1;
×
4763
        }
×
4764
    }
35,437✔
4765

4766
    /* instance is now detached from list */
4767
    instance->next = NULL;
35,437✔
4768

4769
    /* add to free list */
4770
    if (master->free_list == NULL) {
35,437✔
4771
        master->free_list = instance;
1✔
4772
    } else {
35,436✔
4773
        instance->next = master->free_list;
35,436✔
4774
        master->free_list = instance;
35,436✔
4775
    }
35,436✔
4776
    SCLogDebug("detect engine %p moved to free list (%u refs)", de_ctx, de_ctx->ref_cnt);
35,437✔
4777
    return 0;
35,437✔
4778
}
35,437✔
4779

4780
int DetectEngineMoveToFreeList(DetectEngineCtx *de_ctx)
4781
{
35,437✔
4782
    int ret = 0;
35,437✔
4783
    DetectEngineMasterCtx *master = &g_master_de_ctx;
35,437✔
4784
    SCMutexLock(&master->lock);
35,437✔
4785
    ret = DetectEngineMoveToFreeListNoLock(master, de_ctx);
35,437✔
4786
    SCMutexUnlock(&master->lock);
35,437✔
4787
    return ret;
35,437✔
4788
}
35,437✔
4789

4790
void DetectEnginePruneFreeList(void)
4791
{
35,437✔
4792
    DetectEngineMasterCtx *master = &g_master_de_ctx;
35,437✔
4793
    SCMutexLock(&master->lock);
35,437✔
4794

4795
    DetectEngineCtx *prev = NULL;
35,437✔
4796
    DetectEngineCtx *instance = master->free_list;
35,437✔
4797
    while (instance) {
106,310✔
4798
        DetectEngineCtx *next = instance->next;
70,873✔
4799

4800
        SCLogDebug("detect engine %p has %u ref(s)", instance, instance->ref_cnt);
70,873✔
4801

4802
        if (instance->ref_cnt == 0) {
70,873✔
4803
            if (prev == NULL) {
35,436✔
UNCOV
4804
                master->free_list = next;
×
4805
            } else {
35,436✔
4806
                prev->next = next;
35,436✔
4807
            }
35,436✔
4808

4809
            SCLogDebug("freeing detect engine %p", instance);
35,436✔
4810
            DetectEngineCtxFree(instance);
35,436✔
4811
            instance = NULL;
35,436✔
4812
        }
35,436✔
4813

4814
        prev = instance;
70,873✔
4815
        instance = next;
70,873✔
4816
    }
70,873✔
4817
    SCMutexUnlock(&master->lock);
35,437✔
4818
}
35,437✔
4819

4820
void DetectEngineClearMaster(void)
UNCOV
4821
{
×
UNCOV
4822
    DetectEngineMasterCtx *master = &g_master_de_ctx;
×
UNCOV
4823
    SCMutexLock(&master->lock);
×
4824

UNCOV
4825
    DetectEngineCtx *instance = master->list;
×
UNCOV
4826
    while (instance) {
×
UNCOV
4827
        DetectEngineCtx *next = instance->next;
×
UNCOV
4828
        DEBUG_VALIDATE_BUG_ON(instance->ref_cnt);
×
UNCOV
4829
        SCLogDebug("detect engine %p has %u ref(s)", instance, instance->ref_cnt);
×
UNCOV
4830
        instance->ref_cnt = 0;
×
UNCOV
4831
        DetectEngineMoveToFreeListNoLock(master, instance);
×
UNCOV
4832
        instance = next;
×
UNCOV
4833
    }
×
UNCOV
4834
    SCMutexUnlock(&master->lock);
×
UNCOV
4835
    DetectEnginePruneFreeList();
×
UNCOV
4836
}
×
4837

4838
static int reloads = 0;
4839

4840
/** \brief Reload the detection engine
4841
 *
4842
 *  \param filename YAML file to load for the detect config
4843
 *
4844
 *  \retval -1 error
4845
 *  \retval 0 ok
4846
 */
4847
int DetectEngineReload(const SCInstance *suri)
4848
{
35,437✔
4849
    DetectEngineCtx *new_de_ctx = NULL;
35,437✔
4850
    DetectEngineCtx *old_de_ctx = NULL;
35,437✔
4851

4852
    char prefix[128];
35,437✔
4853
    memset(prefix, 0, sizeof(prefix));
35,437✔
4854

4855
    SCLogNotice("rule reload starting");
35,437✔
4856

4857
    if (suri->conf_filename != NULL) {
35,437✔
UNCOV
4858
        snprintf(prefix, sizeof(prefix), "detect-engine-reloads.%d", reloads++);
×
UNCOV
4859
        SCLogConfig("Reloading %s", suri->conf_filename);
×
UNCOV
4860
        if (SCConfYamlLoadFileWithPrefix(suri->conf_filename, prefix) != 0) {
×
4861
            SCLogError("failed to load yaml %s", suri->conf_filename);
×
4862
            return -1;
×
4863
        }
×
4864

UNCOV
4865
        SCConfNode *node = SCConfGetNode(prefix);
×
UNCOV
4866
        if (node == NULL) {
×
4867
            SCLogError("failed to properly setup yaml %s", suri->conf_filename);
×
4868
            return -1;
×
4869
        }
×
4870

UNCOV
4871
        if (suri->additional_configs) {
×
4872
            for (int i = 0; suri->additional_configs[i] != NULL; i++) {
×
4873
                SCLogConfig("Reloading %s", suri->additional_configs[i]);
×
4874
                SCConfYamlHandleInclude(node, suri->additional_configs[i]);
×
4875
            }
×
4876
        }
×
4877

4878
#if 0
4879
        SCConfDump();
4880
#endif
UNCOV
4881
    }
×
4882

4883
    /* get a reference to the current de_ctx */
4884
    old_de_ctx = DetectEngineGetCurrent();
35,437✔
4885
    if (old_de_ctx == NULL)
35,437✔
4886
        return -1;
×
4887
    SCLogDebug("get ref to old_de_ctx %p", old_de_ctx);
35,437✔
4888
    DatasetReload();
35,437✔
4889

4890
    /* only reload a regular 'normal' and 'delayed detect stub' detect engines */
4891
    if (!(old_de_ctx->type == DETECT_ENGINE_TYPE_NORMAL ||
35,437✔
4892
          old_de_ctx->type == DETECT_ENGINE_TYPE_DD_STUB))
35,437✔
4893
    {
×
4894
        DetectEngineDeReference(&old_de_ctx);
×
4895
        SCLogNotice("rule reload complete");
×
4896
        return -1;
×
4897
    }
×
4898

4899
    /* get new detection engine */
4900
    new_de_ctx = DetectEngineCtxInitWithPrefix(prefix, old_de_ctx->tenant_id);
35,437✔
4901
    if (new_de_ctx == NULL) {
35,437✔
4902
        SCLogError("initializing detection engine "
×
4903
                   "context failed.");
×
4904
        DetectEngineDeReference(&old_de_ctx);
×
4905
        return -1;
×
4906
    }
×
4907
    if (SigLoadSignatures(new_de_ctx,
35,437✔
4908
                          suri->sig_file, suri->sig_file_exclusive) != 0) {
35,437✔
4909
        DetectEngineCtxFree(new_de_ctx);
×
4910
        DetectEngineDeReference(&old_de_ctx);
×
4911
        return -1;
×
4912
    }
×
4913
    SCLogDebug("set up new_de_ctx %p", new_de_ctx);
35,437✔
4914

4915
    /* Copy over callbacks. */
4916
    new_de_ctx->RateFilterCallback = old_de_ctx->RateFilterCallback;
35,437✔
4917
    new_de_ctx->rate_filter_callback_arg = old_de_ctx->rate_filter_callback_arg;
35,437✔
4918

4919
    /* add to master */
4920
    DetectEngineAddToMaster(new_de_ctx);
35,437✔
4921

4922
    /* move to old free list */
4923
    DetectEngineMoveToFreeList(old_de_ctx);
35,437✔
4924
    DetectEngineDeReference(&old_de_ctx);
35,437✔
4925

4926
    SCLogDebug("going to reload the threads to use new_de_ctx %p", new_de_ctx);
35,437✔
4927

4928
    DetectEngineMasterCtx *master = &g_master_de_ctx;
35,437✔
4929
    SCMutexLock(&master->lock);
35,437✔
4930
    /* update the threads */
4931
    DetectEngineReloadThreads(new_de_ctx);
35,437✔
4932
    SCMutexUnlock(&master->lock);
35,437✔
4933

4934
    SCLogDebug("threads now run new_de_ctx %p", new_de_ctx);
35,437✔
4935

4936
    /* walk free list, freeing the old_de_ctx */
4937
    DetectEnginePruneFreeList();
35,437✔
4938

4939
    DatasetPostReloadCleanup();
35,437✔
4940

4941
    DetectEngineBumpVersion();
35,437✔
4942

4943
    SCLogDebug("old_de_ctx should have been freed");
35,437✔
4944

4945
    DetectEngineMpmCacheService(DETECT_ENGINE_MPM_CACHE_OP_SAVE | DETECT_ENGINE_MPM_CACHE_OP_PRUNE);
35,437✔
4946

4947
    SCLogNotice("rule reload complete");
35,437✔
4948

4949
#ifdef HAVE_MALLOC_TRIM
35,437✔
4950
    /* The reload process potentially frees up large amounts of memory.
4951
     * Encourage the memory management system to reclaim as much as it
4952
     * can.
4953
     */
4954
    malloc_trim(0);
35,437✔
4955
#endif
35,437✔
4956

4957
    return 0;
35,437✔
4958
}
35,437✔
4959

4960
static uint32_t TenantIdHash(HashTable *h, void *data, uint16_t data_len)
UNCOV
4961
{
×
UNCOV
4962
    DetectEngineThreadCtx *det_ctx = (DetectEngineThreadCtx *)data;
×
UNCOV
4963
    return det_ctx->tenant_id % h->array_size;
×
UNCOV
4964
}
×
4965

4966
static char TenantIdCompare(void *d1, uint16_t d1_len, void *d2, uint16_t d2_len)
4967
{
×
4968
    DetectEngineThreadCtx *det1 = (DetectEngineThreadCtx *)d1;
×
4969
    DetectEngineThreadCtx *det2 = (DetectEngineThreadCtx *)d2;
×
4970
    return (det1->tenant_id == det2->tenant_id);
×
4971
}
×
4972

4973
static void TenantIdFree(void *d)
UNCOV
4974
{
×
UNCOV
4975
    DetectEngineThreadCtxFree(d);
×
UNCOV
4976
}
×
4977

4978
int DetectEngineMTApply(void)
UNCOV
4979
{
×
UNCOV
4980
    DetectEngineMasterCtx *master = &g_master_de_ctx;
×
UNCOV
4981
    SCMutexLock(&master->lock);
×
4982

UNCOV
4983
    if (master->tenant_selector == TENANT_SELECTOR_UNKNOWN) {
×
4984
        SCLogInfo("error, no tenant selector");
×
4985
        SCMutexUnlock(&master->lock);
×
4986
        return -1;
×
4987
    }
×
4988

UNCOV
4989
    DetectEngineCtx *stub_de_ctx = NULL;
×
UNCOV
4990
    DetectEngineCtx *list = master->list;
×
UNCOV
4991
    for ( ; list != NULL; list = list->next) {
×
UNCOV
4992
        SCLogDebug("list %p tenant %u", list, list->tenant_id);
×
4993

UNCOV
4994
        if (list->type == DETECT_ENGINE_TYPE_NORMAL ||
×
UNCOV
4995
            list->type == DETECT_ENGINE_TYPE_MT_STUB ||
×
UNCOV
4996
            list->type == DETECT_ENGINE_TYPE_DD_STUB)
×
UNCOV
4997
        {
×
UNCOV
4998
            stub_de_ctx = list;
×
UNCOV
4999
            break;
×
UNCOV
5000
        }
×
UNCOV
5001
    }
×
UNCOV
5002
    if (stub_de_ctx == NULL) {
×
5003
        stub_de_ctx = DetectEngineCtxInitStubForMT();
×
5004
        if (stub_de_ctx == NULL) {
×
5005
            SCMutexUnlock(&master->lock);
×
5006
            return -1;
×
5007
        }
×
5008

5009
        if (master->list == NULL) {
×
5010
            master->list = stub_de_ctx;
×
5011
        } else {
×
5012
            stub_de_ctx->next = master->list;
×
5013
            master->list = stub_de_ctx;
×
5014
        }
×
5015
    }
×
5016

5017
    /* update the threads */
UNCOV
5018
    SCLogDebug("MT reload starting");
×
UNCOV
5019
    DetectEngineReloadThreads(stub_de_ctx);
×
UNCOV
5020
    SCLogDebug("MT reload done");
×
5021

UNCOV
5022
    SCMutexUnlock(&master->lock);
×
5023

5024
    /* walk free list, freeing the old_de_ctx */
UNCOV
5025
    DetectEnginePruneFreeList();
×
5026
    // needed for VarNameStoreFree
UNCOV
5027
    DetectEngineBumpVersion();
×
5028

UNCOV
5029
    SCLogDebug("old_de_ctx should have been freed");
×
UNCOV
5030
    return 0;
×
UNCOV
5031
}
×
5032

5033
static int g_parse_metadata = 0;
5034

5035
void DetectEngineSetParseMetadata(void)
5036
{
1✔
5037
    g_parse_metadata = 1;
1✔
5038
}
1✔
5039

5040
void DetectEngineUnsetParseMetadata(void)
UNCOV
5041
{
×
UNCOV
5042
    g_parse_metadata = 0;
×
UNCOV
5043
}
×
5044

5045
int DetectEngineMustParseMetadata(void)
5046
{
73,751✔
5047
    return g_parse_metadata;
73,751✔
5048
}
73,751✔
5049

5050
const char *DetectSigmatchListEnumToString(enum DetectSigmatchListEnum type)
UNCOV
5051
{
×
UNCOV
5052
    switch (type) {
×
5053
        case DETECT_SM_LIST_MATCH:
×
5054
            return "packet";
×
UNCOV
5055
        case DETECT_SM_LIST_PMATCH:
×
UNCOV
5056
            return "packet/stream payload";
×
5057

5058
        case DETECT_SM_LIST_TMATCH:
×
5059
            return "tag";
×
5060

5061
        case DETECT_SM_LIST_BASE64_DATA:
×
5062
            return "base64_data";
×
5063

5064
        case DETECT_SM_LIST_POSTMATCH:
×
5065
            return "post-match";
×
5066

5067
        case DETECT_SM_LIST_SUPPRESS:
×
5068
            return "suppress";
×
5069
        case DETECT_SM_LIST_THRESHOLD:
×
5070
            return "threshold";
×
5071

5072
        case DETECT_SM_LIST_MAX:
×
5073
            return "max (internal)";
×
UNCOV
5074
    }
×
5075
    return "error";
×
UNCOV
5076
}
×
5077

5078
/* events api */
5079
void DetectEngineSetEvent(DetectEngineThreadCtx *det_ctx, uint8_t e)
UNCOV
5080
{
×
UNCOV
5081
    SCAppLayerDecoderEventsSetEventRaw(&det_ctx->decoder_events, e);
×
UNCOV
5082
    det_ctx->events++;
×
UNCOV
5083
}
×
5084

5085
bool DetectMd5ValidateCallback(
5086
        const Signature *s, const char **sigerror, const DetectBufferType *map)
5087
{
10,610✔
5088
    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
16,815✔
5089
        if (s->init_data->buffers[x].id != (uint32_t)map->id)
13,585✔
5090
            continue;
1,839✔
5091
        const SigMatch *sm = s->init_data->buffers[x].head;
11,746✔
5092
        for (; sm != NULL; sm = sm->next) {
20,593✔
5093
            if (sm->type != DETECT_CONTENT)
16,227✔
5094
                continue;
4,831✔
5095

5096
            const DetectContentData *cd = (DetectContentData *)sm->ctx;
11,396✔
5097
            if (cd->flags & DETECT_CONTENT_NOCASE) {
11,396✔
5098
                *sigerror = "md5-like keyword should not be used together with "
569✔
5099
                            "nocase, since the rule is automatically "
569✔
5100
                            "lowercased anyway which makes nocase redundant.";
569✔
5101
                SCLogWarning("rule %u: buffer %s: %s", s->id, map->name, *sigerror);
569✔
5102
            }
569✔
5103

5104
            if (cd->content_len != SC_MD5_HEX_LEN) {
11,396✔
5105
                *sigerror = "Invalid length for md5-like keyword (should "
6,192✔
5106
                            "be 32 characters long). This rule will therefore "
6,192✔
5107
                            "never match.";
6,192✔
5108
                SCLogError("rule %u: buffer %s: %s", s->id, map->name, *sigerror);
6,192✔
5109
                return false;
6,192✔
5110
            }
6,192✔
5111

5112
            for (size_t i = 0; i < cd->content_len; ++i) {
138,044✔
5113
                if (!isxdigit(cd->content[i])) {
134,028✔
5114
                    *sigerror =
1,188✔
5115
                            "Invalid md5-like string (should be string of hexadecimal characters)."
1,188✔
5116
                            "This rule will therefore never match.";
1,188✔
5117
                    SCLogWarning("rule %u: buffer %s: %s", s->id, map->name, *sigerror);
1,188✔
5118
                    return false;
1,188✔
5119
                }
1,188✔
5120
            }
134,028✔
5121
        }
5,204✔
5122
    }
11,746✔
5123
    return true;
3,230✔
5124
}
10,610✔
5125

5126
void DetectLowerSetupCallback(
5127
        const DetectEngineCtx *de_ctx, Signature *s, const DetectBufferType *map)
5128
{
16,938✔
5129
    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
58,918✔
5130
        if (s->init_data->buffers[x].id != (uint32_t)map->id)
41,980✔
5131
            continue;
5,428✔
5132
        SigMatch *sm = s->init_data->buffers[x].head;
36,552✔
5133
        for (; sm != NULL; sm = sm->next) {
104,006✔
5134
            if (sm->type != DETECT_CONTENT)
67,454✔
5135
                continue;
47,215✔
5136

5137
            DetectContentData *cd = (DetectContentData *)sm->ctx;
20,239✔
5138

5139
            bool changed = false;
20,239✔
5140
            uint32_t u;
20,239✔
5141
            for (u = 0; u < cd->content_len; u++) {
968,690✔
5142
                if (isupper(cd->content[u])) {
948,451✔
5143
                    cd->content[u] = u8_tolower(cd->content[u]);
118,649✔
5144
                    changed = true;
118,649✔
5145
                }
118,649✔
5146
            }
948,451✔
5147

5148
            if (changed) {
20,239✔
5149
                SpmDestroyCtx(cd->spm_ctx);
8,720✔
5150
                cd->spm_ctx =
8,720✔
5151
                        SpmInitCtx(cd->content, cd->content_len, 1, de_ctx->spm_global_thread_ctx);
8,720✔
5152
            }
8,720✔
5153
        }
20,239✔
5154
    }
36,552✔
5155
}
16,938✔
5156

5157
void SCDetectEngineRegisterRateFilterCallback(SCDetectRateFilterFunc fn, void *arg)
5158
{
×
5159
    DetectEngineCtx *de_ctx = DetectEngineGetCurrent();
×
5160
    de_ctx->RateFilterCallback = fn;
×
5161
    de_ctx->rate_filter_callback_arg = arg;
×
5162
    DetectEngineDeReference(&de_ctx);
×
5163
}
×
5164

5165
int DetectEngineThreadCtxGetJsonContext(DetectEngineThreadCtx *det_ctx)
UNCOV
5166
{
×
UNCOV
5167
    if (det_ctx->json_content_len > SIG_JSON_CONTENT_ARRAY_LEN - 1) {
×
5168
        SCLogDebug("json content length %u exceeds maximum %u", det_ctx->json_content_len,
×
5169
                SIG_JSON_CONTENT_ARRAY_LEN);
×
5170
        return -1;
×
5171
    }
×
UNCOV
5172
    if (det_ctx->json_content_len >= det_ctx->json_content_capacity) {
×
UNCOV
5173
        if (det_ctx->json_content_capacity == 0) {
×
UNCOV
5174
            det_ctx->json_content_capacity = 1;
×
UNCOV
5175
        } else {
×
UNCOV
5176
            det_ctx->json_content_capacity *= 2;
×
UNCOV
5177
        }
×
UNCOV
5178
        void *tmp = SCRealloc(
×
UNCOV
5179
                det_ctx->json_content, det_ctx->json_content_capacity * sizeof(SigJsonContent));
×
UNCOV
5180
        if (unlikely(tmp == NULL)) {
×
5181
            return -1;
×
5182
        }
×
UNCOV
5183
        SCLogDebug("reallocated json content array to %u items", det_ctx->json_content_capacity);
×
UNCOV
5184
        det_ctx->json_content = tmp;
×
UNCOV
5185
    }
×
UNCOV
5186
    return 0;
×
UNCOV
5187
}
×
5188

5189
/*************************************Unittest*********************************/
5190

5191
#ifdef UNITTESTS
5192

5193
static int DetectEngineInitYamlConf(const char *conf)
5194
{
5195
    SCConfCreateContextBackup();
5196
    SCConfInit();
5197
    return SCConfYamlLoadString(conf, strlen(conf));
5198
}
5199

5200
static void DetectEngineDeInitYamlConf(void)
5201
{
5202
    SCConfDeInit();
5203
    SCConfRestoreContextBackup();
5204
}
5205

5206
static int DetectEngineTest01(void)
5207
{
5208
    const char *conf =
5209
        "%YAML 1.1\n"
5210
        "---\n"
5211
        "detect-engine:\n"
5212
        "  - profile: medium\n"
5213
        "  - custom-values:\n"
5214
        "      toclient_src_groups: 2\n"
5215
        "      toclient_dst_groups: 2\n"
5216
        "      toclient_sp_groups: 2\n"
5217
        "      toclient_dp_groups: 3\n"
5218
        "      toserver_src_groups: 2\n"
5219
        "      toserver_dst_groups: 4\n"
5220
        "      toserver_sp_groups: 2\n"
5221
        "      toserver_dp_groups: 25\n"
5222
        "  - inspection-recursion-limit: 0\n";
5223

5224
    FAIL_IF(DetectEngineInitYamlConf(conf) == -1);
5225

5226
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
5227
    FAIL_IF_NULL(de_ctx);
5228

5229
    FAIL_IF_NOT(de_ctx->inspection_recursion_limit == -1);
5230

5231
    DetectEngineCtxFree(de_ctx);
5232

5233
    DetectEngineDeInitYamlConf();
5234

5235
    PASS;
5236
}
5237

5238
static int DetectEngineTest02(void)
5239
{
5240
    const char *conf =
5241
        "%YAML 1.1\n"
5242
        "---\n"
5243
        "detect-engine:\n"
5244
        "  - profile: medium\n"
5245
        "  - custom-values:\n"
5246
        "      toclient_src_groups: 2\n"
5247
        "      toclient_dst_groups: 2\n"
5248
        "      toclient_sp_groups: 2\n"
5249
        "      toclient_dp_groups: 3\n"
5250
        "      toserver_src_groups: 2\n"
5251
        "      toserver_dst_groups: 4\n"
5252
        "      toserver_sp_groups: 2\n"
5253
        "      toserver_dp_groups: 25\n"
5254
        "  - inspection-recursion-limit:\n";
5255

5256
    FAIL_IF(DetectEngineInitYamlConf(conf) == -1);
5257

5258
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
5259
    FAIL_IF_NULL(de_ctx);
5260

5261
    FAIL_IF_NOT(
5262
            de_ctx->inspection_recursion_limit == DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT);
5263

5264
    DetectEngineCtxFree(de_ctx);
5265

5266
    DetectEngineDeInitYamlConf();
5267

5268
    PASS;
5269
}
5270

5271
static int DetectEngineTest03(void)
5272
{
5273
    const char *conf =
5274
        "%YAML 1.1\n"
5275
        "---\n"
5276
        "detect-engine:\n"
5277
        "  - profile: medium\n"
5278
        "  - custom-values:\n"
5279
        "      toclient_src_groups: 2\n"
5280
        "      toclient_dst_groups: 2\n"
5281
        "      toclient_sp_groups: 2\n"
5282
        "      toclient_dp_groups: 3\n"
5283
        "      toserver_src_groups: 2\n"
5284
        "      toserver_dst_groups: 4\n"
5285
        "      toserver_sp_groups: 2\n"
5286
        "      toserver_dp_groups: 25\n";
5287

5288
    FAIL_IF(DetectEngineInitYamlConf(conf) == -1);
5289

5290
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
5291
    FAIL_IF_NULL(de_ctx);
5292

5293
    FAIL_IF_NOT(
5294
            de_ctx->inspection_recursion_limit == DETECT_ENGINE_DEFAULT_INSPECTION_RECURSION_LIMIT);
5295

5296
    DetectEngineCtxFree(de_ctx);
5297

5298
    DetectEngineDeInitYamlConf();
5299

5300
    PASS;
5301
}
5302

5303
static int DetectEngineTest04(void)
5304
{
5305
    const char *conf =
5306
        "%YAML 1.1\n"
5307
        "---\n"
5308
        "detect-engine:\n"
5309
        "  - profile: medium\n"
5310
        "  - custom-values:\n"
5311
        "      toclient_src_groups: 2\n"
5312
        "      toclient_dst_groups: 2\n"
5313
        "      toclient_sp_groups: 2\n"
5314
        "      toclient_dp_groups: 3\n"
5315
        "      toserver_src_groups: 2\n"
5316
        "      toserver_dst_groups: 4\n"
5317
        "      toserver_sp_groups: 2\n"
5318
        "      toserver_dp_groups: 25\n"
5319
        "  - inspection-recursion-limit: 10\n";
5320

5321
    FAIL_IF(DetectEngineInitYamlConf(conf) == -1);
5322

5323
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
5324
    FAIL_IF_NULL(de_ctx);
5325

5326
    FAIL_IF_NOT(de_ctx->inspection_recursion_limit == 10);
5327

5328
    DetectEngineCtxFree(de_ctx);
5329

5330
    DetectEngineDeInitYamlConf();
5331

5332
    PASS;
5333
}
5334

5335
static int DetectEngineTest08(void)
5336
{
5337
    const char *conf =
5338
        "%YAML 1.1\n"
5339
        "---\n"
5340
        "detect-engine:\n"
5341
        "  - profile: custom\n"
5342
        "  - custom-values:\n"
5343
        "      toclient-groups: 23\n"
5344
        "      toserver-groups: 27\n";
5345

5346
    FAIL_IF(DetectEngineInitYamlConf(conf) == -1);
5347

5348
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
5349
    FAIL_IF_NULL(de_ctx);
5350

5351
    FAIL_IF_NOT(de_ctx->max_uniq_toclient_groups == 23);
5352
    FAIL_IF_NOT(de_ctx->max_uniq_toserver_groups == 27);
5353

5354
    DetectEngineCtxFree(de_ctx);
5355

5356
    DetectEngineDeInitYamlConf();
5357

5358
    PASS;
5359
}
5360

5361
/** \test bug 892 bad values */
5362
static int DetectEngineTest09(void)
5363
{
5364
    const char *conf =
5365
        "%YAML 1.1\n"
5366
        "---\n"
5367
        "detect-engine:\n"
5368
        "  - profile: custom\n"
5369
        "  - custom-values:\n"
5370
        "      toclient-groups: BA\n"
5371
        "      toserver-groups: BA\n"
5372
        "  - inspection-recursion-limit: 10\n";
5373

5374
    FAIL_IF(DetectEngineInitYamlConf(conf) == -1);
5375

5376
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
5377
    FAIL_IF_NULL(de_ctx);
5378

5379
    FAIL_IF_NOT(de_ctx->max_uniq_toclient_groups == 20);
5380
    FAIL_IF_NOT(de_ctx->max_uniq_toserver_groups == 40);
5381

5382
    DetectEngineCtxFree(de_ctx);
5383

5384
    DetectEngineDeInitYamlConf();
5385

5386
    PASS;
5387
}
5388

5389
#endif
5390

5391
void DetectEngineRegisterTests(void)
UNCOV
5392
{
×
5393
#ifdef UNITTESTS
5394
    UtRegisterTest("DetectEngineTest01", DetectEngineTest01);
5395
    UtRegisterTest("DetectEngineTest02", DetectEngineTest02);
5396
    UtRegisterTest("DetectEngineTest03", DetectEngineTest03);
5397
    UtRegisterTest("DetectEngineTest04", DetectEngineTest04);
5398
    UtRegisterTest("DetectEngineTest08", DetectEngineTest08);
5399
    UtRegisterTest("DetectEngineTest09", DetectEngineTest09);
5400
#endif
UNCOV
5401
}
×
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