diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:39:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:39:49 +0000 |
commit | a0aa2307322cd47bbf416810ac0292925e03be87 (patch) | |
tree | 37076262a026c4b48c8a0e84f44ff9187556ca35 /src/util-mpm-hs.c | |
parent | Initial commit. (diff) | |
download | suricata-a0aa2307322cd47bbf416810ac0292925e03be87.tar.xz suricata-a0aa2307322cd47bbf416810ac0292925e03be87.zip |
Adding upstream version 1:7.0.3.upstream/1%7.0.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/util-mpm-hs.c | 2187 |
1 files changed, 2187 insertions, 0 deletions
diff --git a/src/util-mpm-hs.c b/src/util-mpm-hs.c new file mode 100644 index 0000000..a6fb823 --- /dev/null +++ b/src/util-mpm-hs.c @@ -0,0 +1,2187 @@ +/* Copyright (C) 2007-2016 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Jim Xu <jim.xu@windriver.com> + * \author Justin Viiret <justin.viiret@intel.com> + * + * MPM pattern matcher that calls the Hyperscan regex matcher. + */ + +#include "suricata-common.h" +#include "suricata.h" + +#include "detect.h" +#include "detect-parse.h" +#include "detect-engine.h" +#include "detect-engine-build.h" + +#include "conf.h" +#include "util-debug.h" +#include "util-unittest.h" +#include "util-unittest-helper.h" +#include "util-memcmp.h" +#include "util-mpm-hs.h" +#include "util-memcpy.h" +#include "util-hash.h" +#include "util-hash-lookup3.h" +#include "util-hyperscan.h" + +#ifdef BUILD_HYPERSCAN + +#include <hs.h> + +void SCHSInitCtx(MpmCtx *); +void SCHSInitThreadCtx(MpmCtx *, MpmThreadCtx *); +void SCHSDestroyCtx(MpmCtx *); +void SCHSDestroyThreadCtx(MpmCtx *, MpmThreadCtx *); +int SCHSAddPatternCI(MpmCtx *, uint8_t *, uint16_t, uint16_t, uint16_t, + uint32_t, SigIntId, uint8_t); +int SCHSAddPatternCS(MpmCtx *, uint8_t *, uint16_t, uint16_t, uint16_t, + uint32_t, SigIntId, uint8_t); +int SCHSPreparePatterns(MpmCtx *mpm_ctx); +uint32_t SCHSSearch(const MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, + PrefilterRuleStore *pmq, const uint8_t *buf, const uint32_t buflen); +void SCHSPrintInfo(MpmCtx *mpm_ctx); +void SCHSPrintSearchStats(MpmThreadCtx *mpm_thread_ctx); +void SCHSRegisterTests(void); + +/* size of the hash table used to speed up pattern insertions initially */ +#define INIT_HASH_SIZE 65536 + +/* Initial size of the global database hash (used for de-duplication). */ +#define INIT_DB_HASH_SIZE 1000 + +/* Global prototype scratch, built incrementally as Hyperscan databases are + * built and then cloned for each thread context. Access is serialised via + * g_scratch_proto_mutex. */ +static hs_scratch_t *g_scratch_proto = NULL; +static SCMutex g_scratch_proto_mutex = SCMUTEX_INITIALIZER; + +/* Global hash table of Hyperscan databases, used for de-duplication. Access is + * serialised via g_db_table_mutex. */ +static HashTable *g_db_table = NULL; +static SCMutex g_db_table_mutex = SCMUTEX_INITIALIZER; + +/** + * \internal + * \brief Wraps SCMalloc (which is a macro) so that it can be passed to + * hs_set_allocator() for Hyperscan's use. + */ +static void *SCHSMalloc(size_t size) +{ + return SCMalloc(size); +} + +/** + * \internal + * \brief Wraps SCFree (which is a macro) so that it can be passed to + * hs_set_allocator() for Hyperscan's use. + */ +static void SCHSFree(void *ptr) +{ + SCFree(ptr); +} + +/** \brief Register Suricata malloc/free with Hyperscan. + * + * Requests that Hyperscan use Suricata's allocator for allocation of + * databases, scratch space, etc. + */ +static void SCHSSetAllocators(void) +{ + hs_error_t err = hs_set_allocator(SCHSMalloc, SCHSFree); + if (err != HS_SUCCESS) { + FatalError("Failed to set Hyperscan allocator."); + } +} + +/** + * \internal + * \brief Creates a hash of the pattern. We use it for the hashing process + * during the initial pattern insertion time, to cull duplicate sigs. + * + * \param pat Pointer to the pattern. + * \param patlen Pattern length. + * + * \retval hash A 32 bit unsigned hash. + */ +static inline uint32_t SCHSInitHashRaw(uint8_t *pat, uint16_t patlen) +{ + uint32_t hash = patlen * pat[0]; + if (patlen > 1) + hash += pat[1]; + + return (hash % INIT_HASH_SIZE); +} + +/** + * \internal + * \brief Looks up a pattern. We use it for the hashing process during + * the initial pattern insertion time, to cull duplicate sigs. + * + * \param ctx Pointer to the HS ctx. + * \param pat Pointer to the pattern. + * \param patlen Pattern length. + * \param flags Flags. We don't need this. + * + * \retval hash A 32 bit unsigned hash. + */ +static inline SCHSPattern *SCHSInitHashLookup(SCHSCtx *ctx, uint8_t *pat, + uint16_t patlen, uint16_t offset, + uint16_t depth, char flags, + uint32_t pid) +{ + uint32_t hash = SCHSInitHashRaw(pat, patlen); + + if (ctx->init_hash == NULL) { + return NULL; + } + + SCHSPattern *t = ctx->init_hash[hash]; + for (; t != NULL; t = t->next) { + /* Since Hyperscan uses offset/depth, we must distinguish between + * patterns with the same ID but different offset/depth here. */ + if (t->id == pid && t->offset == offset && t->depth == depth) { + BUG_ON(t->len != patlen); + BUG_ON(SCMemcmp(t->original_pat, pat, patlen) != 0); + return t; + } + } + + return NULL; +} + +/** + * \internal + * \brief Allocates a new pattern instance. + * + * \param mpm_ctx Pointer to the mpm context. + * + * \retval p Pointer to the newly created pattern. + */ +static inline SCHSPattern *SCHSAllocPattern(MpmCtx *mpm_ctx) +{ + SCHSPattern *p = SCMalloc(sizeof(SCHSPattern)); + if (unlikely(p == NULL)) { + exit(EXIT_FAILURE); + } + memset(p, 0, sizeof(SCHSPattern)); + + mpm_ctx->memory_cnt++; + mpm_ctx->memory_size += sizeof(SCHSPattern); + + return p; +} + +/** + * \internal + * \brief Used to free SCHSPattern instances. + * + * \param mpm_ctx Pointer to the mpm context. + * \param p Pointer to the SCHSPattern instance to be freed. + * \param free Free the above pointer or not. + */ +static inline void SCHSFreePattern(MpmCtx *mpm_ctx, SCHSPattern *p) +{ + if (p != NULL && p->original_pat != NULL) { + SCFree(p->original_pat); + mpm_ctx->memory_cnt--; + mpm_ctx->memory_size -= p->len; + } + + if (p != NULL && p->sids != NULL) { + SCFree(p->sids); + } + + if (p != NULL) { + SCFree(p); + mpm_ctx->memory_cnt--; + mpm_ctx->memory_size -= sizeof(SCHSPattern); + } +} + +static inline uint32_t SCHSInitHash(SCHSPattern *p) +{ + uint32_t hash = p->len * p->original_pat[0]; + if (p->len > 1) + hash += p->original_pat[1]; + + return (hash % INIT_HASH_SIZE); +} + +static inline int SCHSInitHashAdd(SCHSCtx *ctx, SCHSPattern *p) +{ + uint32_t hash = SCHSInitHash(p); + + if (ctx->init_hash == NULL) { + return -1; + } + + if (ctx->init_hash[hash] == NULL) { + ctx->init_hash[hash] = p; + return 0; + } + + SCHSPattern *tt = NULL; + SCHSPattern *t = ctx->init_hash[hash]; + + /* get the list tail */ + do { + tt = t; + t = t->next; + } while (t != NULL); + + tt->next = p; + + return 0; +} + +/** + * \internal + * \brief Add a pattern to the mpm-hs context. + * + * \param mpm_ctx Mpm context. + * \param pat Pointer to the pattern. + * \param patlen Length of the pattern. + * \param pid Pattern id + * \param sid Signature id (internal id). + * \param flags Pattern's MPM_PATTERN_* flags. + * + * \retval 0 On success. + * \retval -1 On failure. + */ +static int SCHSAddPattern(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen, + uint16_t offset, uint16_t depth, uint32_t pid, + SigIntId sid, uint8_t flags) +{ + SCHSCtx *ctx = (SCHSCtx *)mpm_ctx->ctx; + + if (offset != 0) { + flags |= MPM_PATTERN_FLAG_OFFSET; + } + if (depth != 0) { + flags |= MPM_PATTERN_FLAG_DEPTH; + } + + if (patlen == 0) { + SCLogWarning("pattern length 0"); + return 0; + } + + /* check if we have already inserted this pattern */ + SCHSPattern *p = + SCHSInitHashLookup(ctx, pat, patlen, offset, depth, flags, pid); + if (p == NULL) { + SCLogDebug("Allocing new pattern"); + + /* p will never be NULL */ + p = SCHSAllocPattern(mpm_ctx); + + p->len = patlen; + p->flags = flags; + p->id = pid; + + p->offset = offset; + p->depth = depth; + + p->original_pat = SCMalloc(patlen); + if (p->original_pat == NULL) + goto error; + mpm_ctx->memory_cnt++; + mpm_ctx->memory_size += patlen; + memcpy(p->original_pat, pat, patlen); + + /* put in the pattern hash */ + if (SCHSInitHashAdd(ctx, p) != 0) + goto error; + + mpm_ctx->pattern_cnt++; + + if (!(mpm_ctx->flags & MPMCTX_FLAGS_NODEPTH)) { + if (depth) { + mpm_ctx->maxdepth = MAX(mpm_ctx->maxdepth, depth); + SCLogDebug("%p: depth %u max %u", mpm_ctx, depth, mpm_ctx->maxdepth); + } else { + mpm_ctx->flags |= MPMCTX_FLAGS_NODEPTH; + mpm_ctx->maxdepth = 0; + SCLogDebug("%p: alas, no depth for us", mpm_ctx); + } + } + + if (mpm_ctx->maxlen < patlen) + mpm_ctx->maxlen = patlen; + + if (mpm_ctx->minlen == 0) { + mpm_ctx->minlen = patlen; + } else { + if (mpm_ctx->minlen > patlen) + mpm_ctx->minlen = patlen; + } + + p->sids_size = 1; + p->sids = SCMalloc(p->sids_size * sizeof(SigIntId)); + BUG_ON(p->sids == NULL); + p->sids[0] = sid; + } else { + /* TODO figure out how we can be called multiple times for the same CTX with the same sid */ + + int found = 0; + uint32_t x = 0; + for (x = 0; x < p->sids_size; x++) { + if (p->sids[x] == sid) { + found = 1; + break; + } + } + if (!found) { + SigIntId *sids = SCRealloc(p->sids, (sizeof(SigIntId) * (p->sids_size + 1))); + BUG_ON(sids == NULL); + p->sids = sids; + p->sids[p->sids_size] = sid; + p->sids_size++; + } + } + + return 0; + +error: + SCHSFreePattern(mpm_ctx, p); + return -1; +} + +/** + * \brief Pattern database information used only as input to the Hyperscan + * compiler. + */ +typedef struct SCHSCompileData_ { + unsigned int *ids; + unsigned int *flags; + char **expressions; + hs_expr_ext_t **ext; + unsigned int pattern_cnt; +} SCHSCompileData; + +static SCHSCompileData *SCHSAllocCompileData(unsigned int pattern_cnt) +{ + SCHSCompileData *cd = SCMalloc(pattern_cnt * sizeof(SCHSCompileData)); + if (cd == NULL) { + goto error; + } + memset(cd, 0, pattern_cnt * sizeof(SCHSCompileData)); + + cd->pattern_cnt = pattern_cnt; + + cd->ids = SCMalloc(pattern_cnt * sizeof(unsigned int)); + if (cd->ids == NULL) { + goto error; + } + memset(cd->ids, 0, pattern_cnt * sizeof(unsigned int)); + + cd->flags = SCMalloc(pattern_cnt * sizeof(unsigned int)); + if (cd->flags == NULL) { + goto error; + } + memset(cd->flags, 0, pattern_cnt * sizeof(unsigned int)); + + cd->expressions = SCMalloc(pattern_cnt * sizeof(char *)); + if (cd->expressions == NULL) { + goto error; + } + memset(cd->expressions, 0, pattern_cnt * sizeof(char *)); + + cd->ext = SCMalloc(pattern_cnt * sizeof(hs_expr_ext_t *)); + if (cd->ext == NULL) { + goto error; + } + memset(cd->ext, 0, pattern_cnt * sizeof(hs_expr_ext_t *)); + + return cd; + +error: + SCLogDebug("SCHSCompileData alloc failed"); + if (cd) { + SCFree(cd->ids); + SCFree(cd->flags); + SCFree(cd->expressions); + SCFree(cd->ext); + SCFree(cd); + } + return NULL; +} + +static void SCHSFreeCompileData(SCHSCompileData *cd) +{ + if (cd == NULL) { + return; + } + + SCFree(cd->ids); + SCFree(cd->flags); + if (cd->expressions) { + for (unsigned int i = 0; i < cd->pattern_cnt; i++) { + SCFree(cd->expressions[i]); + } + SCFree(cd->expressions); + } + if (cd->ext) { + for (unsigned int i = 0; i < cd->pattern_cnt; i++) { + SCFree(cd->ext[i]); + } + SCFree(cd->ext); + } + SCFree(cd); +} + +typedef struct PatternDatabase_ { + SCHSPattern **parray; + hs_database_t *hs_db; + uint32_t pattern_cnt; + + /* Reference count: number of MPM contexts using this pattern database. */ + uint32_t ref_cnt; +} PatternDatabase; + +static uint32_t SCHSPatternHash(const SCHSPattern *p, uint32_t hash) +{ + BUG_ON(p->original_pat == NULL); + BUG_ON(p->sids == NULL); + + hash = hashlittle_safe(&p->len, sizeof(p->len), hash); + hash = hashlittle_safe(&p->flags, sizeof(p->flags), hash); + hash = hashlittle_safe(p->original_pat, p->len, hash); + hash = hashlittle_safe(&p->id, sizeof(p->id), hash); + hash = hashlittle_safe(&p->offset, sizeof(p->offset), hash); + hash = hashlittle_safe(&p->depth, sizeof(p->depth), hash); + hash = hashlittle_safe(&p->sids_size, sizeof(p->sids_size), hash); + hash = hashlittle_safe(p->sids, p->sids_size * sizeof(SigIntId), hash); + return hash; +} + +static char SCHSPatternCompare(const SCHSPattern *p1, const SCHSPattern *p2) +{ + if ((p1->len != p2->len) || (p1->flags != p2->flags) || + (p1->id != p2->id) || (p1->offset != p2->offset) || + (p1->depth != p2->depth) || (p1->sids_size != p2->sids_size)) { + return 0; + } + + if (SCMemcmp(p1->original_pat, p2->original_pat, p1->len) != 0) { + return 0; + } + + if (SCMemcmp(p1->sids, p2->sids, p1->sids_size * sizeof(p1->sids[0])) != + 0) { + return 0; + } + + return 1; +} + +static uint32_t PatternDatabaseHash(HashTable *ht, void *data, uint16_t len) +{ + const PatternDatabase *pd = data; + uint32_t hash = 0; + hash = hashword(&pd->pattern_cnt, 1, hash); + + for (uint32_t i = 0; i < pd->pattern_cnt; i++) { + hash = SCHSPatternHash(pd->parray[i], hash); + } + + hash %= ht->array_size; + return hash; +} + +static char PatternDatabaseCompare(void *data1, uint16_t len1, void *data2, + uint16_t len2) +{ + const PatternDatabase *pd1 = data1; + const PatternDatabase *pd2 = data2; + + if (pd1->pattern_cnt != pd2->pattern_cnt) { + return 0; + } + + for (uint32_t i = 0; i < pd1->pattern_cnt; i++) { + if (SCHSPatternCompare(pd1->parray[i], pd2->parray[i]) == 0) { + return 0; + } + } + + return 1; +} + +static void PatternDatabaseFree(PatternDatabase *pd) +{ + BUG_ON(pd->ref_cnt != 0); + + if (pd->parray != NULL) { + for (uint32_t i = 0; i < pd->pattern_cnt; i++) { + SCHSPattern *p = pd->parray[i]; + if (p != NULL) { + SCFree(p->original_pat); + SCFree(p->sids); + SCFree(p); + } + } + SCFree(pd->parray); + } + + hs_free_database(pd->hs_db); + + SCFree(pd); +} + +static void PatternDatabaseTableFree(void *data) +{ + /* Stub function handed to hash table; actual freeing of PatternDatabase + * structures is done in MPM destruction when the ref_cnt drops to zero. */ +} + +static PatternDatabase *PatternDatabaseAlloc(uint32_t pattern_cnt) +{ + PatternDatabase *pd = SCMalloc(sizeof(PatternDatabase)); + if (pd == NULL) { + return NULL; + } + memset(pd, 0, sizeof(PatternDatabase)); + pd->pattern_cnt = pattern_cnt; + pd->ref_cnt = 0; + pd->hs_db = NULL; + + /* alloc the pattern array */ + pd->parray = + (SCHSPattern **)SCMalloc(pd->pattern_cnt * sizeof(SCHSPattern *)); + if (pd->parray == NULL) { + SCFree(pd); + return NULL; + } + memset(pd->parray, 0, pd->pattern_cnt * sizeof(SCHSPattern *)); + + return pd; +} + +/** + * \brief Process the patterns added to the mpm, and create the internal tables. + * + * \param mpm_ctx Pointer to the mpm context. + */ +int SCHSPreparePatterns(MpmCtx *mpm_ctx) +{ + SCHSCtx *ctx = (SCHSCtx *)mpm_ctx->ctx; + + if (mpm_ctx->pattern_cnt == 0 || ctx->init_hash == NULL) { + SCLogDebug("no patterns supplied to this mpm_ctx"); + return 0; + } + + hs_error_t err; + hs_compile_error_t *compile_err = NULL; + SCHSCompileData *cd = NULL; + PatternDatabase *pd = NULL; + + cd = SCHSAllocCompileData(mpm_ctx->pattern_cnt); + if (cd == NULL) { + goto error; + } + + pd = PatternDatabaseAlloc(mpm_ctx->pattern_cnt); + if (pd == NULL) { + goto error; + } + + /* populate the pattern array with the patterns in the hash */ + for (uint32_t i = 0, p = 0; i < INIT_HASH_SIZE; i++) { + SCHSPattern *node = ctx->init_hash[i], *nnode = NULL; + while (node != NULL) { + nnode = node->next; + node->next = NULL; + pd->parray[p++] = node; + node = nnode; + } + } + + /* we no longer need the hash, so free its memory */ + SCFree(ctx->init_hash); + ctx->init_hash = NULL; + + /* Serialise whole database compilation as a relatively easy way to ensure + * dedupe is safe. */ + SCMutexLock(&g_db_table_mutex); + + /* Init global pattern database hash if necessary. */ + if (g_db_table == NULL) { + g_db_table = HashTableInit(INIT_DB_HASH_SIZE, PatternDatabaseHash, + PatternDatabaseCompare, + PatternDatabaseTableFree); + if (g_db_table == NULL) { + SCMutexUnlock(&g_db_table_mutex); + goto error; + } + } + + /* Check global hash table to see if we've seen this pattern database + * before, and reuse the Hyperscan database if so. */ + PatternDatabase *pd_cached = HashTableLookup(g_db_table, pd, 1); + + if (pd_cached != NULL) { + SCLogDebug("Reusing cached database %p with %" PRIu32 + " patterns (ref_cnt=%" PRIu32 ")", + pd_cached->hs_db, pd_cached->pattern_cnt, + pd_cached->ref_cnt); + pd_cached->ref_cnt++; + ctx->pattern_db = pd_cached; + SCMutexUnlock(&g_db_table_mutex); + PatternDatabaseFree(pd); + SCHSFreeCompileData(cd); + return 0; + } + + BUG_ON(ctx->pattern_db != NULL); /* already built? */ + + for (uint32_t i = 0; i < pd->pattern_cnt; i++) { + const SCHSPattern *p = pd->parray[i]; + + cd->ids[i] = i; + cd->flags[i] = HS_FLAG_SINGLEMATCH; + if (p->flags & MPM_PATTERN_FLAG_NOCASE) { + cd->flags[i] |= HS_FLAG_CASELESS; + } + + cd->expressions[i] = HSRenderPattern(p->original_pat, p->len); + + if (p->flags & (MPM_PATTERN_FLAG_OFFSET | MPM_PATTERN_FLAG_DEPTH)) { + cd->ext[i] = SCMalloc(sizeof(hs_expr_ext_t)); + if (cd->ext[i] == NULL) { + SCMutexUnlock(&g_db_table_mutex); + goto error; + } + memset(cd->ext[i], 0, sizeof(hs_expr_ext_t)); + + if (p->flags & MPM_PATTERN_FLAG_OFFSET) { + cd->ext[i]->flags |= HS_EXT_FLAG_MIN_OFFSET; + cd->ext[i]->min_offset = p->offset + p->len; + } + if (p->flags & MPM_PATTERN_FLAG_DEPTH) { + cd->ext[i]->flags |= HS_EXT_FLAG_MAX_OFFSET; + cd->ext[i]->max_offset = p->offset + p->depth; + } + } + } + + BUG_ON(mpm_ctx->pattern_cnt == 0); + + err = hs_compile_ext_multi((const char *const *)cd->expressions, cd->flags, + cd->ids, (const hs_expr_ext_t *const *)cd->ext, + cd->pattern_cnt, HS_MODE_BLOCK, NULL, &pd->hs_db, + &compile_err); + + if (err != HS_SUCCESS) { + SCLogError("failed to compile hyperscan database"); + if (compile_err) { + SCLogError("compile error: %s", compile_err->message); + } + hs_free_compile_error(compile_err); + SCMutexUnlock(&g_db_table_mutex); + goto error; + } + + ctx->pattern_db = pd; + + SCMutexLock(&g_scratch_proto_mutex); + err = hs_alloc_scratch(pd->hs_db, &g_scratch_proto); + SCMutexUnlock(&g_scratch_proto_mutex); + if (err != HS_SUCCESS) { + SCLogError("failed to allocate scratch"); + SCMutexUnlock(&g_db_table_mutex); + goto error; + } + + err = hs_database_size(pd->hs_db, &ctx->hs_db_size); + if (err != HS_SUCCESS) { + SCLogError("failed to query database size"); + SCMutexUnlock(&g_db_table_mutex); + goto error; + } + + mpm_ctx->memory_cnt++; + mpm_ctx->memory_size += ctx->hs_db_size; + + SCLogDebug("Built %" PRIu32 " patterns into a database of size %" PRIuMAX + " bytes", mpm_ctx->pattern_cnt, (uintmax_t)ctx->hs_db_size); + + /* Cache this database globally for later. */ + pd->ref_cnt = 1; + int r = HashTableAdd(g_db_table, pd, 1); + SCMutexUnlock(&g_db_table_mutex); + if (r < 0) + goto error; + + SCHSFreeCompileData(cd); + return 0; + +error: + if (pd) { + PatternDatabaseFree(pd); + } + if (cd) { + SCHSFreeCompileData(cd); + } + return -1; +} + +/** + * \brief Init the mpm thread context. + * + * \param mpm_ctx Pointer to the mpm context. + * \param mpm_thread_ctx Pointer to the mpm thread context. + */ +void SCHSInitThreadCtx(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx) +{ + memset(mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + + SCHSThreadCtx *ctx = SCMalloc(sizeof(SCHSThreadCtx)); + if (ctx == NULL) { + exit(EXIT_FAILURE); + } + mpm_thread_ctx->ctx = ctx; + + memset(ctx, 0, sizeof(SCHSThreadCtx)); + mpm_thread_ctx->memory_cnt++; + mpm_thread_ctx->memory_size += sizeof(SCHSThreadCtx); + + ctx->scratch = NULL; + ctx->scratch_size = 0; + + SCMutexLock(&g_scratch_proto_mutex); + + if (g_scratch_proto == NULL) { + /* There is no scratch prototype: this means that we have not compiled + * any Hyperscan databases. */ + SCMutexUnlock(&g_scratch_proto_mutex); + SCLogDebug("No scratch space prototype"); + return; + } + + hs_error_t err = hs_clone_scratch(g_scratch_proto, + (hs_scratch_t **)&ctx->scratch); + + SCMutexUnlock(&g_scratch_proto_mutex); + + if (err != HS_SUCCESS) { + FatalError("Unable to clone scratch prototype"); + } + + err = hs_scratch_size(ctx->scratch, &ctx->scratch_size); + if (err != HS_SUCCESS) { + FatalError("Unable to query scratch size"); + } + + mpm_thread_ctx->memory_cnt++; + mpm_thread_ctx->memory_size += ctx->scratch_size; +} + +/** + * \brief Initialize the HS context. + * + * \param mpm_ctx Mpm context. + */ +void SCHSInitCtx(MpmCtx *mpm_ctx) +{ + if (mpm_ctx->ctx != NULL) + return; + + mpm_ctx->ctx = SCMalloc(sizeof(SCHSCtx)); + if (mpm_ctx->ctx == NULL) { + exit(EXIT_FAILURE); + } + memset(mpm_ctx->ctx, 0, sizeof(SCHSCtx)); + + mpm_ctx->memory_cnt++; + mpm_ctx->memory_size += sizeof(SCHSCtx); + + /* initialize the hash we use to speed up pattern insertions */ + SCHSCtx *ctx = (SCHSCtx *)mpm_ctx->ctx; + ctx->init_hash = SCMalloc(sizeof(SCHSPattern *) * INIT_HASH_SIZE); + if (ctx->init_hash == NULL) { + exit(EXIT_FAILURE); + } + memset(ctx->init_hash, 0, sizeof(SCHSPattern *) * INIT_HASH_SIZE); +} + +/** + * \brief Destroy the mpm thread context. + * + * \param mpm_ctx Pointer to the mpm context. + * \param mpm_thread_ctx Pointer to the mpm thread context. + */ +void SCHSDestroyThreadCtx(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx) +{ + SCHSPrintSearchStats(mpm_thread_ctx); + + if (mpm_thread_ctx->ctx != NULL) { + SCHSThreadCtx *thr_ctx = (SCHSThreadCtx *)mpm_thread_ctx->ctx; + + if (thr_ctx->scratch != NULL) { + hs_free_scratch(thr_ctx->scratch); + mpm_thread_ctx->memory_cnt--; + mpm_thread_ctx->memory_size -= thr_ctx->scratch_size; + } + + SCFree(mpm_thread_ctx->ctx); + mpm_thread_ctx->ctx = NULL; + mpm_thread_ctx->memory_cnt--; + mpm_thread_ctx->memory_size -= sizeof(SCHSThreadCtx); + } +} + +/** + * \brief Destroy the mpm context. + * + * \param mpm_ctx Pointer to the mpm context. + */ +void SCHSDestroyCtx(MpmCtx *mpm_ctx) +{ + SCHSCtx *ctx = (SCHSCtx *)mpm_ctx->ctx; + if (ctx == NULL) + return; + + if (ctx->init_hash != NULL) { + SCFree(ctx->init_hash); + ctx->init_hash = NULL; + mpm_ctx->memory_cnt--; + mpm_ctx->memory_size -= (INIT_HASH_SIZE * sizeof(SCHSPattern *)); + } + + /* Decrement pattern database ref count, and delete it entirely if the + * count has dropped to zero. */ + SCMutexLock(&g_db_table_mutex); + PatternDatabase *pd = ctx->pattern_db; + if (pd) { + BUG_ON(pd->ref_cnt == 0); + pd->ref_cnt--; + if (pd->ref_cnt == 0) { + HashTableRemove(g_db_table, pd, 1); + PatternDatabaseFree(pd); + } + } + SCMutexUnlock(&g_db_table_mutex); + + SCFree(mpm_ctx->ctx); + mpm_ctx->memory_cnt--; + mpm_ctx->memory_size -= sizeof(SCHSCtx); +} + +typedef struct SCHSCallbackCtx_ { + SCHSCtx *ctx; + void *pmq; + uint32_t match_count; +} SCHSCallbackCtx; + +/* Hyperscan MPM match event handler */ +static int SCHSMatchEvent(unsigned int id, unsigned long long from, + unsigned long long to, unsigned int flags, + void *ctx) +{ + SCHSCallbackCtx *cctx = ctx; + PrefilterRuleStore *pmq = cctx->pmq; + const PatternDatabase *pd = cctx->ctx->pattern_db; + const SCHSPattern *pat = pd->parray[id]; + + SCLogDebug("Hyperscan Match %" PRIu32 ": id=%" PRIu32 " @ %" PRIuMAX + " (pat id=%" PRIu32 ")", + cctx->match_count, (uint32_t)id, (uintmax_t)to, pat->id); + + PrefilterAddSids(pmq, pat->sids, pat->sids_size); + + cctx->match_count++; + return 0; +} + +/** + * \brief The Hyperscan search function. + * + * \param mpm_ctx Pointer to the mpm context. + * \param mpm_thread_ctx Pointer to the mpm thread context. + * \param pmq Pointer to the Pattern Matcher Queue to hold + * search matches. + * \param buf Buffer to be searched. + * \param buflen Buffer length. + * + * \retval matches Match count. + */ +uint32_t SCHSSearch(const MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx, + PrefilterRuleStore *pmq, const uint8_t *buf, const uint32_t buflen) +{ + uint32_t ret = 0; + SCHSCtx *ctx = (SCHSCtx *)mpm_ctx->ctx; + SCHSThreadCtx *hs_thread_ctx = (SCHSThreadCtx *)(mpm_thread_ctx->ctx); + const PatternDatabase *pd = ctx->pattern_db; + + if (unlikely(buflen == 0)) { + return 0; + } + + SCHSCallbackCtx cctx = {.ctx = ctx, .pmq = pmq, .match_count = 0}; + + /* scratch should have been cloned from g_scratch_proto at thread init. */ + hs_scratch_t *scratch = hs_thread_ctx->scratch; + BUG_ON(pd->hs_db == NULL); + BUG_ON(scratch == NULL); + + hs_error_t err = hs_scan(pd->hs_db, (const char *)buf, buflen, 0, scratch, + SCHSMatchEvent, &cctx); + if (err != HS_SUCCESS) { + /* An error value (other than HS_SCAN_TERMINATED) from hs_scan() + * indicates that it was passed an invalid database or scratch region, + * which is not something we can recover from at scan time. */ + SCLogError("Hyperscan returned error %d", err); + exit(EXIT_FAILURE); + } else { + ret = cctx.match_count; + } + + return ret; +} + +/** + * \brief Add a case insensitive pattern. Although we have different calls for + * adding case sensitive and insensitive patterns, we make a single call + * for either case. No special treatment for either case. + * + * \param mpm_ctx Pointer to the mpm context. + * \param pat The pattern to add. + * \param patlen The pattern length. + * \param offset The pattern offset. + * \param depth The pattern depth. + * \param pid The pattern id. + * \param sid The pattern signature id. + * \param flags Flags associated with this pattern. + * + * \retval 0 On success. + * \retval -1 On failure. + */ +int SCHSAddPatternCI(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen, + uint16_t offset, uint16_t depth, uint32_t pid, + SigIntId sid, uint8_t flags) +{ + flags |= MPM_PATTERN_FLAG_NOCASE; + return SCHSAddPattern(mpm_ctx, pat, patlen, offset, depth, pid, sid, flags); +} + +/** + * \brief Add a case sensitive pattern. Although we have different calls for + * adding case sensitive and insensitive patterns, we make a single call + * for either case. No special treatment for either case. + * + * \param mpm_ctx Pointer to the mpm context. + * \param pat The pattern to add. + * \param patlen The pattern length. + * \param offset The pattern offset. + * \param depth The pattern depth. + * \param pid The pattern id. + * \param sid The pattern signature id. + * \param flags Flags associated with this pattern. + * + * \retval 0 On success. + * \retval -1 On failure. + */ +int SCHSAddPatternCS(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen, + uint16_t offset, uint16_t depth, uint32_t pid, + SigIntId sid, uint8_t flags) +{ + return SCHSAddPattern(mpm_ctx, pat, patlen, offset, depth, pid, sid, flags); +} + +void SCHSPrintSearchStats(MpmThreadCtx *mpm_thread_ctx) +{ + return; +} + +void SCHSPrintInfo(MpmCtx *mpm_ctx) +{ + SCHSCtx *ctx = (SCHSCtx *)mpm_ctx->ctx; + + printf("MPM HS Information:\n"); + printf("Memory allocs: %" PRIu32 "\n", mpm_ctx->memory_cnt); + printf("Memory alloced: %" PRIu32 "\n", mpm_ctx->memory_size); + printf(" Sizeof:\n"); + printf(" MpmCtx %" PRIuMAX "\n", (uintmax_t)sizeof(MpmCtx)); + printf(" SCHSCtx: %" PRIuMAX "\n", (uintmax_t)sizeof(SCHSCtx)); + printf(" SCHSPattern %" PRIuMAX "\n", (uintmax_t)sizeof(SCHSPattern)); + printf("Unique Patterns: %" PRIu32 "\n", mpm_ctx->pattern_cnt); + printf("Smallest: %" PRIu32 "\n", mpm_ctx->minlen); + printf("Largest: %" PRIu32 "\n", mpm_ctx->maxlen); + printf("\n"); + + if (ctx) { + PatternDatabase *pd = ctx->pattern_db; + char *db_info = NULL; + if (hs_database_info(pd->hs_db, &db_info) == HS_SUCCESS) { + printf("HS Database Info: %s\n", db_info); + SCFree(db_info); + } + printf("HS Database Size: %" PRIuMAX " bytes\n", + (uintmax_t)ctx->hs_db_size); + } + + printf("\n"); +} + +/************************** Mpm Registration ***************************/ + +/** + * \brief Register the Hyperscan MPM. + */ +void MpmHSRegister(void) +{ + mpm_table[MPM_HS].name = "hs"; + mpm_table[MPM_HS].InitCtx = SCHSInitCtx; + mpm_table[MPM_HS].InitThreadCtx = SCHSInitThreadCtx; + mpm_table[MPM_HS].DestroyCtx = SCHSDestroyCtx; + mpm_table[MPM_HS].DestroyThreadCtx = SCHSDestroyThreadCtx; + mpm_table[MPM_HS].AddPattern = SCHSAddPatternCS; + mpm_table[MPM_HS].AddPatternNocase = SCHSAddPatternCI; + mpm_table[MPM_HS].Prepare = SCHSPreparePatterns; + mpm_table[MPM_HS].Search = SCHSSearch; + mpm_table[MPM_HS].PrintCtx = SCHSPrintInfo; + mpm_table[MPM_HS].PrintThreadCtx = SCHSPrintSearchStats; + mpm_table[MPM_HS].RegisterUnittests = SCHSRegisterTests; + + /* Set Hyperscan memory allocators */ + SCHSSetAllocators(); +} + +/** + * \brief Clean up global memory used by all Hyperscan MPM instances. + * + * Currently, this is just the global scratch prototype. + */ +void MpmHSGlobalCleanup(void) +{ + SCMutexLock(&g_scratch_proto_mutex); + if (g_scratch_proto) { + SCLogDebug("Cleaning up Hyperscan global scratch"); + hs_free_scratch(g_scratch_proto); + g_scratch_proto = NULL; + } + SCMutexUnlock(&g_scratch_proto_mutex); + + SCMutexLock(&g_db_table_mutex); + if (g_db_table != NULL) { + SCLogDebug("Clearing Hyperscan database cache"); + HashTableFree(g_db_table); + g_db_table = NULL; + } + SCMutexUnlock(&g_db_table_mutex); +} + +/*************************************Unittests********************************/ + +#ifdef UNITTESTS +#include "detect-engine-alert.h" + +static int SCHSTest01(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 1 match */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "abcdefghjiklmnopqrstuvwxyz"; + + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 1) + result = 1; + else + printf("1 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest02(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 1 match */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abce", 4, 0, 0, 0, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "abcdefghjiklmnopqrstuvwxyz"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 0) + result = 1; + else + printf("0 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest03(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0x00, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 1 match */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0); + /* 1 match */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcde", 4, 0, 0, 1, 0, 0); + /* 1 match */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"fghj", 4, 0, 0, 2, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "abcdefghjiklmnopqrstuvwxyz"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 3) + result = 1; + else + printf("3 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest04(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0); + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcdegh", 6, 0, 0, 1, 0, 0); + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"fghjxyz", 7, 0, 0, 2, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "abcdefghjiklmnopqrstuvwxyz"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 1) + result = 1; + else + printf("1 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest05(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0x00, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + MpmAddPatternCI(&mpm_ctx, (uint8_t *)"ABCD", 4, 0, 0, 0, 0, 0); + MpmAddPatternCI(&mpm_ctx, (uint8_t *)"bCdEfG", 6, 0, 0, 1, 0, 0); + MpmAddPatternCI(&mpm_ctx, (uint8_t *)"fghJikl", 7, 0, 0, 2, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "abcdefghjiklmnopqrstuvwxyz"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 3) + result = 1; + else + printf("3 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest06(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "abcd"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 1) + result = 1; + else + printf("1 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest07(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* should match 30 times */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"A", 1, 0, 0, 0, 0, 0); + /* should match 29 times */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 1, 0, 0); + /* should match 28 times */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAA", 3, 0, 0, 2, 0, 0); + /* 26 */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAA", 5, 0, 0, 3, 0, 0); + /* 21 */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAA", 10, 0, 0, 4, 0, 0); + /* 1 */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 30, + 0, 0, 5, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 6) + result = 1; + else + printf("6 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest08(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 1 match */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + uint32_t cnt = + SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)"a", 1); + + if (cnt == 0) + result = 1; + else + printf("0 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest09(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 1 match */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"ab", 2, 0, 0, 0, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + uint32_t cnt = + SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)"ab", 2); + + if (cnt == 1) + result = 1; + else + printf("1 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest10(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 1 match */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcdefgh", 8, 0, 0, 0, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "abcdefgh" + "01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 1) + result = 1; + else + printf("1 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest11(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"he", 2, 0, 0, 1, 0, 0) == -1) + goto end; + if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"she", 3, 0, 0, 2, 0, 0) == -1) + goto end; + if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"his", 3, 0, 0, 3, 0, 0) == -1) + goto end; + if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"hers", 4, 0, 0, 4, 0, 0) == -1) + goto end; + PmqSetup(&pmq); + + if (SCHSPreparePatterns(&mpm_ctx) == -1) + goto end; + + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + result = 1; + + const char *buf = "he"; + result &= (SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)) == 1); + buf = "she"; + result &= (SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)) == 2); + buf = "his"; + result &= (SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)) == 1); + buf = "hers"; + result &= (SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)) == 2); + +end: + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest12(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0x00, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 1 match */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"wxyz", 4, 0, 0, 0, 0, 0); + /* 1 match */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"vwxyz", 5, 0, 0, 1, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "abcdefghijklmnopqrstuvwxyz"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 2) + result = 1; + else + printf("2 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest13(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0x00, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 1 match */ + const char pat[] = "abcdefghijklmnopqrstuvwxyzABCD"; + MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, sizeof(pat) - 1, 0, 0, 0, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "abcdefghijklmnopqrstuvwxyzABCD"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 1) + result = 1; + else + printf("1 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest14(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0x00, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 1 match */ + const char pat[] = "abcdefghijklmnopqrstuvwxyzABCDE"; + MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, sizeof(pat) - 1, 0, 0, 0, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "abcdefghijklmnopqrstuvwxyzABCDE"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 1) + result = 1; + else + printf("1 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest15(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0x00, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 1 match */ + const char pat[] = "abcdefghijklmnopqrstuvwxyzABCDEF"; + MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, sizeof(pat) - 1, 0, 0, 0, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "abcdefghijklmnopqrstuvwxyzABCDEF"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 1) + result = 1; + else + printf("1 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest16(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0x00, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 1 match */ + const char pat[] = "abcdefghijklmnopqrstuvwxyzABC"; + MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, sizeof(pat) - 1, 0, 0, 0, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "abcdefghijklmnopqrstuvwxyzABC"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 1) + result = 1; + else + printf("1 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest17(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0x00, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 1 match */ + const char pat[] = "abcdefghijklmnopqrstuvwxyzAB"; + MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, sizeof(pat) - 1, 0, 0, 0, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "abcdefghijklmnopqrstuvwxyzAB"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 1) + result = 1; + else + printf("1 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest18(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0x00, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 1 match */ + const char pat[] = "abcde" + "fghij" + "klmno" + "pqrst" + "uvwxy" + "z"; + MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, sizeof(pat) - 1, 0, 0, 0, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "abcde" + "fghij" + "klmno" + "pqrst" + "uvwxy" + "z"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 1) + result = 1; + else + printf("1 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest19(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0x00, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 1 */ + const char pat[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, sizeof(pat) - 1, 0, 0, 0, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 1) + result = 1; + else + printf("1 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest20(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0x00, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 1 */ + const char pat[] = "AAAAA" + "AAAAA" + "AAAAA" + "AAAAA" + "AAAAA" + "AAAAA" + "AA"; + MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, sizeof(pat) - 1, 0, 0, 0, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "AAAAA" + "AAAAA" + "AAAAA" + "AAAAA" + "AAAAA" + "AAAAA" + "AA"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 1) + result = 1; + else + printf("1 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest21(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0x00, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 1 */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 0, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + uint32_t cnt = + SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)"AA", 2); + + if (cnt == 1) + result = 1; + else + printf("1 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest22(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0x00, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 1 match */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0); + /* 1 match */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcde", 5, 0, 0, 1, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "abcdefghijklmnopqrstuvwxyz"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 2) + result = 1; + else + printf("2 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest23(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0x00, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 1 */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 0, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + uint32_t cnt = + SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)"aa", 2); + + if (cnt == 0) + result = 1; + else + printf("1 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest24(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0x00, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 1 */ + MpmAddPatternCI(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 0, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + uint32_t cnt = + SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)"aa", 2); + + if (cnt == 1) + result = 1; + else + printf("1 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest25(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0x00, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + MpmAddPatternCI(&mpm_ctx, (uint8_t *)"ABCD", 4, 0, 0, 0, 0, 0); + MpmAddPatternCI(&mpm_ctx, (uint8_t *)"bCdEfG", 6, 0, 0, 1, 0, 0); + MpmAddPatternCI(&mpm_ctx, (uint8_t *)"fghiJkl", 7, 0, 0, 2, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 3) + result = 1; + else + printf("3 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest26(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0x00, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + MpmAddPatternCI(&mpm_ctx, (uint8_t *)"Works", 5, 0, 0, 0, 0, 0); + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"Works", 5, 0, 0, 1, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "works"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 1) + result = 1; + else + printf("3 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest27(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 0 match */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"ONE", 3, 0, 0, 0, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "tone"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 0) + result = 1; + else + printf("0 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest28(void) +{ + int result = 0; + MpmCtx mpm_ctx; + MpmThreadCtx mpm_thread_ctx; + PrefilterRuleStore pmq; + + memset(&mpm_ctx, 0, sizeof(MpmCtx)); + memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx)); + MpmInitCtx(&mpm_ctx, MPM_HS); + + /* 0 match */ + MpmAddPatternCS(&mpm_ctx, (uint8_t *)"one", 3, 0, 0, 0, 0, 0); + PmqSetup(&pmq); + + SCHSPreparePatterns(&mpm_ctx); + SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx); + + const char *buf = "tONE"; + uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf, + strlen(buf)); + + if (cnt == 0) + result = 1; + else + printf("0 != %" PRIu32 " ", cnt); + + SCHSDestroyCtx(&mpm_ctx); + SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx); + PmqFree(&pmq); + return result; +} + +static int SCHSTest29(void) +{ + uint8_t buf[] = "onetwothreefourfivesixseveneightnine"; + uint16_t buflen = sizeof(buf) - 1; + Packet *p = NULL; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx = NULL; + int result = 0; + + memset(&th_v, 0, sizeof(th_v)); + p = UTHBuildPacket(buf, buflen, IPPROTO_TCP); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + de_ctx->mpm_matcher = MPM_HS; + + de_ctx->flags |= DE_QUIET; + + de_ctx->sig_list = SigInit( + de_ctx, "alert tcp any any -> any any " + "(content:\"onetwothreefourfivesixseveneightnine\"; sid:1;)"); + if (de_ctx->sig_list == NULL) + goto end; + de_ctx->sig_list->next = + SigInit(de_ctx, "alert tcp any any -> any any " + "(content:\"onetwothreefourfivesixseveneightnine\"; " + "fast_pattern:3,3; sid:2;)"); + if (de_ctx->sig_list->next == NULL) + goto end; + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (PacketAlertCheck(p, 1) != 1) { + printf("if (PacketAlertCheck(p, 1) != 1) failure\n"); + goto end; + } + if (PacketAlertCheck(p, 2) != 1) { + printf("if (PacketAlertCheck(p, 1) != 2) failure\n"); + goto end; + } + + result = 1; +end: + if (de_ctx != NULL) { + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + + DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); + DetectEngineCtxFree(de_ctx); + } + + UTHFreePackets(&p, 1); + return result; +} + +#endif /* UNITTESTS */ + +void SCHSRegisterTests(void) +{ +#ifdef UNITTESTS + UtRegisterTest("SCHSTest01", SCHSTest01); + UtRegisterTest("SCHSTest02", SCHSTest02); + UtRegisterTest("SCHSTest03", SCHSTest03); + UtRegisterTest("SCHSTest04", SCHSTest04); + UtRegisterTest("SCHSTest05", SCHSTest05); + UtRegisterTest("SCHSTest06", SCHSTest06); + UtRegisterTest("SCHSTest07", SCHSTest07); + UtRegisterTest("SCHSTest08", SCHSTest08); + UtRegisterTest("SCHSTest09", SCHSTest09); + UtRegisterTest("SCHSTest10", SCHSTest10); + UtRegisterTest("SCHSTest11", SCHSTest11); + UtRegisterTest("SCHSTest12", SCHSTest12); + UtRegisterTest("SCHSTest13", SCHSTest13); + UtRegisterTest("SCHSTest14", SCHSTest14); + UtRegisterTest("SCHSTest15", SCHSTest15); + UtRegisterTest("SCHSTest16", SCHSTest16); + UtRegisterTest("SCHSTest17", SCHSTest17); + UtRegisterTest("SCHSTest18", SCHSTest18); + UtRegisterTest("SCHSTest19", SCHSTest19); + UtRegisterTest("SCHSTest20", SCHSTest20); + UtRegisterTest("SCHSTest21", SCHSTest21); + UtRegisterTest("SCHSTest22", SCHSTest22); + UtRegisterTest("SCHSTest23", SCHSTest23); + UtRegisterTest("SCHSTest24", SCHSTest24); + UtRegisterTest("SCHSTest25", SCHSTest25); + UtRegisterTest("SCHSTest26", SCHSTest26); + UtRegisterTest("SCHSTest27", SCHSTest27); + UtRegisterTest("SCHSTest28", SCHSTest28); + UtRegisterTest("SCHSTest29", SCHSTest29); +#endif + + return; +} + +#endif /* BUILD_HYPERSCAN */ |