suricata
util-mpm-hs.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2016 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 Jim Xu <jim.xu@windriver.com>
22  * \author Justin Viiret <justin.viiret@intel.com>
23  *
24  * MPM pattern matcher that calls the Hyperscan regex matcher.
25  */
26 
27 #include "suricata-common.h"
28 #include "suricata.h"
29 
30 #include "detect.h"
31 #include "detect-parse.h"
32 #include "detect-engine.h"
33 
34 #include "conf.h"
35 #include "util-debug.h"
36 #include "util-unittest.h"
37 #include "util-unittest-helper.h"
38 #include "util-memcmp.h"
39 #include "util-mpm-hs.h"
40 #include "util-memcpy.h"
41 #include "util-hash.h"
42 #include "util-hash-lookup3.h"
43 #include "util-hyperscan.h"
44 
45 #ifdef BUILD_HYPERSCAN
46 
47 #include <hs.h>
48 
49 void SCHSInitCtx(MpmCtx *);
50 void SCHSInitThreadCtx(MpmCtx *, MpmThreadCtx *);
51 void SCHSDestroyCtx(MpmCtx *);
52 void SCHSDestroyThreadCtx(MpmCtx *, MpmThreadCtx *);
53 int SCHSAddPatternCI(MpmCtx *, uint8_t *, uint16_t, uint16_t, uint16_t,
54  uint32_t, SigIntId, uint8_t);
55 int SCHSAddPatternCS(MpmCtx *, uint8_t *, uint16_t, uint16_t, uint16_t,
56  uint32_t, SigIntId, uint8_t);
57 int SCHSPreparePatterns(MpmCtx *mpm_ctx);
58 uint32_t SCHSSearch(const MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx,
59  PrefilterRuleStore *pmq, const uint8_t *buf, const uint32_t buflen);
60 void SCHSPrintInfo(MpmCtx *mpm_ctx);
61 void SCHSPrintSearchStats(MpmThreadCtx *mpm_thread_ctx);
62 void SCHSRegisterTests(void);
63 
64 /* size of the hash table used to speed up pattern insertions initially */
65 #define INIT_HASH_SIZE 65536
66 
67 /* Initial size of the global database hash (used for de-duplication). */
68 #define INIT_DB_HASH_SIZE 1000
69 
70 /* Global prototype scratch, built incrementally as Hyperscan databases are
71  * built and then cloned for each thread context. Access is serialised via
72  * g_scratch_proto_mutex. */
73 static hs_scratch_t *g_scratch_proto = NULL;
74 static SCMutex g_scratch_proto_mutex = SCMUTEX_INITIALIZER;
75 
76 /* Global hash table of Hyperscan databases, used for de-duplication. Access is
77  * serialised via g_db_table_mutex. */
78 static HashTable *g_db_table = NULL;
79 static SCMutex g_db_table_mutex = SCMUTEX_INITIALIZER;
80 
81 /**
82  * \internal
83  * \brief Wraps SCMalloc (which is a macro) so that it can be passed to
84  * hs_set_allocator() for Hyperscan's use.
85  */
86 static void *SCHSMalloc(size_t size)
87 {
88  return SCMalloc(size);
89 }
90 
91 /**
92  * \internal
93  * \brief Wraps SCFree (which is a macro) so that it can be passed to
94  * hs_set_allocator() for Hyperscan's use.
95  */
96 static void SCHSFree(void *ptr)
97 {
98  SCFree(ptr);
99 }
100 
101 /** \brief Register Suricata malloc/free with Hyperscan.
102  *
103  * Requests that Hyperscan use Suricata's allocator for allocation of
104  * databases, scratch space, etc.
105  */
106 static void SCHSSetAllocators(void)
107 {
108  hs_error_t err = hs_set_allocator(SCHSMalloc, SCHSFree);
109  if (err != HS_SUCCESS) {
110  SCLogError(SC_ERR_FATAL, "Failed to set Hyperscan allocator.");
111  exit(EXIT_FAILURE);
112  }
113 }
114 
115 /**
116  * \internal
117  * \brief Creates a hash of the pattern. We use it for the hashing process
118  * during the initial pattern insertion time, to cull duplicate sigs.
119  *
120  * \param pat Pointer to the pattern.
121  * \param patlen Pattern length.
122  *
123  * \retval hash A 32 bit unsigned hash.
124  */
125 static inline uint32_t SCHSInitHashRaw(uint8_t *pat, uint16_t patlen)
126 {
127  uint32_t hash = patlen * pat[0];
128  if (patlen > 1)
129  hash += pat[1];
130 
131  return (hash % INIT_HASH_SIZE);
132 }
133 
134 /**
135  * \internal
136  * \brief Looks up a pattern. We use it for the hashing process during
137  * the initial pattern insertion time, to cull duplicate sigs.
138  *
139  * \param ctx Pointer to the HS ctx.
140  * \param pat Pointer to the pattern.
141  * \param patlen Pattern length.
142  * \param flags Flags. We don't need this.
143  *
144  * \retval hash A 32 bit unsigned hash.
145  */
146 static inline SCHSPattern *SCHSInitHashLookup(SCHSCtx *ctx, uint8_t *pat,
147  uint16_t patlen, uint16_t offset,
148  uint16_t depth, char flags,
149  uint32_t pid)
150 {
151  uint32_t hash = SCHSInitHashRaw(pat, patlen);
152 
153  if (ctx->init_hash == NULL) {
154  return NULL;
155  }
156 
157  SCHSPattern *t = ctx->init_hash[hash];
158  for (; t != NULL; t = t->next) {
159  /* Since Hyperscan uses offset/depth, we must distinguish between
160  * patterns with the same ID but different offset/depth here. */
161  if (t->id == pid && t->offset == offset && t->depth == depth) {
162  BUG_ON(t->len != patlen);
163  BUG_ON(SCMemcmp(t->original_pat, pat, patlen) != 0);
164  return t;
165  }
166  }
167 
168  return NULL;
169 }
170 
171 /**
172  * \internal
173  * \brief Allocates a new pattern instance.
174  *
175  * \param mpm_ctx Pointer to the mpm context.
176  *
177  * \retval p Pointer to the newly created pattern.
178  */
179 static inline SCHSPattern *SCHSAllocPattern(MpmCtx *mpm_ctx)
180 {
181  SCHSPattern *p = SCMalloc(sizeof(SCHSPattern));
182  if (unlikely(p == NULL)) {
183  exit(EXIT_FAILURE);
184  }
185  memset(p, 0, sizeof(SCHSPattern));
186 
187  mpm_ctx->memory_cnt++;
188  mpm_ctx->memory_size += sizeof(SCHSPattern);
189 
190  return p;
191 }
192 
193 /**
194  * \internal
195  * \brief Used to free SCHSPattern instances.
196  *
197  * \param mpm_ctx Pointer to the mpm context.
198  * \param p Pointer to the SCHSPattern instance to be freed.
199  * \param free Free the above pointer or not.
200  */
201 static inline void SCHSFreePattern(MpmCtx *mpm_ctx, SCHSPattern *p)
202 {
203  if (p != NULL && p->original_pat != NULL) {
204  SCFree(p->original_pat);
205  mpm_ctx->memory_cnt--;
206  mpm_ctx->memory_size -= p->len;
207  }
208 
209  if (p != NULL && p->sids != NULL) {
210  SCFree(p->sids);
211  }
212 
213  if (p != NULL) {
214  SCFree(p);
215  mpm_ctx->memory_cnt--;
216  mpm_ctx->memory_size -= sizeof(SCHSPattern);
217  }
218 }
219 
220 static inline uint32_t SCHSInitHash(SCHSPattern *p)
221 {
222  uint32_t hash = p->len * p->original_pat[0];
223  if (p->len > 1)
224  hash += p->original_pat[1];
225 
226  return (hash % INIT_HASH_SIZE);
227 }
228 
229 static inline int SCHSInitHashAdd(SCHSCtx *ctx, SCHSPattern *p)
230 {
231  uint32_t hash = SCHSInitHash(p);
232 
233  if (ctx->init_hash == NULL) {
234  return 0;
235  }
236 
237  if (ctx->init_hash[hash] == NULL) {
238  ctx->init_hash[hash] = p;
239  return 0;
240  }
241 
242  SCHSPattern *tt = NULL;
243  SCHSPattern *t = ctx->init_hash[hash];
244 
245  /* get the list tail */
246  do {
247  tt = t;
248  t = t->next;
249  } while (t != NULL);
250 
251  tt->next = p;
252 
253  return 0;
254 }
255 
256 /**
257  * \internal
258  * \brief Add a pattern to the mpm-hs context.
259  *
260  * \param mpm_ctx Mpm context.
261  * \param pat Pointer to the pattern.
262  * \param patlen Length of the pattern.
263  * \param pid Pattern id
264  * \param sid Signature id (internal id).
265  * \param flags Pattern's MPM_PATTERN_* flags.
266  *
267  * \retval 0 On success.
268  * \retval -1 On failure.
269  */
270 static int SCHSAddPattern(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
271  uint16_t offset, uint16_t depth, uint32_t pid,
272  SigIntId sid, uint8_t flags)
273 {
274  SCHSCtx *ctx = (SCHSCtx *)mpm_ctx->ctx;
275 
276  if (offset != 0) {
277  flags |= MPM_PATTERN_FLAG_OFFSET;
278  }
279  if (depth != 0) {
280  flags |= MPM_PATTERN_FLAG_DEPTH;
281  }
282 
283  if (patlen == 0) {
284  SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "pattern length 0");
285  return 0;
286  }
287 
288  /* check if we have already inserted this pattern */
289  SCHSPattern *p =
290  SCHSInitHashLookup(ctx, pat, patlen, offset, depth, flags, pid);
291  if (p == NULL) {
292  SCLogDebug("Allocing new pattern");
293 
294  /* p will never be NULL */
295  p = SCHSAllocPattern(mpm_ctx);
296 
297  p->len = patlen;
298  p->flags = flags;
299  p->id = pid;
300 
301  p->offset = offset;
302  p->depth = depth;
303 
304  p->original_pat = SCMalloc(patlen);
305  if (p->original_pat == NULL)
306  goto error;
307  mpm_ctx->memory_cnt++;
308  mpm_ctx->memory_size += patlen;
309  memcpy(p->original_pat, pat, patlen);
310 
311  /* put in the pattern hash */
312  SCHSInitHashAdd(ctx, p);
313 
314  mpm_ctx->pattern_cnt++;
315 
316  if (mpm_ctx->maxlen < patlen)
317  mpm_ctx->maxlen = patlen;
318 
319  if (mpm_ctx->minlen == 0) {
320  mpm_ctx->minlen = patlen;
321  } else {
322  if (mpm_ctx->minlen > patlen)
323  mpm_ctx->minlen = patlen;
324  }
325 
326  p->sids_size = 1;
327  p->sids = SCMalloc(p->sids_size * sizeof(SigIntId));
328  BUG_ON(p->sids == NULL);
329  p->sids[0] = sid;
330  } else {
331  /* TODO figure out how we can be called multiple times for the same CTX with the same sid */
332 
333  int found = 0;
334  uint32_t x = 0;
335  for (x = 0; x < p->sids_size; x++) {
336  if (p->sids[x] == sid) {
337  found = 1;
338  break;
339  }
340  }
341  if (!found) {
342  SigIntId *sids = SCRealloc(p->sids, (sizeof(SigIntId) * (p->sids_size + 1)));
343  BUG_ON(sids == NULL);
344  p->sids = sids;
345  p->sids[p->sids_size] = sid;
346  p->sids_size++;
347  }
348  }
349 
350  return 0;
351 
352 error:
353  SCHSFreePattern(mpm_ctx, p);
354  return -1;
355 }
356 
357 /**
358  * \brief Pattern database information used only as input to the Hyperscan
359  * compiler.
360  */
361 typedef struct SCHSCompileData_ {
362  unsigned int *ids;
363  unsigned int *flags;
364  char **expressions;
365  hs_expr_ext_t **ext;
366  unsigned int pattern_cnt;
367 } SCHSCompileData;
368 
369 static SCHSCompileData *SCHSAllocCompileData(unsigned int pattern_cnt)
370 {
371  SCHSCompileData *cd = SCMalloc(pattern_cnt * sizeof(SCHSCompileData));
372  if (cd == NULL) {
373  goto error;
374  }
375  memset(cd, 0, pattern_cnt * sizeof(SCHSCompileData));
376 
377  cd->pattern_cnt = pattern_cnt;
378 
379  cd->ids = SCMalloc(pattern_cnt * sizeof(unsigned int));
380  if (cd->ids == NULL) {
381  goto error;
382  }
383  memset(cd->ids, 0, pattern_cnt * sizeof(unsigned int));
384 
385  cd->flags = SCMalloc(pattern_cnt * sizeof(unsigned int));
386  if (cd->flags == NULL) {
387  goto error;
388  }
389  memset(cd->flags, 0, pattern_cnt * sizeof(unsigned int));
390 
391  cd->expressions = SCMalloc(pattern_cnt * sizeof(char *));
392  if (cd->expressions == NULL) {
393  goto error;
394  }
395  memset(cd->expressions, 0, pattern_cnt * sizeof(char *));
396 
397  cd->ext = SCMalloc(pattern_cnt * sizeof(hs_expr_ext_t *));
398  if (cd->ext == NULL) {
399  goto error;
400  }
401  memset(cd->ext, 0, pattern_cnt * sizeof(hs_expr_ext_t *));
402 
403  return cd;
404 
405 error:
406  SCLogDebug("SCHSCompileData alloc failed");
407  if (cd) {
408  SCFree(cd->ids);
409  SCFree(cd->flags);
410  SCFree(cd->expressions);
411  SCFree(cd->ext);
412  SCFree(cd);
413  }
414  return NULL;
415 }
416 
417 static void SCHSFreeCompileData(SCHSCompileData *cd)
418 {
419  if (cd == NULL) {
420  return;
421  }
422 
423  SCFree(cd->ids);
424  SCFree(cd->flags);
425  if (cd->expressions) {
426  for (unsigned int i = 0; i < cd->pattern_cnt; i++) {
427  SCFree(cd->expressions[i]);
428  }
429  SCFree(cd->expressions);
430  }
431  if (cd->ext) {
432  for (unsigned int i = 0; i < cd->pattern_cnt; i++) {
433  SCFree(cd->ext[i]);
434  }
435  SCFree(cd->ext);
436  }
437  SCFree(cd);
438 }
439 
440 typedef struct PatternDatabase_ {
441  SCHSPattern **parray;
442  hs_database_t *hs_db;
443  uint32_t pattern_cnt;
444 
445  /* Reference count: number of MPM contexts using this pattern database. */
446  uint32_t ref_cnt;
447 } PatternDatabase;
448 
449 static uint32_t SCHSPatternHash(const SCHSPattern *p, uint32_t hash)
450 {
451  BUG_ON(p->original_pat == NULL);
452  BUG_ON(p->sids == NULL);
453 
454  hash = hashlittle_safe(&p->len, sizeof(p->len), hash);
455  hash = hashlittle_safe(&p->flags, sizeof(p->flags), hash);
456  hash = hashlittle_safe(p->original_pat, p->len, hash);
457  hash = hashlittle_safe(&p->id, sizeof(p->id), hash);
458  hash = hashlittle_safe(&p->offset, sizeof(p->offset), hash);
459  hash = hashlittle_safe(&p->depth, sizeof(p->depth), hash);
460  hash = hashlittle_safe(&p->sids_size, sizeof(p->sids_size), hash);
461  hash = hashlittle_safe(p->sids, p->sids_size * sizeof(SigIntId), hash);
462  return hash;
463 }
464 
465 static char SCHSPatternCompare(const SCHSPattern *p1, const SCHSPattern *p2)
466 {
467  if ((p1->len != p2->len) || (p1->flags != p2->flags) ||
468  (p1->id != p2->id) || (p1->offset != p2->offset) ||
469  (p1->depth != p2->depth) || (p1->sids_size != p2->sids_size)) {
470  return 0;
471  }
472 
473  if (SCMemcmp(p1->original_pat, p2->original_pat, p1->len) != 0) {
474  return 0;
475  }
476 
477  if (SCMemcmp(p1->sids, p2->sids, p1->sids_size * sizeof(p1->sids[0])) !=
478  0) {
479  return 0;
480  }
481 
482  return 1;
483 }
484 
485 static uint32_t PatternDatabaseHash(HashTable *ht, void *data, uint16_t len)
486 {
487  const PatternDatabase *pd = data;
488  uint32_t hash = 0;
489  hash = hashword(&pd->pattern_cnt, 1, hash);
490 
491  for (uint32_t i = 0; i < pd->pattern_cnt; i++) {
492  hash = SCHSPatternHash(pd->parray[i], hash);
493  }
494 
495  hash %= ht->array_size;
496  return hash;
497 }
498 
499 static char PatternDatabaseCompare(void *data1, uint16_t len1, void *data2,
500  uint16_t len2)
501 {
502  const PatternDatabase *pd1 = data1;
503  const PatternDatabase *pd2 = data2;
504 
505  if (pd1->pattern_cnt != pd2->pattern_cnt) {
506  return 0;
507  }
508 
509  for (uint32_t i = 0; i < pd1->pattern_cnt; i++) {
510  if (SCHSPatternCompare(pd1->parray[i], pd2->parray[i]) == 0) {
511  return 0;
512  }
513  }
514 
515  return 1;
516 }
517 
518 static void PatternDatabaseFree(PatternDatabase *pd)
519 {
520  BUG_ON(pd->ref_cnt != 0);
521 
522  if (pd->parray != NULL) {
523  for (uint32_t i = 0; i < pd->pattern_cnt; i++) {
524  SCHSPattern *p = pd->parray[i];
525  if (p != NULL) {
526  SCFree(p->original_pat);
527  SCFree(p->sids);
528  SCFree(p);
529  }
530  }
531  SCFree(pd->parray);
532  }
533 
534  hs_free_database(pd->hs_db);
535 
536  SCFree(pd);
537 }
538 
539 static void PatternDatabaseTableFree(void *data)
540 {
541  /* Stub function handed to hash table; actual freeing of PatternDatabase
542  * structures is done in MPM destruction when the ref_cnt drops to zero. */
543 }
544 
545 static PatternDatabase *PatternDatabaseAlloc(uint32_t pattern_cnt)
546 {
547  PatternDatabase *pd = SCMalloc(sizeof(PatternDatabase));
548  if (pd == NULL) {
549  return NULL;
550  }
551  memset(pd, 0, sizeof(PatternDatabase));
552  pd->pattern_cnt = pattern_cnt;
553  pd->ref_cnt = 0;
554  pd->hs_db = NULL;
555 
556  /* alloc the pattern array */
557  pd->parray =
558  (SCHSPattern **)SCMalloc(pd->pattern_cnt * sizeof(SCHSPattern *));
559  if (pd->parray == NULL) {
560  SCFree(pd);
561  return NULL;
562  }
563  memset(pd->parray, 0, pd->pattern_cnt * sizeof(SCHSPattern *));
564 
565  return pd;
566 }
567 
568 /**
569  * \brief Process the patterns added to the mpm, and create the internal tables.
570  *
571  * \param mpm_ctx Pointer to the mpm context.
572  */
573 int SCHSPreparePatterns(MpmCtx *mpm_ctx)
574 {
575  SCHSCtx *ctx = (SCHSCtx *)mpm_ctx->ctx;
576 
577  if (mpm_ctx->pattern_cnt == 0 || ctx->init_hash == NULL) {
578  SCLogDebug("no patterns supplied to this mpm_ctx");
579  return 0;
580  }
581 
582  hs_error_t err;
583  hs_compile_error_t *compile_err = NULL;
584  SCHSCompileData *cd = NULL;
585  PatternDatabase *pd = NULL;
586 
587  cd = SCHSAllocCompileData(mpm_ctx->pattern_cnt);
588  if (cd == NULL) {
589  goto error;
590  }
591 
592  pd = PatternDatabaseAlloc(mpm_ctx->pattern_cnt);
593  if (pd == NULL) {
594  goto error;
595  }
596 
597  /* populate the pattern array with the patterns in the hash */
598  for (uint32_t i = 0, p = 0; i < INIT_HASH_SIZE; i++) {
599  SCHSPattern *node = ctx->init_hash[i], *nnode = NULL;
600  while (node != NULL) {
601  nnode = node->next;
602  node->next = NULL;
603  pd->parray[p++] = node;
604  node = nnode;
605  }
606  }
607 
608  /* we no longer need the hash, so free its memory */
609  SCFree(ctx->init_hash);
610  ctx->init_hash = NULL;
611 
612  /* Serialise whole database compilation as a relatively easy way to ensure
613  * dedupe is safe. */
614  SCMutexLock(&g_db_table_mutex);
615 
616  /* Init global pattern database hash if necessary. */
617  if (g_db_table == NULL) {
618  g_db_table = HashTableInit(INIT_DB_HASH_SIZE, PatternDatabaseHash,
619  PatternDatabaseCompare,
620  PatternDatabaseTableFree);
621  if (g_db_table == NULL) {
622  SCMutexUnlock(&g_db_table_mutex);
623  goto error;
624  }
625  }
626 
627  /* Check global hash table to see if we've seen this pattern database
628  * before, and reuse the Hyperscan database if so. */
629  PatternDatabase *pd_cached = HashTableLookup(g_db_table, pd, 1);
630 
631  if (pd_cached != NULL) {
632  SCLogDebug("Reusing cached database %p with %" PRIu32
633  " patterns (ref_cnt=%" PRIu32 ")",
634  pd_cached->hs_db, pd_cached->pattern_cnt,
635  pd_cached->ref_cnt);
636  pd_cached->ref_cnt++;
637  ctx->pattern_db = pd_cached;
638  SCMutexUnlock(&g_db_table_mutex);
639  PatternDatabaseFree(pd);
640  SCHSFreeCompileData(cd);
641  return 0;
642  }
643 
644  BUG_ON(ctx->pattern_db != NULL); /* already built? */
645 
646  for (uint32_t i = 0; i < pd->pattern_cnt; i++) {
647  const SCHSPattern *p = pd->parray[i];
648 
649  cd->ids[i] = i;
650  cd->flags[i] = HS_FLAG_SINGLEMATCH;
651  if (p->flags & MPM_PATTERN_FLAG_NOCASE) {
652  cd->flags[i] |= HS_FLAG_CASELESS;
653  }
654 
655  cd->expressions[i] = HSRenderPattern(p->original_pat, p->len);
656 
658  cd->ext[i] = SCMalloc(sizeof(hs_expr_ext_t));
659  if (cd->ext[i] == NULL) {
660  SCMutexUnlock(&g_db_table_mutex);
661  goto error;
662  }
663  memset(cd->ext[i], 0, sizeof(hs_expr_ext_t));
664 
665  if (p->flags & MPM_PATTERN_FLAG_OFFSET) {
666  cd->ext[i]->flags |= HS_EXT_FLAG_MIN_OFFSET;
667  cd->ext[i]->min_offset = p->offset + p->len;
668  }
669  if (p->flags & MPM_PATTERN_FLAG_DEPTH) {
670  cd->ext[i]->flags |= HS_EXT_FLAG_MAX_OFFSET;
671  cd->ext[i]->max_offset = p->offset + p->depth;
672  }
673  }
674  }
675 
676  BUG_ON(mpm_ctx->pattern_cnt == 0);
677 
678  err = hs_compile_ext_multi((const char *const *)cd->expressions, cd->flags,
679  cd->ids, (const hs_expr_ext_t *const *)cd->ext,
680  cd->pattern_cnt, HS_MODE_BLOCK, NULL, &pd->hs_db,
681  &compile_err);
682 
683  if (err != HS_SUCCESS) {
684  SCLogError(SC_ERR_FATAL, "failed to compile hyperscan database");
685  if (compile_err) {
686  SCLogError(SC_ERR_FATAL, "compile error: %s", compile_err->message);
687  }
688  hs_free_compile_error(compile_err);
689  SCMutexUnlock(&g_db_table_mutex);
690  goto error;
691  }
692 
693  ctx->pattern_db = pd;
694 
695  SCMutexLock(&g_scratch_proto_mutex);
696  err = hs_alloc_scratch(pd->hs_db, &g_scratch_proto);
697  SCMutexUnlock(&g_scratch_proto_mutex);
698  if (err != HS_SUCCESS) {
699  SCLogError(SC_ERR_FATAL, "failed to allocate scratch");
700  SCMutexUnlock(&g_db_table_mutex);
701  goto error;
702  }
703 
704  err = hs_database_size(pd->hs_db, &ctx->hs_db_size);
705  if (err != HS_SUCCESS) {
706  SCLogError(SC_ERR_FATAL, "failed to query database size");
707  SCMutexUnlock(&g_db_table_mutex);
708  goto error;
709  }
710 
711  mpm_ctx->memory_cnt++;
712  mpm_ctx->memory_size += ctx->hs_db_size;
713 
714  SCLogDebug("Built %" PRIu32 " patterns into a database of size %" PRIuMAX
715  " bytes", mpm_ctx->pattern_cnt, (uintmax_t)ctx->hs_db_size);
716 
717  /* Cache this database globally for later. */
718  pd->ref_cnt = 1;
719  int r = HashTableAdd(g_db_table, pd, 1);
720  SCMutexUnlock(&g_db_table_mutex);
721  if (r < 0)
722  goto error;
723 
724  SCHSFreeCompileData(cd);
725  return 0;
726 
727 error:
728  if (pd) {
729  PatternDatabaseFree(pd);
730  }
731  if (cd) {
732  SCHSFreeCompileData(cd);
733  }
734  return -1;
735 }
736 
737 /**
738  * \brief Init the mpm thread context.
739  *
740  * \param mpm_ctx Pointer to the mpm context.
741  * \param mpm_thread_ctx Pointer to the mpm thread context.
742  */
743 void SCHSInitThreadCtx(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx)
744 {
745  memset(mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
746 
747  SCHSThreadCtx *ctx = SCMalloc(sizeof(SCHSThreadCtx));
748  if (ctx == NULL) {
749  exit(EXIT_FAILURE);
750  }
751  mpm_thread_ctx->ctx = ctx;
752 
753  memset(ctx, 0, sizeof(SCHSThreadCtx));
754  mpm_thread_ctx->memory_cnt++;
755  mpm_thread_ctx->memory_size += sizeof(SCHSThreadCtx);
756 
757  ctx->scratch = NULL;
758  ctx->scratch_size = 0;
759 
760  SCMutexLock(&g_scratch_proto_mutex);
761 
762  if (g_scratch_proto == NULL) {
763  /* There is no scratch prototype: this means that we have not compiled
764  * any Hyperscan databases. */
765  SCMutexUnlock(&g_scratch_proto_mutex);
766  SCLogDebug("No scratch space prototype");
767  return;
768  }
769 
770  hs_error_t err = hs_clone_scratch(g_scratch_proto,
771  (hs_scratch_t **)&ctx->scratch);
772 
773  SCMutexUnlock(&g_scratch_proto_mutex);
774 
775  if (err != HS_SUCCESS) {
776  SCLogError(SC_ERR_FATAL, "Unable to clone scratch prototype");
777  exit(EXIT_FAILURE);
778  }
779 
780  err = hs_scratch_size(ctx->scratch, &ctx->scratch_size);
781  if (err != HS_SUCCESS) {
782  SCLogError(SC_ERR_FATAL, "Unable to query scratch size");
783  exit(EXIT_FAILURE);
784  }
785 
786  mpm_thread_ctx->memory_cnt++;
787  mpm_thread_ctx->memory_size += ctx->scratch_size;
788 }
789 
790 /**
791  * \brief Initialize the HS context.
792  *
793  * \param mpm_ctx Mpm context.
794  */
795 void SCHSInitCtx(MpmCtx *mpm_ctx)
796 {
797  if (mpm_ctx->ctx != NULL)
798  return;
799 
800  mpm_ctx->ctx = SCMalloc(sizeof(SCHSCtx));
801  if (mpm_ctx->ctx == NULL) {
802  exit(EXIT_FAILURE);
803  }
804  memset(mpm_ctx->ctx, 0, sizeof(SCHSCtx));
805 
806  mpm_ctx->memory_cnt++;
807  mpm_ctx->memory_size += sizeof(SCHSCtx);
808 
809  /* initialize the hash we use to speed up pattern insertions */
810  SCHSCtx *ctx = (SCHSCtx *)mpm_ctx->ctx;
811  ctx->init_hash = SCMalloc(sizeof(SCHSPattern *) * INIT_HASH_SIZE);
812  if (ctx->init_hash == NULL) {
813  exit(EXIT_FAILURE);
814  }
815  memset(ctx->init_hash, 0, sizeof(SCHSPattern *) * INIT_HASH_SIZE);
816 }
817 
818 /**
819  * \brief Destroy the mpm thread context.
820  *
821  * \param mpm_ctx Pointer to the mpm context.
822  * \param mpm_thread_ctx Pointer to the mpm thread context.
823  */
824 void SCHSDestroyThreadCtx(MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx)
825 {
826  SCHSPrintSearchStats(mpm_thread_ctx);
827 
828  if (mpm_thread_ctx->ctx != NULL) {
829  SCHSThreadCtx *thr_ctx = (SCHSThreadCtx *)mpm_thread_ctx->ctx;
830 
831  if (thr_ctx->scratch != NULL) {
832  hs_free_scratch(thr_ctx->scratch);
833  mpm_thread_ctx->memory_cnt--;
834  mpm_thread_ctx->memory_size -= thr_ctx->scratch_size;
835  }
836 
837  SCFree(mpm_thread_ctx->ctx);
838  mpm_thread_ctx->ctx = NULL;
839  mpm_thread_ctx->memory_cnt--;
840  mpm_thread_ctx->memory_size -= sizeof(SCHSThreadCtx);
841  }
842 }
843 
844 /**
845  * \brief Destroy the mpm context.
846  *
847  * \param mpm_ctx Pointer to the mpm context.
848  */
849 void SCHSDestroyCtx(MpmCtx *mpm_ctx)
850 {
851  SCHSCtx *ctx = (SCHSCtx *)mpm_ctx->ctx;
852  if (ctx == NULL)
853  return;
854 
855  if (ctx->init_hash != NULL) {
856  SCFree(ctx->init_hash);
857  ctx->init_hash = NULL;
858  mpm_ctx->memory_cnt--;
859  mpm_ctx->memory_size -= (INIT_HASH_SIZE * sizeof(SCHSPattern *));
860  }
861 
862  /* Decrement pattern database ref count, and delete it entirely if the
863  * count has dropped to zero. */
864  SCMutexLock(&g_db_table_mutex);
865  PatternDatabase *pd = ctx->pattern_db;
866  if (pd) {
867  BUG_ON(pd->ref_cnt == 0);
868  pd->ref_cnt--;
869  if (pd->ref_cnt == 0) {
870  HashTableRemove(g_db_table, pd, 1);
871  PatternDatabaseFree(pd);
872  }
873  }
874  SCMutexUnlock(&g_db_table_mutex);
875 
876  SCFree(mpm_ctx->ctx);
877  mpm_ctx->memory_cnt--;
878  mpm_ctx->memory_size -= sizeof(SCHSCtx);
879 }
880 
881 typedef struct SCHSCallbackCtx_ {
882  SCHSCtx *ctx;
883  void *pmq;
884  uint32_t match_count;
885 } SCHSCallbackCtx;
886 
887 /* Hyperscan MPM match event handler */
888 static int SCHSMatchEvent(unsigned int id, unsigned long long from,
889  unsigned long long to, unsigned int flags,
890  void *ctx)
891 {
892  SCHSCallbackCtx *cctx = ctx;
893  PrefilterRuleStore *pmq = cctx->pmq;
894  const PatternDatabase *pd = cctx->ctx->pattern_db;
895  const SCHSPattern *pat = pd->parray[id];
896 
897  SCLogDebug("Hyperscan Match %" PRIu32 ": id=%" PRIu32 " @ %" PRIuMAX
898  " (pat id=%" PRIu32 ")",
899  cctx->match_count, (uint32_t)id, (uintmax_t)to, pat->id);
900 
901  PrefilterAddSids(pmq, pat->sids, pat->sids_size);
902 
903  cctx->match_count++;
904  return 0;
905 }
906 
907 /**
908  * \brief The Hyperscan search function.
909  *
910  * \param mpm_ctx Pointer to the mpm context.
911  * \param mpm_thread_ctx Pointer to the mpm thread context.
912  * \param pmq Pointer to the Pattern Matcher Queue to hold
913  * search matches.
914  * \param buf Buffer to be searched.
915  * \param buflen Buffer length.
916  *
917  * \retval matches Match count.
918  */
919 uint32_t SCHSSearch(const MpmCtx *mpm_ctx, MpmThreadCtx *mpm_thread_ctx,
920  PrefilterRuleStore *pmq, const uint8_t *buf, const uint32_t buflen)
921 {
922  uint32_t ret = 0;
923  SCHSCtx *ctx = (SCHSCtx *)mpm_ctx->ctx;
924  SCHSThreadCtx *hs_thread_ctx = (SCHSThreadCtx *)(mpm_thread_ctx->ctx);
925  const PatternDatabase *pd = ctx->pattern_db;
926 
927  if (unlikely(buflen == 0)) {
928  return 0;
929  }
930 
931  SCHSCallbackCtx cctx = {.ctx = ctx, .pmq = pmq, .match_count = 0};
932 
933  /* scratch should have been cloned from g_scratch_proto at thread init. */
934  hs_scratch_t *scratch = hs_thread_ctx->scratch;
935  BUG_ON(pd->hs_db == NULL);
936  BUG_ON(scratch == NULL);
937 
938  hs_error_t err = hs_scan(pd->hs_db, (const char *)buf, buflen, 0, scratch,
939  SCHSMatchEvent, &cctx);
940  if (err != HS_SUCCESS) {
941  /* An error value (other than HS_SCAN_TERMINATED) from hs_scan()
942  * indicates that it was passed an invalid database or scratch region,
943  * which is not something we can recover from at scan time. */
944  SCLogError(SC_ERR_FATAL, "Hyperscan returned error %d", err);
945  exit(EXIT_FAILURE);
946  } else {
947  ret = cctx.match_count;
948  }
949 
950  return ret;
951 }
952 
953 /**
954  * \brief Add a case insensitive pattern. Although we have different calls for
955  * adding case sensitive and insensitive patterns, we make a single call
956  * for either case. No special treatment for either case.
957  *
958  * \param mpm_ctx Pointer to the mpm context.
959  * \param pat The pattern to add.
960  * \param patlen The pattern length.
961  * \param offset The pattern offset.
962  * \param depth The pattern depth.
963  * \param pid The pattern id.
964  * \param sid The pattern signature id.
965  * \param flags Flags associated with this pattern.
966  *
967  * \retval 0 On success.
968  * \retval -1 On failure.
969  */
970 int SCHSAddPatternCI(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
971  uint16_t offset, uint16_t depth, uint32_t pid,
972  SigIntId sid, uint8_t flags)
973 {
974  flags |= MPM_PATTERN_FLAG_NOCASE;
975  return SCHSAddPattern(mpm_ctx, pat, patlen, offset, depth, pid, sid, flags);
976 }
977 
978 /**
979  * \brief Add a case sensitive pattern. Although we have different calls for
980  * adding case sensitive and insensitive patterns, we make a single call
981  * for either case. No special treatment for either case.
982  *
983  * \param mpm_ctx Pointer to the mpm context.
984  * \param pat The pattern to add.
985  * \param patlen The pattern length.
986  * \param offset The pattern offset.
987  * \param depth The pattern depth.
988  * \param pid The pattern id.
989  * \param sid The pattern signature id.
990  * \param flags Flags associated with this pattern.
991  *
992  * \retval 0 On success.
993  * \retval -1 On failure.
994  */
995 int SCHSAddPatternCS(MpmCtx *mpm_ctx, uint8_t *pat, uint16_t patlen,
996  uint16_t offset, uint16_t depth, uint32_t pid,
997  SigIntId sid, uint8_t flags)
998 {
999  return SCHSAddPattern(mpm_ctx, pat, patlen, offset, depth, pid, sid, flags);
1000 }
1001 
1002 void SCHSPrintSearchStats(MpmThreadCtx *mpm_thread_ctx)
1003 {
1004  return;
1005 }
1006 
1007 void SCHSPrintInfo(MpmCtx *mpm_ctx)
1008 {
1009  SCHSCtx *ctx = (SCHSCtx *)mpm_ctx->ctx;
1010 
1011  printf("MPM HS Information:\n");
1012  printf("Memory allocs: %" PRIu32 "\n", mpm_ctx->memory_cnt);
1013  printf("Memory alloced: %" PRIu32 "\n", mpm_ctx->memory_size);
1014  printf(" Sizeof:\n");
1015  printf(" MpmCtx %" PRIuMAX "\n", (uintmax_t)sizeof(MpmCtx));
1016  printf(" SCHSCtx: %" PRIuMAX "\n", (uintmax_t)sizeof(SCHSCtx));
1017  printf(" SCHSPattern %" PRIuMAX "\n", (uintmax_t)sizeof(SCHSPattern));
1018  printf("Unique Patterns: %" PRIu32 "\n", mpm_ctx->pattern_cnt);
1019  printf("Smallest: %" PRIu32 "\n", mpm_ctx->minlen);
1020  printf("Largest: %" PRIu32 "\n", mpm_ctx->maxlen);
1021  printf("\n");
1022 
1023  if (ctx) {
1024  PatternDatabase *pd = ctx->pattern_db;
1025  char *db_info = NULL;
1026  if (hs_database_info(pd->hs_db, &db_info) == HS_SUCCESS) {
1027  printf("HS Database Info: %s\n", db_info);
1028  SCFree(db_info);
1029  }
1030  printf("HS Database Size: %" PRIuMAX " bytes\n",
1031  (uintmax_t)ctx->hs_db_size);
1032  }
1033 
1034  printf("\n");
1035 }
1036 
1037 /************************** Mpm Registration ***************************/
1038 
1039 /**
1040  * \brief Register the Hyperscan MPM.
1041  */
1042 void MpmHSRegister(void)
1043 {
1044  mpm_table[MPM_HS].name = "hs";
1045  mpm_table[MPM_HS].InitCtx = SCHSInitCtx;
1046  mpm_table[MPM_HS].InitThreadCtx = SCHSInitThreadCtx;
1047  mpm_table[MPM_HS].DestroyCtx = SCHSDestroyCtx;
1048  mpm_table[MPM_HS].DestroyThreadCtx = SCHSDestroyThreadCtx;
1049  mpm_table[MPM_HS].AddPattern = SCHSAddPatternCS;
1050  mpm_table[MPM_HS].AddPatternNocase = SCHSAddPatternCI;
1051  mpm_table[MPM_HS].Prepare = SCHSPreparePatterns;
1052  mpm_table[MPM_HS].Search = SCHSSearch;
1053  mpm_table[MPM_HS].PrintCtx = SCHSPrintInfo;
1054  mpm_table[MPM_HS].PrintThreadCtx = SCHSPrintSearchStats;
1055  mpm_table[MPM_HS].RegisterUnittests = SCHSRegisterTests;
1056 
1057  /* Set Hyperscan memory allocators */
1058  SCHSSetAllocators();
1059 }
1060 
1061 /**
1062  * \brief Clean up global memory used by all Hyperscan MPM instances.
1063  *
1064  * Currently, this is just the global scratch prototype.
1065  */
1066 void MpmHSGlobalCleanup(void)
1067 {
1068  SCMutexLock(&g_scratch_proto_mutex);
1069  if (g_scratch_proto) {
1070  SCLogPerf("Cleaning up Hyperscan global scratch");
1071  hs_free_scratch(g_scratch_proto);
1072  g_scratch_proto = NULL;
1073  }
1074  SCMutexUnlock(&g_scratch_proto_mutex);
1075 
1076  SCMutexLock(&g_db_table_mutex);
1077  if (g_db_table != NULL) {
1078  SCLogPerf("Clearing Hyperscan database cache");
1079  HashTableFree(g_db_table);
1080  g_db_table = NULL;
1081  }
1082  SCMutexUnlock(&g_db_table_mutex);
1083 }
1084 
1085 /*************************************Unittests********************************/
1086 
1087 #ifdef UNITTESTS
1088 
1089 static int SCHSTest01(void)
1090 {
1091  int result = 0;
1092  MpmCtx mpm_ctx;
1093  MpmThreadCtx mpm_thread_ctx;
1094  PrefilterRuleStore pmq;
1095 
1096  memset(&mpm_ctx, 0, sizeof(MpmCtx));
1097  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1098  MpmInitCtx(&mpm_ctx, MPM_HS);
1099 
1100  /* 1 match */
1101  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
1102  PmqSetup(&pmq);
1103 
1104  SCHSPreparePatterns(&mpm_ctx);
1105  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1106 
1107  const char *buf = "abcdefghjiklmnopqrstuvwxyz";
1108 
1109  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1110  strlen(buf));
1111 
1112  if (cnt == 1)
1113  result = 1;
1114  else
1115  printf("1 != %" PRIu32 " ", cnt);
1116 
1117  SCHSDestroyCtx(&mpm_ctx);
1118  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1119  PmqFree(&pmq);
1120  return result;
1121 }
1122 
1123 static int SCHSTest02(void)
1124 {
1125  int result = 0;
1126  MpmCtx mpm_ctx;
1127  MpmThreadCtx mpm_thread_ctx;
1128  PrefilterRuleStore pmq;
1129 
1130  memset(&mpm_ctx, 0, sizeof(MpmCtx));
1131  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1132  MpmInitCtx(&mpm_ctx, MPM_HS);
1133 
1134  /* 1 match */
1135  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abce", 4, 0, 0, 0, 0, 0);
1136  PmqSetup(&pmq);
1137 
1138  SCHSPreparePatterns(&mpm_ctx);
1139  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1140 
1141  const char *buf = "abcdefghjiklmnopqrstuvwxyz";
1142  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1143  strlen(buf));
1144 
1145  if (cnt == 0)
1146  result = 1;
1147  else
1148  printf("0 != %" PRIu32 " ", cnt);
1149 
1150  SCHSDestroyCtx(&mpm_ctx);
1151  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1152  PmqFree(&pmq);
1153  return result;
1154 }
1155 
1156 static int SCHSTest03(void)
1157 {
1158  int result = 0;
1159  MpmCtx mpm_ctx;
1160  MpmThreadCtx mpm_thread_ctx;
1161  PrefilterRuleStore pmq;
1162 
1163  memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
1164  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1165  MpmInitCtx(&mpm_ctx, MPM_HS);
1166 
1167  /* 1 match */
1168  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
1169  /* 1 match */
1170  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcde", 4, 0, 0, 1, 0, 0);
1171  /* 1 match */
1172  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"fghj", 4, 0, 0, 2, 0, 0);
1173  PmqSetup(&pmq);
1174 
1175  SCHSPreparePatterns(&mpm_ctx);
1176  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1177 
1178  const char *buf = "abcdefghjiklmnopqrstuvwxyz";
1179  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1180  strlen(buf));
1181 
1182  if (cnt == 3)
1183  result = 1;
1184  else
1185  printf("3 != %" PRIu32 " ", cnt);
1186 
1187  SCHSDestroyCtx(&mpm_ctx);
1188  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1189  PmqFree(&pmq);
1190  return result;
1191 }
1192 
1193 static int SCHSTest04(void)
1194 {
1195  int result = 0;
1196  MpmCtx mpm_ctx;
1197  MpmThreadCtx mpm_thread_ctx;
1198  PrefilterRuleStore pmq;
1199 
1200  memset(&mpm_ctx, 0, sizeof(MpmCtx));
1201  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1202  MpmInitCtx(&mpm_ctx, MPM_HS);
1203 
1204  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
1205  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"bcdegh", 6, 0, 0, 1, 0, 0);
1206  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"fghjxyz", 7, 0, 0, 2, 0, 0);
1207  PmqSetup(&pmq);
1208 
1209  SCHSPreparePatterns(&mpm_ctx);
1210  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1211 
1212  const char *buf = "abcdefghjiklmnopqrstuvwxyz";
1213  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1214  strlen(buf));
1215 
1216  if (cnt == 1)
1217  result = 1;
1218  else
1219  printf("1 != %" PRIu32 " ", cnt);
1220 
1221  SCHSDestroyCtx(&mpm_ctx);
1222  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1223  PmqFree(&pmq);
1224  return result;
1225 }
1226 
1227 static int SCHSTest05(void)
1228 {
1229  int result = 0;
1230  MpmCtx mpm_ctx;
1231  MpmThreadCtx mpm_thread_ctx;
1232  PrefilterRuleStore pmq;
1233 
1234  memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
1235  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1236  MpmInitCtx(&mpm_ctx, MPM_HS);
1237 
1238  MpmAddPatternCI(&mpm_ctx, (uint8_t *)"ABCD", 4, 0, 0, 0, 0, 0);
1239  MpmAddPatternCI(&mpm_ctx, (uint8_t *)"bCdEfG", 6, 0, 0, 1, 0, 0);
1240  MpmAddPatternCI(&mpm_ctx, (uint8_t *)"fghJikl", 7, 0, 0, 2, 0, 0);
1241  PmqSetup(&pmq);
1242 
1243  SCHSPreparePatterns(&mpm_ctx);
1244  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1245 
1246  const char *buf = "abcdefghjiklmnopqrstuvwxyz";
1247  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1248  strlen(buf));
1249 
1250  if (cnt == 3)
1251  result = 1;
1252  else
1253  printf("3 != %" PRIu32 " ", cnt);
1254 
1255  SCHSDestroyCtx(&mpm_ctx);
1256  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1257  PmqFree(&pmq);
1258  return result;
1259 }
1260 
1261 static int SCHSTest06(void)
1262 {
1263  int result = 0;
1264  MpmCtx mpm_ctx;
1265  MpmThreadCtx mpm_thread_ctx;
1266  PrefilterRuleStore pmq;
1267 
1268  memset(&mpm_ctx, 0, sizeof(MpmCtx));
1269  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1270  MpmInitCtx(&mpm_ctx, MPM_HS);
1271 
1272  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
1273  PmqSetup(&pmq);
1274 
1275  SCHSPreparePatterns(&mpm_ctx);
1276  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1277 
1278  const char *buf = "abcd";
1279  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1280  strlen(buf));
1281 
1282  if (cnt == 1)
1283  result = 1;
1284  else
1285  printf("1 != %" PRIu32 " ", cnt);
1286 
1287  SCHSDestroyCtx(&mpm_ctx);
1288  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1289  PmqFree(&pmq);
1290  return result;
1291 }
1292 
1293 static int SCHSTest07(void)
1294 {
1295  int result = 0;
1296  MpmCtx mpm_ctx;
1297  MpmThreadCtx mpm_thread_ctx;
1298  PrefilterRuleStore pmq;
1299 
1300  memset(&mpm_ctx, 0, sizeof(MpmCtx));
1301  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1302  MpmInitCtx(&mpm_ctx, MPM_HS);
1303 
1304  /* should match 30 times */
1305  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"A", 1, 0, 0, 0, 0, 0);
1306  /* should match 29 times */
1307  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 1, 0, 0);
1308  /* should match 28 times */
1309  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAA", 3, 0, 0, 2, 0, 0);
1310  /* 26 */
1311  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAA", 5, 0, 0, 3, 0, 0);
1312  /* 21 */
1313  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAA", 10, 0, 0, 4, 0, 0);
1314  /* 1 */
1315  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 30,
1316  0, 0, 5, 0, 0);
1317  PmqSetup(&pmq);
1318 
1319  SCHSPreparePatterns(&mpm_ctx);
1320  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1321 
1322  const char *buf = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
1323  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1324  strlen(buf));
1325 
1326  if (cnt == 6)
1327  result = 1;
1328  else
1329  printf("6 != %" PRIu32 " ", cnt);
1330 
1331  SCHSDestroyCtx(&mpm_ctx);
1332  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1333  PmqFree(&pmq);
1334  return result;
1335 }
1336 
1337 static int SCHSTest08(void)
1338 {
1339  int result = 0;
1340  MpmCtx mpm_ctx;
1341  MpmThreadCtx mpm_thread_ctx;
1342  PrefilterRuleStore pmq;
1343 
1344  memset(&mpm_ctx, 0, sizeof(MpmCtx));
1345  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1346  MpmInitCtx(&mpm_ctx, MPM_HS);
1347 
1348  /* 1 match */
1349  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
1350  PmqSetup(&pmq);
1351 
1352  SCHSPreparePatterns(&mpm_ctx);
1353  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1354 
1355  uint32_t cnt =
1356  SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)"a", 1);
1357 
1358  if (cnt == 0)
1359  result = 1;
1360  else
1361  printf("0 != %" PRIu32 " ", cnt);
1362 
1363  SCHSDestroyCtx(&mpm_ctx);
1364  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1365  PmqFree(&pmq);
1366  return result;
1367 }
1368 
1369 static int SCHSTest09(void)
1370 {
1371  int result = 0;
1372  MpmCtx mpm_ctx;
1373  MpmThreadCtx mpm_thread_ctx;
1374  PrefilterRuleStore pmq;
1375 
1376  memset(&mpm_ctx, 0, sizeof(MpmCtx));
1377  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1378  MpmInitCtx(&mpm_ctx, MPM_HS);
1379 
1380  /* 1 match */
1381  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"ab", 2, 0, 0, 0, 0, 0);
1382  PmqSetup(&pmq);
1383 
1384  SCHSPreparePatterns(&mpm_ctx);
1385  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1386 
1387  uint32_t cnt =
1388  SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)"ab", 2);
1389 
1390  if (cnt == 1)
1391  result = 1;
1392  else
1393  printf("1 != %" PRIu32 " ", cnt);
1394 
1395  SCHSDestroyCtx(&mpm_ctx);
1396  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1397  PmqFree(&pmq);
1398  return result;
1399 }
1400 
1401 static int SCHSTest10(void)
1402 {
1403  int result = 0;
1404  MpmCtx mpm_ctx;
1405  MpmThreadCtx mpm_thread_ctx;
1406  PrefilterRuleStore pmq;
1407 
1408  memset(&mpm_ctx, 0, sizeof(MpmCtx));
1409  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1410  MpmInitCtx(&mpm_ctx, MPM_HS);
1411 
1412  /* 1 match */
1413  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcdefgh", 8, 0, 0, 0, 0, 0);
1414  PmqSetup(&pmq);
1415 
1416  SCHSPreparePatterns(&mpm_ctx);
1417  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1418 
1419  const char *buf = "01234567890123456789012345678901234567890123456789"
1420  "01234567890123456789012345678901234567890123456789"
1421  "abcdefgh"
1422  "01234567890123456789012345678901234567890123456789"
1423  "01234567890123456789012345678901234567890123456789";
1424  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1425  strlen(buf));
1426 
1427  if (cnt == 1)
1428  result = 1;
1429  else
1430  printf("1 != %" PRIu32 " ", cnt);
1431 
1432  SCHSDestroyCtx(&mpm_ctx);
1433  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1434  PmqFree(&pmq);
1435  return result;
1436 }
1437 
1438 static int SCHSTest11(void)
1439 {
1440  int result = 0;
1441  MpmCtx mpm_ctx;
1442  MpmThreadCtx mpm_thread_ctx;
1443  PrefilterRuleStore pmq;
1444 
1445  memset(&mpm_ctx, 0, sizeof(MpmCtx));
1446  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1447  MpmInitCtx(&mpm_ctx, MPM_HS);
1448 
1449  if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"he", 2, 0, 0, 1, 0, 0) == -1)
1450  goto end;
1451  if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"she", 3, 0, 0, 2, 0, 0) == -1)
1452  goto end;
1453  if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"his", 3, 0, 0, 3, 0, 0) == -1)
1454  goto end;
1455  if (MpmAddPatternCS(&mpm_ctx, (uint8_t *)"hers", 4, 0, 0, 4, 0, 0) == -1)
1456  goto end;
1457  PmqSetup(&pmq);
1458 
1459  if (SCHSPreparePatterns(&mpm_ctx) == -1)
1460  goto end;
1461 
1462  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1463 
1464  result = 1;
1465 
1466  const char *buf = "he";
1467  result &= (SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1468  strlen(buf)) == 1);
1469  buf = "she";
1470  result &= (SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1471  strlen(buf)) == 2);
1472  buf = "his";
1473  result &= (SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1474  strlen(buf)) == 1);
1475  buf = "hers";
1476  result &= (SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1477  strlen(buf)) == 2);
1478 
1479 end:
1480  SCHSDestroyCtx(&mpm_ctx);
1481  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1482  PmqFree(&pmq);
1483  return result;
1484 }
1485 
1486 static int SCHSTest12(void)
1487 {
1488  int result = 0;
1489  MpmCtx mpm_ctx;
1490  MpmThreadCtx mpm_thread_ctx;
1491  PrefilterRuleStore pmq;
1492 
1493  memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
1494  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1495  MpmInitCtx(&mpm_ctx, MPM_HS);
1496 
1497  /* 1 match */
1498  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"wxyz", 4, 0, 0, 0, 0, 0);
1499  /* 1 match */
1500  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"vwxyz", 5, 0, 0, 1, 0, 0);
1501  PmqSetup(&pmq);
1502 
1503  SCHSPreparePatterns(&mpm_ctx);
1504  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1505 
1506  const char *buf = "abcdefghijklmnopqrstuvwxyz";
1507  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1508  strlen(buf));
1509 
1510  if (cnt == 2)
1511  result = 1;
1512  else
1513  printf("2 != %" PRIu32 " ", cnt);
1514 
1515  SCHSDestroyCtx(&mpm_ctx);
1516  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1517  PmqFree(&pmq);
1518  return result;
1519 }
1520 
1521 static int SCHSTest13(void)
1522 {
1523  int result = 0;
1524  MpmCtx mpm_ctx;
1525  MpmThreadCtx mpm_thread_ctx;
1526  PrefilterRuleStore pmq;
1527 
1528  memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
1529  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1530  MpmInitCtx(&mpm_ctx, MPM_HS);
1531 
1532  /* 1 match */
1533  const char *pat = "abcdefghijklmnopqrstuvwxyzABCD";
1534  MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
1535  PmqSetup(&pmq);
1536 
1537  SCHSPreparePatterns(&mpm_ctx);
1538  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1539 
1540  const char *buf = "abcdefghijklmnopqrstuvwxyzABCD";
1541  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1542  strlen(buf));
1543 
1544  if (cnt == 1)
1545  result = 1;
1546  else
1547  printf("1 != %" PRIu32 " ", cnt);
1548 
1549  SCHSDestroyCtx(&mpm_ctx);
1550  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1551  PmqFree(&pmq);
1552  return result;
1553 }
1554 
1555 static int SCHSTest14(void)
1556 {
1557  int result = 0;
1558  MpmCtx mpm_ctx;
1559  MpmThreadCtx mpm_thread_ctx;
1560  PrefilterRuleStore pmq;
1561 
1562  memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
1563  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1564  MpmInitCtx(&mpm_ctx, MPM_HS);
1565 
1566  /* 1 match */
1567  const char *pat = "abcdefghijklmnopqrstuvwxyzABCDE";
1568  MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
1569  PmqSetup(&pmq);
1570 
1571  SCHSPreparePatterns(&mpm_ctx);
1572  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1573 
1574  const char *buf = "abcdefghijklmnopqrstuvwxyzABCDE";
1575  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1576  strlen(buf));
1577 
1578  if (cnt == 1)
1579  result = 1;
1580  else
1581  printf("1 != %" PRIu32 " ", cnt);
1582 
1583  SCHSDestroyCtx(&mpm_ctx);
1584  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1585  PmqFree(&pmq);
1586  return result;
1587 }
1588 
1589 static int SCHSTest15(void)
1590 {
1591  int result = 0;
1592  MpmCtx mpm_ctx;
1593  MpmThreadCtx mpm_thread_ctx;
1594  PrefilterRuleStore pmq;
1595 
1596  memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
1597  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1598  MpmInitCtx(&mpm_ctx, MPM_HS);
1599 
1600  /* 1 match */
1601  const char *pat = "abcdefghijklmnopqrstuvwxyzABCDEF";
1602  MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
1603  PmqSetup(&pmq);
1604 
1605  SCHSPreparePatterns(&mpm_ctx);
1606  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1607 
1608  const char *buf = "abcdefghijklmnopqrstuvwxyzABCDEF";
1609  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1610  strlen(buf));
1611 
1612  if (cnt == 1)
1613  result = 1;
1614  else
1615  printf("1 != %" PRIu32 " ", cnt);
1616 
1617  SCHSDestroyCtx(&mpm_ctx);
1618  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1619  PmqFree(&pmq);
1620  return result;
1621 }
1622 
1623 static int SCHSTest16(void)
1624 {
1625  int result = 0;
1626  MpmCtx mpm_ctx;
1627  MpmThreadCtx mpm_thread_ctx;
1628  PrefilterRuleStore pmq;
1629 
1630  memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
1631  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1632  MpmInitCtx(&mpm_ctx, MPM_HS);
1633 
1634  /* 1 match */
1635  const char *pat = "abcdefghijklmnopqrstuvwxyzABC";
1636  MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
1637  PmqSetup(&pmq);
1638 
1639  SCHSPreparePatterns(&mpm_ctx);
1640  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1641 
1642  const char *buf = "abcdefghijklmnopqrstuvwxyzABC";
1643  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1644  strlen(buf));
1645 
1646  if (cnt == 1)
1647  result = 1;
1648  else
1649  printf("1 != %" PRIu32 " ", cnt);
1650 
1651  SCHSDestroyCtx(&mpm_ctx);
1652  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1653  PmqFree(&pmq);
1654  return result;
1655 }
1656 
1657 static int SCHSTest17(void)
1658 {
1659  int result = 0;
1660  MpmCtx mpm_ctx;
1661  MpmThreadCtx mpm_thread_ctx;
1662  PrefilterRuleStore pmq;
1663 
1664  memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
1665  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1666  MpmInitCtx(&mpm_ctx, MPM_HS);
1667 
1668  /* 1 match */
1669  const char *pat = "abcdefghijklmnopqrstuvwxyzAB";
1670  MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
1671  PmqSetup(&pmq);
1672 
1673  SCHSPreparePatterns(&mpm_ctx);
1674  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1675 
1676  const char *buf = "abcdefghijklmnopqrstuvwxyzAB";
1677  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1678  strlen(buf));
1679 
1680  if (cnt == 1)
1681  result = 1;
1682  else
1683  printf("1 != %" PRIu32 " ", cnt);
1684 
1685  SCHSDestroyCtx(&mpm_ctx);
1686  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1687  PmqFree(&pmq);
1688  return result;
1689 }
1690 
1691 static int SCHSTest18(void)
1692 {
1693  int result = 0;
1694  MpmCtx mpm_ctx;
1695  MpmThreadCtx mpm_thread_ctx;
1696  PrefilterRuleStore pmq;
1697 
1698  memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
1699  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1700  MpmInitCtx(&mpm_ctx, MPM_HS);
1701 
1702  /* 1 match */
1703  const char *pat = "abcde"
1704  "fghij"
1705  "klmno"
1706  "pqrst"
1707  "uvwxy"
1708  "z";
1709  MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
1710  PmqSetup(&pmq);
1711 
1712  SCHSPreparePatterns(&mpm_ctx);
1713  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1714 
1715  const char *buf = "abcde"
1716  "fghij"
1717  "klmno"
1718  "pqrst"
1719  "uvwxy"
1720  "z";
1721  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1722  strlen(buf));
1723 
1724  if (cnt == 1)
1725  result = 1;
1726  else
1727  printf("1 != %" PRIu32 " ", cnt);
1728 
1729  SCHSDestroyCtx(&mpm_ctx);
1730  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1731  PmqFree(&pmq);
1732  return result;
1733 }
1734 
1735 static int SCHSTest19(void)
1736 {
1737  int result = 0;
1738  MpmCtx mpm_ctx;
1739  MpmThreadCtx mpm_thread_ctx;
1740  PrefilterRuleStore pmq;
1741 
1742  memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
1743  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1744  MpmInitCtx(&mpm_ctx, MPM_HS);
1745 
1746  /* 1 */
1747  const char *pat = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
1748  MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
1749  PmqSetup(&pmq);
1750 
1751  SCHSPreparePatterns(&mpm_ctx);
1752  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1753 
1754  const char *buf = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
1755  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1756  strlen(buf));
1757 
1758  if (cnt == 1)
1759  result = 1;
1760  else
1761  printf("1 != %" PRIu32 " ", cnt);
1762 
1763  SCHSDestroyCtx(&mpm_ctx);
1764  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1765  PmqFree(&pmq);
1766  return result;
1767 }
1768 
1769 static int SCHSTest20(void)
1770 {
1771  int result = 0;
1772  MpmCtx mpm_ctx;
1773  MpmThreadCtx mpm_thread_ctx;
1774  PrefilterRuleStore pmq;
1775 
1776  memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
1777  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1778  MpmInitCtx(&mpm_ctx, MPM_HS);
1779 
1780  /* 1 */
1781  const char *pat = "AAAAA"
1782  "AAAAA"
1783  "AAAAA"
1784  "AAAAA"
1785  "AAAAA"
1786  "AAAAA"
1787  "AA";
1788  MpmAddPatternCS(&mpm_ctx, (uint8_t *)pat, strlen(pat), 0, 0, 0, 0, 0);
1789  PmqSetup(&pmq);
1790 
1791  SCHSPreparePatterns(&mpm_ctx);
1792  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1793 
1794  const char *buf = "AAAAA"
1795  "AAAAA"
1796  "AAAAA"
1797  "AAAAA"
1798  "AAAAA"
1799  "AAAAA"
1800  "AA";
1801  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1802  strlen(buf));
1803 
1804  if (cnt == 1)
1805  result = 1;
1806  else
1807  printf("1 != %" PRIu32 " ", cnt);
1808 
1809  SCHSDestroyCtx(&mpm_ctx);
1810  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1811  PmqFree(&pmq);
1812  return result;
1813 }
1814 
1815 static int SCHSTest21(void)
1816 {
1817  int result = 0;
1818  MpmCtx mpm_ctx;
1819  MpmThreadCtx mpm_thread_ctx;
1820  PrefilterRuleStore pmq;
1821 
1822  memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
1823  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1824  MpmInitCtx(&mpm_ctx, MPM_HS);
1825 
1826  /* 1 */
1827  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 0, 0, 0);
1828  PmqSetup(&pmq);
1829 
1830  SCHSPreparePatterns(&mpm_ctx);
1831  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1832 
1833  uint32_t cnt =
1834  SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)"AA", 2);
1835 
1836  if (cnt == 1)
1837  result = 1;
1838  else
1839  printf("1 != %" PRIu32 " ", cnt);
1840 
1841  SCHSDestroyCtx(&mpm_ctx);
1842  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1843  PmqFree(&pmq);
1844  return result;
1845 }
1846 
1847 static int SCHSTest22(void)
1848 {
1849  int result = 0;
1850  MpmCtx mpm_ctx;
1851  MpmThreadCtx mpm_thread_ctx;
1852  PrefilterRuleStore pmq;
1853 
1854  memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
1855  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1856  MpmInitCtx(&mpm_ctx, MPM_HS);
1857 
1858  /* 1 match */
1859  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcd", 4, 0, 0, 0, 0, 0);
1860  /* 1 match */
1861  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"abcde", 5, 0, 0, 1, 0, 0);
1862  PmqSetup(&pmq);
1863 
1864  SCHSPreparePatterns(&mpm_ctx);
1865  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1866 
1867  const char *buf = "abcdefghijklmnopqrstuvwxyz";
1868  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1869  strlen(buf));
1870 
1871  if (cnt == 2)
1872  result = 1;
1873  else
1874  printf("2 != %" PRIu32 " ", cnt);
1875 
1876  SCHSDestroyCtx(&mpm_ctx);
1877  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1878  PmqFree(&pmq);
1879  return result;
1880 }
1881 
1882 static int SCHSTest23(void)
1883 {
1884  int result = 0;
1885  MpmCtx mpm_ctx;
1886  MpmThreadCtx mpm_thread_ctx;
1887  PrefilterRuleStore pmq;
1888 
1889  memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
1890  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1891  MpmInitCtx(&mpm_ctx, MPM_HS);
1892 
1893  /* 1 */
1894  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 0, 0, 0);
1895  PmqSetup(&pmq);
1896 
1897  SCHSPreparePatterns(&mpm_ctx);
1898  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1899 
1900  uint32_t cnt =
1901  SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)"aa", 2);
1902 
1903  if (cnt == 0)
1904  result = 1;
1905  else
1906  printf("1 != %" PRIu32 " ", cnt);
1907 
1908  SCHSDestroyCtx(&mpm_ctx);
1909  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1910  PmqFree(&pmq);
1911  return result;
1912 }
1913 
1914 static int SCHSTest24(void)
1915 {
1916  int result = 0;
1917  MpmCtx mpm_ctx;
1918  MpmThreadCtx mpm_thread_ctx;
1919  PrefilterRuleStore pmq;
1920 
1921  memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
1922  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1923  MpmInitCtx(&mpm_ctx, MPM_HS);
1924 
1925  /* 1 */
1926  MpmAddPatternCI(&mpm_ctx, (uint8_t *)"AA", 2, 0, 0, 0, 0, 0);
1927  PmqSetup(&pmq);
1928 
1929  SCHSPreparePatterns(&mpm_ctx);
1930  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1931 
1932  uint32_t cnt =
1933  SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)"aa", 2);
1934 
1935  if (cnt == 1)
1936  result = 1;
1937  else
1938  printf("1 != %" PRIu32 " ", cnt);
1939 
1940  SCHSDestroyCtx(&mpm_ctx);
1941  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1942  PmqFree(&pmq);
1943  return result;
1944 }
1945 
1946 static int SCHSTest25(void)
1947 {
1948  int result = 0;
1949  MpmCtx mpm_ctx;
1950  MpmThreadCtx mpm_thread_ctx;
1951  PrefilterRuleStore pmq;
1952 
1953  memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
1954  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1955  MpmInitCtx(&mpm_ctx, MPM_HS);
1956 
1957  MpmAddPatternCI(&mpm_ctx, (uint8_t *)"ABCD", 4, 0, 0, 0, 0, 0);
1958  MpmAddPatternCI(&mpm_ctx, (uint8_t *)"bCdEfG", 6, 0, 0, 1, 0, 0);
1959  MpmAddPatternCI(&mpm_ctx, (uint8_t *)"fghiJkl", 7, 0, 0, 2, 0, 0);
1960  PmqSetup(&pmq);
1961 
1962  SCHSPreparePatterns(&mpm_ctx);
1963  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1964 
1965  const char *buf = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1966  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
1967  strlen(buf));
1968 
1969  if (cnt == 3)
1970  result = 1;
1971  else
1972  printf("3 != %" PRIu32 " ", cnt);
1973 
1974  SCHSDestroyCtx(&mpm_ctx);
1975  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1976  PmqFree(&pmq);
1977  return result;
1978 }
1979 
1980 static int SCHSTest26(void)
1981 {
1982  int result = 0;
1983  MpmCtx mpm_ctx;
1984  MpmThreadCtx mpm_thread_ctx;
1985  PrefilterRuleStore pmq;
1986 
1987  memset(&mpm_ctx, 0x00, sizeof(MpmCtx));
1988  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
1989  MpmInitCtx(&mpm_ctx, MPM_HS);
1990 
1991  MpmAddPatternCI(&mpm_ctx, (uint8_t *)"Works", 5, 0, 0, 0, 0, 0);
1992  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"Works", 5, 0, 0, 1, 0, 0);
1993  PmqSetup(&pmq);
1994 
1995  SCHSPreparePatterns(&mpm_ctx);
1996  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
1997 
1998  const char *buf = "works";
1999  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
2000  strlen(buf));
2001 
2002  if (cnt == 1)
2003  result = 1;
2004  else
2005  printf("3 != %" PRIu32 " ", cnt);
2006 
2007  SCHSDestroyCtx(&mpm_ctx);
2008  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
2009  PmqFree(&pmq);
2010  return result;
2011 }
2012 
2013 static int SCHSTest27(void)
2014 {
2015  int result = 0;
2016  MpmCtx mpm_ctx;
2017  MpmThreadCtx mpm_thread_ctx;
2018  PrefilterRuleStore pmq;
2019 
2020  memset(&mpm_ctx, 0, sizeof(MpmCtx));
2021  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
2022  MpmInitCtx(&mpm_ctx, MPM_HS);
2023 
2024  /* 0 match */
2025  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"ONE", 3, 0, 0, 0, 0, 0);
2026  PmqSetup(&pmq);
2027 
2028  SCHSPreparePatterns(&mpm_ctx);
2029  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
2030 
2031  const char *buf = "tone";
2032  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
2033  strlen(buf));
2034 
2035  if (cnt == 0)
2036  result = 1;
2037  else
2038  printf("0 != %" PRIu32 " ", cnt);
2039 
2040  SCHSDestroyCtx(&mpm_ctx);
2041  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
2042  PmqFree(&pmq);
2043  return result;
2044 }
2045 
2046 static int SCHSTest28(void)
2047 {
2048  int result = 0;
2049  MpmCtx mpm_ctx;
2050  MpmThreadCtx mpm_thread_ctx;
2051  PrefilterRuleStore pmq;
2052 
2053  memset(&mpm_ctx, 0, sizeof(MpmCtx));
2054  memset(&mpm_thread_ctx, 0, sizeof(MpmThreadCtx));
2055  MpmInitCtx(&mpm_ctx, MPM_HS);
2056 
2057  /* 0 match */
2058  MpmAddPatternCS(&mpm_ctx, (uint8_t *)"one", 3, 0, 0, 0, 0, 0);
2059  PmqSetup(&pmq);
2060 
2061  SCHSPreparePatterns(&mpm_ctx);
2062  SCHSInitThreadCtx(&mpm_ctx, &mpm_thread_ctx);
2063 
2064  const char *buf = "tONE";
2065  uint32_t cnt = SCHSSearch(&mpm_ctx, &mpm_thread_ctx, &pmq, (uint8_t *)buf,
2066  strlen(buf));
2067 
2068  if (cnt == 0)
2069  result = 1;
2070  else
2071  printf("0 != %" PRIu32 " ", cnt);
2072 
2073  SCHSDestroyCtx(&mpm_ctx);
2074  SCHSDestroyThreadCtx(&mpm_ctx, &mpm_thread_ctx);
2075  PmqFree(&pmq);
2076  return result;
2077 }
2078 
2079 static int SCHSTest29(void)
2080 {
2081  uint8_t *buf = (uint8_t *)"onetwothreefourfivesixseveneightnine";
2082  uint16_t buflen = strlen((char *)buf);
2083  Packet *p = NULL;
2084  ThreadVars th_v;
2085  DetectEngineThreadCtx *det_ctx = NULL;
2086  int result = 0;
2087 
2088  memset(&th_v, 0, sizeof(th_v));
2089  p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
2090 
2092  if (de_ctx == NULL)
2093  goto end;
2094  de_ctx->mpm_matcher = MPM_HS;
2095 
2096  de_ctx->flags |= DE_QUIET;
2097 
2098  de_ctx->sig_list = SigInit(
2099  de_ctx, "alert tcp any any -> any any "
2100  "(content:\"onetwothreefourfivesixseveneightnine\"; sid:1;)");
2101  if (de_ctx->sig_list == NULL)
2102  goto end;
2103  de_ctx->sig_list->next =
2104  SigInit(de_ctx, "alert tcp any any -> any any "
2105  "(content:\"onetwothreefourfivesixseveneightnine\"; "
2106  "fast_pattern:3,3; sid:2;)");
2107  if (de_ctx->sig_list->next == NULL)
2108  goto end;
2109 
2110  SigGroupBuild(de_ctx);
2111  DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
2112 
2113  SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
2114  if (PacketAlertCheck(p, 1) != 1) {
2115  printf("if (PacketAlertCheck(p, 1) != 1) failure\n");
2116  goto end;
2117  }
2118  if (PacketAlertCheck(p, 2) != 1) {
2119  printf("if (PacketAlertCheck(p, 1) != 2) failure\n");
2120  goto end;
2121  }
2122 
2123  result = 1;
2124 end:
2125  if (de_ctx != NULL) {
2126  SigGroupCleanup(de_ctx);
2127  SigCleanSignatures(de_ctx);
2128 
2129  DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
2130  DetectEngineCtxFree(de_ctx);
2131  }
2132 
2133  UTHFreePackets(&p, 1);
2134  return result;
2135 }
2136 
2137 #endif /* UNITTESTS */
2138 
2139 void SCHSRegisterTests(void)
2140 {
2141 #ifdef UNITTESTS
2142  UtRegisterTest("SCHSTest01", SCHSTest01);
2143  UtRegisterTest("SCHSTest02", SCHSTest02);
2144  UtRegisterTest("SCHSTest03", SCHSTest03);
2145  UtRegisterTest("SCHSTest04", SCHSTest04);
2146  UtRegisterTest("SCHSTest05", SCHSTest05);
2147  UtRegisterTest("SCHSTest06", SCHSTest06);
2148  UtRegisterTest("SCHSTest07", SCHSTest07);
2149  UtRegisterTest("SCHSTest08", SCHSTest08);
2150  UtRegisterTest("SCHSTest09", SCHSTest09);
2151  UtRegisterTest("SCHSTest10", SCHSTest10);
2152  UtRegisterTest("SCHSTest11", SCHSTest11);
2153  UtRegisterTest("SCHSTest12", SCHSTest12);
2154  UtRegisterTest("SCHSTest13", SCHSTest13);
2155  UtRegisterTest("SCHSTest14", SCHSTest14);
2156  UtRegisterTest("SCHSTest15", SCHSTest15);
2157  UtRegisterTest("SCHSTest16", SCHSTest16);
2158  UtRegisterTest("SCHSTest17", SCHSTest17);
2159  UtRegisterTest("SCHSTest18", SCHSTest18);
2160  UtRegisterTest("SCHSTest19", SCHSTest19);
2161  UtRegisterTest("SCHSTest20", SCHSTest20);
2162  UtRegisterTest("SCHSTest21", SCHSTest21);
2163  UtRegisterTest("SCHSTest22", SCHSTest22);
2164  UtRegisterTest("SCHSTest23", SCHSTest23);
2165  UtRegisterTest("SCHSTest24", SCHSTest24);
2166  UtRegisterTest("SCHSTest25", SCHSTest25);
2167  UtRegisterTest("SCHSTest26", SCHSTest26);
2168  UtRegisterTest("SCHSTest27", SCHSTest27);
2169  UtRegisterTest("SCHSTest28", SCHSTest28);
2170  UtRegisterTest("SCHSTest29", SCHSTest29);
2171 #endif
2172 
2173  return;
2174 }
2175 
2176 #endif /* BUILD_HYPERSCAN */
#define MPM_PATTERN_FLAG_DEPTH
Definition: util-mpm.h:128
uint32_t hashword(const uint32_t *k, size_t length, uint32_t initval)
uint16_t flags
#define SCMemcmp(a, b, c)
Definition: util-memcmp.h:490
size_t scratch_size
Definition: util-mpm-hs.h:69
#define SCLogDebug(...)
Definition: util-debug.h:335
int MpmAddPatternCS(struct MpmCtx_ *mpm_ctx, uint8_t *pat, uint16_t patlen, uint16_t offset, uint16_t depth, uint32_t pid, SigIntId sid, uint8_t flags)
Definition: util-mpm.c:310
uint16_t minlen
Definition: util-mpm.h:95
int HashTableAdd(HashTable *ht, void *data, uint16_t datalen)
Definition: util-hash.c:113
void(* PrintCtx)(struct MpmCtx_ *)
Definition: util-mpm.h:159
size_t hs_db_size
Definition: util-mpm-hs.h:60
int PacketAlertCheck(Packet *p, uint32_t sid)
Check if a certain sid alerted, this is used in the test functions.
uint16_t len
Definition: util-mpm-hs.h:32
#define BUG_ON(x)
uint32_t pattern_cnt
Definition: util-mpm.h:93
int PmqSetup(PrefilterRuleStore *)
Setup a pmq.
uint16_t offset
Definition: util-mpm-hs.h:40
#define unlikely(expr)
Definition: util-optimize.h:35
Signature * SigInit(DetectEngineCtx *, const char *)
Parses a signature and adds it to the Detection Engine Context.
#define MPM_PATTERN_FLAG_NOCASE
Definition: util-mpm.h:124
void(* InitCtx)(struct MpmCtx_ *)
Definition: util-mpm.h:139
void(* RegisterUnittests)(void)
Definition: util-mpm.h:161
Signature * sig_list
Definition: detect.h:726
uint64_t offset
void SigCleanSignatures(DetectEngineCtx *de_ctx)
HashTable * HashTableInit(uint32_t size, uint32_t(*Hash)(struct HashTable_ *, void *, uint16_t), char(*Compare)(void *, uint16_t, void *, uint16_t), void(*Free)(void *))
Definition: util-hash.c:34
void(* DestroyThreadCtx)(struct MpmCtx_ *, struct MpmThreadCtx_ *)
Definition: util-mpm.h:142
uint32_t memory_cnt
Definition: util-mpm.h:98
void * scratch
Definition: util-mpm-hs.h:66
char * HSRenderPattern(const uint8_t *pat, uint16_t pat_len)
TmEcode DetectEngineThreadCtxInit(ThreadVars *, void *, void **)
initialize thread specific detection engine context
uint8_t * original_pat
Definition: util-mpm-hs.h:36
uint32_t hashlittle_safe(const void *key, size_t length, uint32_t initval)
uint32_t array_size
Definition: util-hash.h:37
void MpmHSGlobalCleanup(void)
uint32_t memory_size
Definition: util-mpm.h:50
main detection engine ctx
Definition: detect.h:720
TmEcode DetectEngineThreadCtxDeinit(ThreadVars *, void *)
void PmqFree(PrefilterRuleStore *)
Cleanup and free a Pmq.
#define DE_QUIET
Definition: detect.h:298
uint16_t depth
Definition: util-mpm-hs.h:41
int(* Prepare)(struct MpmCtx_ *)
Definition: util-mpm.h:157
void(* PrintThreadCtx)(struct MpmThreadCtx_ *)
Definition: util-mpm.h:160
#define SCMutexUnlock(mut)
int HashTableRemove(HashTable *ht, void *data, uint16_t datalen)
Definition: util-hash.c:152
uint8_t flags
Definition: detect.h:721
struct SCHSThreadCtx_ SCHSThreadCtx
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:294
int SigGroupBuild(DetectEngineCtx *de_ctx)
Convert the signature list into the runtime match structure.
uint16_t mpm_matcher
Definition: detect.h:769
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
void SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
wrapper for old tests
Definition: detect.c:1752
Packet * UTHBuildPacket(uint8_t *payload, uint16_t payload_len, uint8_t ipproto)
UTHBuildPacket is a wrapper that build packets with default ip and port fields.
void(* DestroyCtx)(struct MpmCtx_ *)
Definition: util-mpm.h:141
int SigGroupCleanup(DetectEngineCtx *de_ctx)
struct Signature_ * next
Definition: detect.h:563
#define SCLogWarning(err_code,...)
Macro used to log WARNING messages.
Definition: util-debug.h:281
#define SCRealloc(x, a)
Definition: util-mem.h:190
uint32_t memory_size
Definition: util-mpm.h:99
uint32_t memory_cnt
Definition: util-mpm.h:49
void HashTableFree(HashTable *ht)
Definition: util-hash.c:79
#define SCMalloc(a)
Definition: util-mem.h:174
struct SCHSPattern_ * next
Definition: util-mpm-hs.h:49
void(* InitThreadCtx)(struct MpmCtx_ *, struct MpmThreadCtx_ *)
Definition: util-mpm.h:140
void * HashTableLookup(HashTable *ht, void *data, uint16_t datalen)
Definition: util-hash.c:193
struct SCHSPattern_ SCHSPattern
uint16_t maxlen
Definition: util-mpm.h:96
void MpmInitCtx(MpmCtx *mpm_ctx, uint16_t matcher)
Definition: util-mpm.c:261
MpmTableElmt mpm_table[MPM_TABLE_SIZE]
Definition: util-mpm.h:165
#define SCFree(a)
Definition: util-mem.h:236
SCHSPattern ** init_hash
Definition: util-mpm-hs.h:54
struct SCHSCtx_ SCHSCtx
uint32_t id
Definition: util-mpm-hs.h:38
#define SCMutex
void MpmHSRegister(void)
#define SCLogPerf(...)
Definition: util-debug.h:261
#define SCMutexLock(mut)
void * pattern_db
Definition: util-mpm-hs.h:57
structure for storing potential rule matches
int MpmAddPatternCI(struct MpmCtx_ *mpm_ctx, uint8_t *pat, uint16_t patlen, uint16_t offset, uint16_t depth, uint32_t pid, SigIntId sid, uint8_t flags)
Definition: util-mpm.c:319
SigIntId * sids
Definition: util-mpm-hs.h:45
uint32_t(* Search)(const struct MpmCtx_ *, struct MpmThreadCtx_ *, PrefilterRuleStore *, const uint8_t *, uint32_t)
Definition: util-mpm.h:158
uint8_t len
Per thread variable structure.
Definition: threadvars.h:57
const char * name
Definition: util-mpm.h:138
void * ctx
Definition: util-mpm.h:83
uint32_t sids_size
Definition: util-mpm-hs.h:44
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
void UTHFreePackets(Packet **p, int numpkts)
UTHFreePackets: function to release the allocated data from UTHBuildPacket and the packet itself...
int(* AddPatternNocase)(struct MpmCtx_ *, uint8_t *, uint16_t, uint16_t, uint16_t, uint32_t, SigIntId, uint8_t)
Definition: util-mpm.h:156
uint8_t flags
Definition: util-mpm-hs.h:34
#define SigIntId
int(* AddPattern)(struct MpmCtx_ *, uint8_t *, uint16_t, uint16_t, uint16_t, uint32_t, SigIntId, uint8_t)
Definition: util-mpm.h:155
DetectEngineCtx * DetectEngineCtxInit(void)
#define MPM_PATTERN_FLAG_OFFSET
Definition: util-mpm.h:130
void * ctx
Definition: util-mpm.h:47
#define SCMUTEX_INITIALIZER