Coverage Report

Created: 2026-03-31 07:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/util-mpm.c
Line
Count
Source
1
/* Copyright (C) 2007-2021 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
/**
19
 * \file
20
 *
21
 * \author Victor Julien <victor@inliniac.net>
22
 *
23
 * Pattern matcher utility Functions
24
 */
25
26
#include "suricata-common.h"
27
#include "util-mpm.h"
28
#include "util-debug.h"
29
30
/* include pattern matchers */
31
#include "util-mpm-ac.h"
32
#include "util-mpm-ac-bs.h"
33
#include "util-mpm-ac-ks.h"
34
#include "util-mpm-hs.h"
35
#include "util-hashlist.h"
36
37
#include "detect-engine.h"
38
#include "util-misc.h"
39
#include "conf.h"
40
#include "conf-yaml-loader.h"
41
#include "queue.h"
42
#include "util-unittest.h"
43
#include "util-memcpy.h"
44
#ifdef BUILD_HYPERSCAN
45
#include "hs.h"
46
#endif
47
48
MpmTableElmt mpm_table[MPM_TABLE_SIZE];
49
uint8_t mpm_default_matcher;
50
51
/**
52
 * \brief Register a new Mpm Context.
53
 *
54
 * \param name A new profile to be registered to store this MpmCtx.
55
 * \param sm_list sm_list for this name (might be variable with xforms)
56
 * \param alproto app proto or ALPROTO_UNKNOWN if not for app-layer
57
 *
58
 * \retval id Return the id created for the new MpmCtx profile.
59
 */
60
int32_t MpmFactoryRegisterMpmCtxProfile(
61
        DetectEngineCtx *de_ctx, const char *name, const int sm_list, const AppProto alproto)
62
42.9M
{
63
    /* the very first entry */
64
42.9M
    if (de_ctx->mpm_ctx_factory_container == NULL) {
65
146k
        de_ctx->mpm_ctx_factory_container = SCCalloc(1, sizeof(MpmCtxFactoryContainer));
66
146k
        if (de_ctx->mpm_ctx_factory_container == NULL) {
67
0
            FatalError("Error allocating memory");
68
0
        }
69
146k
        de_ctx->mpm_ctx_factory_container->max_id = ENGINE_SGH_MPM_FACTORY_CONTEXT_START_ID_RANGE;
70
146k
    }
71
72
42.9M
    MpmCtxFactoryItem *item = de_ctx->mpm_ctx_factory_container->items;
73
42.9M
    MpmCtxFactoryItem *pitem = NULL;
74
5.30G
    while (item) {
75
5.27G
        if (item->sm_list == sm_list && item->alproto == alproto && item->name != NULL &&
76
13.4M
                strcmp(item->name, name) == 0) {
77
12.6M
            return item->id;
78
12.6M
        }
79
5.26G
        pitem = item;
80
5.26G
        item = item->next;
81
5.26G
    }
82
83
30.3M
    MpmCtxFactoryItem *nitem = SCCalloc(1, sizeof(MpmCtxFactoryItem));
84
30.3M
    if (unlikely(nitem == NULL)) {
85
0
        FatalError("Error allocating memory");
86
0
    }
87
30.3M
    nitem->name = name;
88
30.3M
    nitem->sm_list = sm_list;
89
30.3M
    nitem->id = de_ctx->mpm_ctx_factory_container->max_id++;
90
30.3M
    nitem->alproto = alproto;
91
92
    /* toserver */
93
30.3M
    nitem->mpm_ctx_ts = SCCalloc(1, sizeof(MpmCtx));
94
30.3M
    if (nitem->mpm_ctx_ts == NULL) {
95
0
        FatalError("Error allocating memory");
96
0
    }
97
30.3M
    nitem->mpm_ctx_ts->flags |= MPMCTX_FLAGS_GLOBAL;
98
99
    /* toclient */
100
30.3M
    nitem->mpm_ctx_tc = SCCalloc(1, sizeof(MpmCtx));
101
30.3M
    if (nitem->mpm_ctx_tc == NULL) {
102
0
        FatalError("Error allocating memory");
103
0
    }
104
30.3M
    nitem->mpm_ctx_tc->flags |= MPMCTX_FLAGS_GLOBAL;
105
106
    /* store the newly created item */
107
30.3M
    if (pitem == NULL)
108
146k
        de_ctx->mpm_ctx_factory_container->items = nitem;
109
30.1M
    else
110
30.1M
        pitem->next = nitem;
111
112
30.3M
    de_ctx->mpm_ctx_factory_container->no_of_items++;
113
30.3M
    return nitem->id;
114
30.3M
}
115
116
int32_t MpmFactoryIsMpmCtxAvailable(const DetectEngineCtx *de_ctx, const MpmCtx *mpm_ctx)
117
2.44k
{
118
2.44k
    if (mpm_ctx == NULL)
119
0
        return 0;
120
121
2.44k
    if (de_ctx->mpm_ctx_factory_container == NULL) {
122
0
        return 0;
123
0
    }
124
125
518k
    for (MpmCtxFactoryItem *i = de_ctx->mpm_ctx_factory_container->items; i != NULL; i = i->next) {
126
518k
        if (mpm_ctx == i->mpm_ctx_ts || mpm_ctx == i->mpm_ctx_tc) {
127
2.44k
            return 1;
128
2.44k
        }
129
518k
    }
130
0
    return 0;
131
2.44k
}
132
133
MpmCtx *MpmFactoryGetMpmCtxForProfile(const DetectEngineCtx *de_ctx, int32_t id, int direction)
134
43.5M
{
135
43.5M
    if (id == MPM_CTX_FACTORY_UNIQUE_CONTEXT) {
136
0
        MpmCtx *mpm_ctx = SCMalloc(sizeof(MpmCtx));
137
0
        if (unlikely(mpm_ctx == NULL)) {
138
0
            FatalError("Error allocating memory");
139
0
        }
140
0
        memset(mpm_ctx, 0, sizeof(MpmCtx));
141
0
        return mpm_ctx;
142
43.5M
    } else if (id < -1) {
143
0
        SCLogError("Invalid argument - %d\n", id);
144
0
        return NULL;
145
43.5M
    } else if (id >= de_ctx->mpm_ctx_factory_container->max_id) {
146
        /* this id does not exist */
147
0
        return NULL;
148
43.5M
    } else {
149
5.42G
        for (MpmCtxFactoryItem *i = de_ctx->mpm_ctx_factory_container->items; i != NULL;
150
5.42G
                i = i->next) {
151
5.42G
            if (id == i->id) {
152
43.5M
                return (direction == 0) ? i->mpm_ctx_ts : i->mpm_ctx_tc;
153
43.5M
            }
154
5.42G
        }
155
0
        return NULL;
156
43.5M
    }
157
43.5M
}
158
159
void MpmFactoryReClaimMpmCtx(const DetectEngineCtx *de_ctx, MpmCtx *mpm_ctx)
160
2.44k
{
161
2.44k
    if (mpm_ctx == NULL)
162
0
        return;
163
164
2.44k
    if (!MpmFactoryIsMpmCtxAvailable(de_ctx, mpm_ctx)) {
165
0
        if (mpm_ctx->mpm_type != MPM_NOTSET)
166
0
            mpm_table[mpm_ctx->mpm_type].DestroyCtx(mpm_ctx);
167
0
        SCFree(mpm_ctx);
168
0
    }
169
2.44k
}
170
171
void MpmFactoryDeRegisterAllMpmCtxProfiles(DetectEngineCtx *de_ctx)
172
146k
{
173
146k
    if (de_ctx->mpm_ctx_factory_container == NULL)
174
12
        return;
175
176
146k
    MpmCtxFactoryItem *item = de_ctx->mpm_ctx_factory_container->items;
177
30.4M
    while (item) {
178
30.3M
        if (item->mpm_ctx_ts != NULL) {
179
30.3M
            if (item->mpm_ctx_ts->mpm_type != MPM_NOTSET)
180
74.3k
                mpm_table[item->mpm_ctx_ts->mpm_type].DestroyCtx(item->mpm_ctx_ts);
181
30.3M
            SCFree(item->mpm_ctx_ts);
182
30.3M
        }
183
30.3M
        if (item->mpm_ctx_tc != NULL) {
184
30.3M
            if (item->mpm_ctx_tc->mpm_type != MPM_NOTSET)
185
93.6k
                mpm_table[item->mpm_ctx_tc->mpm_type].DestroyCtx(item->mpm_ctx_tc);
186
30.3M
            SCFree(item->mpm_ctx_tc);
187
30.3M
        }
188
189
30.3M
        MpmCtxFactoryItem *next = item->next;
190
30.3M
        SCFree(item);
191
30.3M
        item = next;
192
30.3M
    }
193
194
146k
    SCFree(de_ctx->mpm_ctx_factory_container);
195
146k
    de_ctx->mpm_ctx_factory_container = NULL;
196
146k
}
197
198
void MpmInitThreadCtx(MpmThreadCtx *mpm_thread_ctx, uint16_t matcher)
199
223k
{
200
223k
    mpm_table[matcher].InitThreadCtx(NULL, mpm_thread_ctx);
201
223k
}
202
203
void MpmInitCtx(MpmCtx *mpm_ctx, uint8_t matcher)
204
209k
{
205
209k
    mpm_ctx->mpm_type = matcher;
206
209k
    mpm_table[matcher].InitCtx(mpm_ctx);
207
209k
}
208
209
/* MPM matcher to use by default, i.e. when "mpm-algo" is set to "auto".
210
 * If Hyperscan is available, use it. Otherwise, use AC. */
211
#ifdef BUILD_HYPERSCAN
212
# define DEFAULT_MPM    MPM_HS
213
# define DEFAULT_MPM_AC MPM_AC
214
#else
215
75
# define DEFAULT_MPM    MPM_AC
216
#endif
217
218
void MpmTableSetup(void)
219
75
{
220
75
    memset(mpm_table, 0, sizeof(mpm_table));
221
75
    mpm_default_matcher = DEFAULT_MPM;
222
223
75
    MpmACRegister();
224
75
    MpmACBSRegister();
225
75
    MpmACTileRegister();
226
#ifdef BUILD_HYPERSCAN
227
    #ifdef HAVE_HS_VALID_PLATFORM
228
    /* Enable runtime check for SSSE3. Do not use Hyperscan MPM matcher if
229
     * check is not successful. */
230
        if (hs_valid_platform() != HS_SUCCESS) {
231
            SCLogInfo("SSSE3 support not detected, disabling Hyperscan for "
232
                      "MPM");
233
            /* Fall back to best Aho-Corasick variant. */
234
            mpm_default_matcher = DEFAULT_MPM_AC;
235
        } else {
236
            MpmHSRegister();
237
        }
238
    #else
239
        MpmHSRegister();
240
    #endif /* HAVE_HS_VALID_PLATFORM */
241
#endif /* BUILD_HYPERSCAN */
242
75
}
243
244
int MpmAddPatternCS(struct MpmCtx_ *mpm_ctx, uint8_t *pat, uint16_t patlen,
245
                    uint16_t offset, uint16_t depth,
246
                    uint32_t pid, SigIntId sid, uint8_t flags)
247
312k
{
248
312k
    return mpm_table[mpm_ctx->mpm_type].AddPattern(mpm_ctx, pat, patlen,
249
312k
                                                   offset, depth,
250
312k
                                                   pid, sid, flags);
251
312k
}
252
253
int MpmAddPatternCI(struct MpmCtx_ *mpm_ctx, uint8_t *pat, uint16_t patlen,
254
                    uint16_t offset, uint16_t depth,
255
                    uint32_t pid, SigIntId sid, uint8_t flags)
256
33.3k
{
257
33.3k
    return mpm_table[mpm_ctx->mpm_type].AddPatternNocase(mpm_ctx, pat, patlen,
258
33.3k
                                                         offset, depth,
259
33.3k
                                                         pid, sid, flags);
260
33.3k
}
261
262
263
/**
264
 * \internal
265
 * \brief Creates a hash of the pattern.  We use it for the hashing process
266
 *        during the initial pattern insertion time, to cull duplicate sigs.
267
 *
268
 * \param pat    Pointer to the pattern.
269
 * \param patlen Pattern length.
270
 *
271
 * \retval hash A 32 bit unsigned hash.
272
 */
273
static inline uint32_t MpmInitHashRaw(uint8_t *pat, uint16_t patlen)
274
366k
{
275
366k
    uint32_t hash = patlen * pat[0];
276
366k
    if (patlen > 1)
277
344k
        hash += pat[1];
278
279
366k
    return (hash % MPM_INIT_HASH_SIZE);
280
366k
}
281
282
/**
283
 * \internal
284
 * \brief Looks up a pattern.  We use it for the hashing process during the
285
 *        the initial pattern insertion time, to cull duplicate sigs.
286
 *
287
 * \param ctx    Pointer to the AC ctx.
288
 * \param pat    Pointer to the pattern.
289
 * \param patlen Pattern length.
290
 * \param flags  Flags.  We don't need this.
291
 *
292
 * \retval hash A 32 bit unsigned hash.
293
 */
294
static inline MpmPattern *MpmInitHashLookup(MpmCtx *ctx,
295
        uint8_t *pat, uint16_t patlen,
296
        uint16_t offset, uint16_t depth,
297
        uint8_t flags, uint32_t pid)
298
366k
{
299
366k
    uint32_t hash = MpmInitHashRaw(pat, patlen);
300
301
366k
    if (ctx->init_hash == NULL) {
302
0
        return NULL;
303
0
    }
304
305
366k
    MpmPattern *t = ctx->init_hash[hash];
306
408k
    for ( ; t != NULL; t = t->next) {
307
195k
        if (!(flags & MPM_PATTERN_CTX_OWNS_ID)) {
308
11.2k
            if (t->id == pid)
309
0
                return t;
310
183k
        } else {
311
183k
            if (t->len == patlen && t->offset == offset && t->depth == depth &&
312
172k
                    memcmp(pat, t->original_pat, patlen) == 0 &&
313
154k
                    t->flags == flags)
314
153k
            {
315
153k
                return t;
316
153k
            }
317
183k
        }
318
195k
    }
319
320
213k
    return NULL;
321
366k
}
322
323
/**
324
 * \internal
325
 * \brief Allocs a new pattern instance.
326
 *
327
 * \param mpm_ctx Pointer to the mpm context.
328
 *
329
 * \retval p Pointer to the newly created pattern.
330
 */
331
static inline MpmPattern *MpmAllocPattern(MpmCtx *mpm_ctx)
332
213k
{
333
213k
    MpmPattern *p = SCMalloc(sizeof(MpmPattern));
334
213k
    if (unlikely(p == NULL)) {
335
0
        exit(EXIT_FAILURE);
336
0
    }
337
213k
    memset(p, 0, sizeof(MpmPattern));
338
339
213k
    mpm_ctx->memory_cnt++;
340
213k
    mpm_ctx->memory_size += sizeof(MpmPattern);
341
342
213k
    return p;
343
213k
}
344
345
/**
346
 * \internal
347
 * \brief Used to free MpmPattern instances.
348
 *
349
 * \param mpm_ctx Pointer to the mpm context.
350
 * \param p       Pointer to the MpmPattern instance to be freed.
351
 */
352
void MpmFreePattern(MpmCtx *mpm_ctx, MpmPattern *p)
353
106k
{
354
106k
    if (p != NULL && p->cs != NULL && p->cs != p->ci) {
355
26.7k
        SCFree(p->cs);
356
26.7k
        mpm_ctx->memory_cnt--;
357
26.7k
        mpm_ctx->memory_size -= p->len;
358
26.7k
    }
359
360
106k
    if (p != NULL && p->ci != NULL) {
361
106k
        SCFree(p->ci);
362
106k
        mpm_ctx->memory_cnt--;
363
106k
        mpm_ctx->memory_size -= p->len;
364
106k
    }
365
366
106k
    if (p != NULL && p->original_pat != NULL) {
367
106k
        SCFree(p->original_pat);
368
106k
        mpm_ctx->memory_cnt--;
369
106k
        mpm_ctx->memory_size -= p->len;
370
106k
    }
371
372
106k
    if (p != NULL) {
373
106k
        SCFree(p);
374
106k
        mpm_ctx->memory_cnt--;
375
106k
        mpm_ctx->memory_size -= sizeof(MpmPattern);
376
106k
    }
377
106k
    return;
378
106k
}
379
380
static inline uint32_t MpmInitHash(MpmPattern *p)
381
213k
{
382
213k
    uint32_t hash = p->len * p->original_pat[0];
383
213k
    if (p->len > 1)
384
203k
        hash += p->original_pat[1];
385
386
213k
    return (hash % MPM_INIT_HASH_SIZE);
387
213k
}
388
389
static inline int MpmInitHashAdd(MpmCtx *ctx, MpmPattern *p)
390
213k
{
391
213k
    uint32_t hash = MpmInitHash(p);
392
393
213k
    if (ctx->init_hash == NULL) {
394
0
        return -1;
395
0
    }
396
397
213k
    if (ctx->init_hash[hash] == NULL) {
398
200k
        ctx->init_hash[hash] = p;
399
200k
        return 0;
400
200k
    }
401
402
12.7k
    MpmPattern *tt = NULL;
403
12.7k
    MpmPattern *t = ctx->init_hash[hash];
404
405
    /* get the list tail */
406
18.7k
    do {
407
18.7k
        tt = t;
408
18.7k
        t = t->next;
409
18.7k
    } while (t != NULL);
410
411
12.7k
    tt->next = p;
412
413
12.7k
    return 0;
414
213k
}
415
416
/**
417
 * \internal
418
 * \brief Add a pattern to the mpm-ac context.
419
 *
420
 * \param mpm_ctx Mpm context.
421
 * \param pat     Pointer to the pattern.
422
 * \param patlen  Length of the pattern.
423
 * \param pid     Pattern id
424
 * \param sid     Signature id (internal id).
425
 * \param flags   Pattern's MPM_PATTERN_* flags.
426
 *
427
 * \retval  0 On success.
428
 * \retval -1 On failure.
429
 */
430
int MpmAddPattern(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
431
                            uint16_t offset, uint16_t depth, uint32_t pid,
432
                            SigIntId sid, uint8_t flags)
433
366k
{
434
366k
    SCLogDebug("Adding pattern for ctx %p, patlen %"PRIu16" and pid %" PRIu32,
435
366k
               mpm_ctx, patlen, pid);
436
437
366k
    if (patlen == 0) {
438
0
        SCLogWarning("pattern length 0");
439
0
        return 0;
440
0
    }
441
442
366k
    if (flags & MPM_PATTERN_CTX_OWNS_ID)
443
351k
        pid = UINT_MAX;
444
445
    /* check if we have already inserted this pattern */
446
366k
    MpmPattern *p = MpmInitHashLookup(mpm_ctx, pat, patlen,
447
366k
            offset, depth, flags, pid);
448
366k
    if (p == NULL) {
449
213k
        SCLogDebug("Allocing new pattern");
450
451
        /* p will never be NULL */
452
213k
        p = MpmAllocPattern(mpm_ctx);
453
454
213k
        p->len = patlen;
455
213k
        p->flags = flags;
456
213k
        p->offset = offset;
457
213k
        p->depth = depth;
458
213k
        if (flags & MPM_PATTERN_CTX_OWNS_ID)
459
198k
            p->id = mpm_ctx->max_pat_id++;
460
15.0k
        else
461
15.0k
            p->id = pid;
462
463
213k
        p->original_pat = SCMalloc(patlen);
464
213k
        if (p->original_pat == NULL)
465
0
            goto error;
466
213k
        mpm_ctx->memory_cnt++;
467
213k
        mpm_ctx->memory_size += patlen;
468
213k
        memcpy(p->original_pat, pat, patlen);
469
470
213k
        p->ci = SCMalloc(patlen);
471
213k
        if (p->ci == NULL)
472
0
            goto error;
473
213k
        mpm_ctx->memory_cnt++;
474
213k
        mpm_ctx->memory_size += patlen;
475
213k
        memcpy_tolower(p->ci, pat, patlen);
476
477
        /* setup the case sensitive part of the pattern */
478
213k
        if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
479
            /* nocase means no difference between cs and ci */
480
31.3k
            p->cs = p->ci;
481
182k
        } else {
482
182k
            if (memcmp(p->ci, pat, p->len) == 0) {
483
                /* no diff between cs and ci: pat is lowercase */
484
128k
                p->cs = p->ci;
485
128k
            } else {
486
53.7k
                p->cs = SCMalloc(patlen);
487
53.7k
                if (p->cs == NULL)
488
0
                    goto error;
489
53.7k
                mpm_ctx->memory_cnt++;
490
53.7k
                mpm_ctx->memory_size += patlen;
491
53.7k
                memcpy(p->cs, pat, patlen);
492
53.7k
            }
493
182k
        }
494
495
        /* put in the pattern hash */
496
213k
        if (MpmInitHashAdd(mpm_ctx, p) != 0)
497
0
            goto error;
498
499
213k
        mpm_ctx->pattern_cnt++;
500
501
213k
        if (!(mpm_ctx->flags & MPMCTX_FLAGS_NODEPTH)) {
502
180k
            if (depth) {
503
29.8k
                mpm_ctx->maxdepth = MAX(mpm_ctx->maxdepth, depth);
504
29.8k
                SCLogDebug("%p: depth %u max %u", mpm_ctx, depth, mpm_ctx->maxdepth);
505
150k
            } else {
506
150k
                mpm_ctx->flags |= MPMCTX_FLAGS_NODEPTH;
507
150k
                mpm_ctx->maxdepth = 0;
508
150k
                SCLogDebug("%p: alas, no depth for us", mpm_ctx);
509
150k
            }
510
180k
        }
511
512
213k
        if (mpm_ctx->maxlen < patlen)
513
178k
            mpm_ctx->maxlen = patlen;
514
515
213k
        if (mpm_ctx->minlen == 0) {
516
166k
            mpm_ctx->minlen = patlen;
517
166k
        } else {
518
46.8k
            if (mpm_ctx->minlen > patlen)
519
8.20k
                mpm_ctx->minlen = patlen;
520
46.8k
        }
521
522
        /* we need the max pat id */
523
213k
        if (p->id > mpm_ctx->max_pat_id)
524
14.5k
            mpm_ctx->max_pat_id = p->id;
525
526
213k
        p->sids_size = 1;
527
213k
        p->sids = SCMalloc(p->sids_size * sizeof(SigIntId));
528
213k
        BUG_ON(p->sids == NULL);
529
213k
        p->sids[0] = sid;
530
213k
    } else {
531
        /* we can be called multiple times for the same sid in the case
532
         * of the 'single' modus. Here multiple rule groups share the
533
         * same mpm ctx and might be adding the same pattern to the
534
         * mpm_ctx */
535
153k
        int found = 0;
536
153k
        uint32_t x = 0;
537
322k
        for (x = 0; x < p->sids_size; x++) {
538
265k
            if (p->sids[x] == sid) {
539
95.4k
                found = 1;
540
95.4k
                break;
541
95.4k
            }
542
265k
        }
543
544
153k
        if (!found) {
545
57.9k
            SigIntId *sids = SCRealloc(p->sids, (sizeof(SigIntId) * (p->sids_size + 1)));
546
57.9k
            BUG_ON(sids == NULL);
547
57.9k
            p->sids = sids;
548
57.9k
            p->sids[p->sids_size] = sid;
549
57.9k
            p->sids_size++;
550
57.9k
        }
551
153k
    }
552
553
366k
    return 0;
554
555
0
error:
556
0
    MpmFreePattern(mpm_ctx, p);
557
0
    return -1;
558
366k
}
559
560
561
/************************************Unittests*********************************/
562
563
#ifdef UNITTESTS
564
#endif /* UNITTESTS */
565
566
void MpmRegisterTests(void)
567
0
{
568
#ifdef UNITTESTS
569
    uint16_t i;
570
571
    for (i = 0; i < MPM_TABLE_SIZE; i++) {
572
        if (i == MPM_NOTSET)
573
            continue;
574
575
        g_ut_modules++;
576
577
        if (mpm_table[i].RegisterUnittests != NULL) {
578
            g_ut_covered++;
579
            mpm_table[i].RegisterUnittests();
580
        } else {
581
            if (coverage_unittests)
582
                SCLogWarning("mpm module %s has no "
583
                             "unittest registration function.",
584
                        mpm_table[i].name);
585
        }
586
    }
587
588
#endif
589
0
}