suricata
util-mpm-hs-cache.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2024 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 Lukas Sismis <lsismis@oisf.net>
22  *
23  * Hyperscan cache helper utilities for MPM cache files.
24  */
25 
26 #include "suricata-common.h"
27 #include "suricata.h"
28 #include "detect-engine.h"
29 #include "util-debug.h"
30 #include "util-hash-lookup3.h"
31 #include "util-mpm-hs-core.h"
32 #include "util-mpm-hs-cache.h"
33 #include "util-path.h"
34 
35 #ifdef BUILD_HYPERSCAN
36 
37 #include "rust.h"
38 #include <hs.h>
39 
40 #define HS_CACHE_FILE_VERSION "2"
41 #define HS_CACHE_FILE_SUFFIX "_v" HS_CACHE_FILE_VERSION ".hs"
42 
43 static SCMutex g_hs_ref_info_mutex = SCMUTEX_INITIALIZER;
44 static char *g_hs_ref_info = NULL;
45 
46 static int16_t HSCacheConstructFPath(
47  const char *dir_path, const char *db_hash, char *out_path, uint16_t out_path_size)
48 {
49  char filename[NAME_MAX];
50  uint64_t r = snprintf(filename, sizeof(filename), "%s" HS_CACHE_FILE_SUFFIX, db_hash);
51  if (r != (uint64_t)(strlen(db_hash) + strlen(HS_CACHE_FILE_SUFFIX)))
52  return -1;
53 
54  r = PathMerge(out_path, out_path_size, dir_path, filename);
55  if (r)
56  return -1;
57 
58  return 0;
59 }
60 
61 static char *HSReadStream(const char *file_path, size_t *buffer_sz)
62 {
63  FILE *file = fopen(file_path, "rb");
64  if (!file) {
65  SCLogDebug("Failed to open file %s: %s", file_path, strerror(errno));
66  return NULL;
67  }
68 
69  // Seek to the end of the file to determine its size
70  fseek(file, 0, SEEK_END);
71  long file_sz = ftell(file);
72  if (file_sz < 0) {
73  SCLogDebug("Failed to determine file size of %s: %s", file_path, strerror(errno));
74  fclose(file);
75  return NULL;
76  }
77 
78  char *buffer = (char *)SCCalloc(file_sz, sizeof(char));
79  if (!buffer) {
80  SCLogWarning("Failed to allocate memory");
81  fclose(file);
82  return NULL;
83  }
84 
85  // Rewind file pointer and read the file into the buffer
86  errno = 0;
87  rewind(file);
88  if (errno != 0) {
89  SCLogDebug("Failed to rewind file %s: %s", file_path, strerror(errno));
90  SCFree(buffer);
91  fclose(file);
92  return NULL;
93  }
94  size_t bytes_read = fread(buffer, 1, file_sz, file);
95  if (bytes_read != (size_t)file_sz) {
96  SCLogDebug("Failed to read the entire file %s: %s", file_path, strerror(errno));
97  SCFree(buffer);
98  fclose(file);
99  return NULL;
100  }
101 
102  *buffer_sz = file_sz;
103  fclose(file);
104  return buffer;
105 }
106 
107 /**
108  * Function to hash the searched pattern, only things relevant to Hyperscan
109  * compilation are hashed.
110  */
111 static void SCHSCachePatternHash(const SCHSPattern *p, SCSha256 *sha256)
112 {
113  BUG_ON(p->original_pat == NULL);
114  BUG_ON(p->sids == NULL);
115 
116  SCSha256Update(sha256, (const uint8_t *)&p->len, sizeof(p->len));
117  SCSha256Update(sha256, (const uint8_t *)&p->flags, sizeof(p->flags));
118  SCSha256Update(sha256, (const uint8_t *)p->original_pat, p->len);
119  SCSha256Update(sha256, (const uint8_t *)&p->id, sizeof(p->id));
120  SCSha256Update(sha256, (const uint8_t *)&p->offset, sizeof(p->offset));
121  SCSha256Update(sha256, (const uint8_t *)&p->depth, sizeof(p->depth));
122  SCSha256Update(sha256, (const uint8_t *)&p->sids_size, sizeof(p->sids_size));
123  SCSha256Update(sha256, (const uint8_t *)p->sids, p->sids_size * sizeof(SigIntId));
124 }
125 
126 /**
127  * \brief Get the hs_database_info string for a reference BLOCK-mode database
128  * compiled with the current Hyperscan installation.
129  *
130  * Compiled lazily on the first call and cached. Thread-safe.
131  *
132  * \retval Pointer to info string, or NULL on failure. Do not free the ptr.
133  */
134 static const char *HSGetReferenceDbInfo(void)
135 {
136  if (g_hs_ref_info != NULL) {
137  return g_hs_ref_info;
138  }
139 
140  hs_database_t *ref_db = NULL;
141  hs_compile_error_t *compile_err = NULL;
142  hs_error_t err = hs_compile("Suricata suricatta is the scientific name for the meerkat",
143  HS_FLAG_SINGLEMATCH, HS_MODE_BLOCK, NULL, &ref_db, &compile_err);
144  if (err == HS_SUCCESS && ref_db != NULL) {
145  if (hs_database_info(ref_db, &g_hs_ref_info) != HS_SUCCESS) {
146  if (g_hs_ref_info)
147  SCFree(g_hs_ref_info);
148  g_hs_ref_info = NULL;
149  }
150  hs_free_database(ref_db);
151  }
152  if (compile_err != NULL) {
153  SCLogInfo("Failed to compile reference Hyperscan database: %s", compile_err->message);
154  hs_free_compile_error(compile_err);
155  }
156 
157  return g_hs_ref_info;
158 }
159 
160 int HSLoadCache(hs_database_t **hs_db, const char *hs_db_hash, const char *dirpath)
161 {
162  char hash_file_static[PATH_MAX];
163  int ret = (int)HSCacheConstructFPath(
164  dirpath, hs_db_hash, hash_file_static, sizeof(hash_file_static));
165 
166  if (ret != 0)
167  return -1;
168 
169  SCLogDebug("Loading the cached HS DB from %s", hash_file_static);
170  if (!SCPathExists(hash_file_static))
171  return -1;
172 
173  char *db_info = NULL;
174  char *buffer = NULL;
175  size_t buffer_size;
176  buffer = HSReadStream(hash_file_static, &buffer_size);
177  if (!buffer) {
178  SCLogWarning("Hyperscan cached DB file %s cannot be read", hash_file_static);
179  return -1;
180  }
181 
182  hs_error_t error = hs_deserialize_database(buffer, buffer_size, hs_db);
183  if (error != HS_SUCCESS) {
184  SCLogWarning("Failed to deserialize Hyperscan database of %s: %s", hash_file_static,
185  HSErrorToStr(error));
186  ret = -1;
187  goto freeup;
188  }
189 
190  // Verify the loaded database is compatible with the current Hyperscan
191  // If both the loaded DB and the reference DB fail to load, consider the cache.
192  SCMutexLock(&g_hs_ref_info_mutex);
193  const char *ref_info = HSGetReferenceDbInfo();
194  if (ref_info != NULL) {
195  if (hs_database_info(*hs_db, &db_info) != HS_SUCCESS || db_info == NULL) {
196  SCLogDebug("Failed to query info for loaded Hyperscan database %s: %s",
197  hash_file_static, HSErrorToStr(error));
198  ret = -1;
199  SCMutexUnlock(&g_hs_ref_info_mutex);
200  goto freeup;
201  }
202  if (strcmp(db_info, ref_info) != 0) {
203  SCLogDebug("Loaded Hyperscan database %s is incompatible with the current "
204  "Hyperscan installation and will be ignored",
205  hash_file_static);
206  ret = -1;
207  SCMutexUnlock(&g_hs_ref_info_mutex);
208  goto freeup;
209  }
210  }
211  SCMutexUnlock(&g_hs_ref_info_mutex);
212  ret = 0;
213  /* Touch file to update modification time so active caches are retained. */
214  if (SCTouchFile(hash_file_static) != 0) {
215  SCLogDebug("Failed to update mtime for %s", hash_file_static);
216  }
217 
218 freeup:
219  if (ret != 0 && *hs_db != NULL) {
220  hs_free_database(*hs_db);
221  *hs_db = NULL;
222  }
223  if (db_info)
224  SCFree(db_info);
225  SCFree(buffer);
226  return ret;
227 }
228 
229 static int HSSaveCache(hs_database_t *hs_db, const char *hs_db_hash, const char *dstpath)
230 {
231  static bool notified = false;
232  char *db_stream = NULL;
233  size_t db_size;
234  int ret;
235 
236  hs_error_t err = hs_serialize_database(hs_db, &db_stream, &db_size);
237  if (err != HS_SUCCESS) {
238  SCLogWarning("Failed to serialize Hyperscan database: %s", HSErrorToStr(err));
239  ret = -1;
240  goto cleanup;
241  }
242 
243  char hash_file_static[PATH_MAX];
244  ret = (int)HSCacheConstructFPath(
245  dstpath, hs_db_hash, hash_file_static, sizeof(hash_file_static));
246  if (ret != 0)
247  goto cleanup;
248  SCLogDebug("Caching the compiled HS at %s", hash_file_static);
249  if (SCPathExists(hash_file_static)) {
250  // potentially signs that it might not work as expected as we got into
251  // hash collision. If this happens with older and not used caches it is
252  // fine.
253  // It is problematic when one ruleset yields two colliding MPM groups.
254  SCLogWarning("Overwriting cache file %s. If the problem persists consider switching off "
255  "the caching",
256  hash_file_static);
257  }
258 
259  FILE *db_cache_out = fopen(hash_file_static, "wb");
260  if (!db_cache_out) {
261  if (!notified) {
262  SCLogWarning("Failed to create Hyperscan cache file, make sure the folder exist and is "
263  "writable or adjust sgh-mpm-caching-path setting (%s)",
264  hash_file_static);
265  notified = true;
266  }
267  ret = -1;
268  goto cleanup;
269  }
270  size_t r = fwrite(db_stream, sizeof(db_stream[0]), db_size, db_cache_out);
271  if (r != db_size) {
272  SCLogWarning("Failed to write to file: %s", hash_file_static);
273  // possibly a corrupted DB cache was created
274  if (remove(hash_file_static) != 0) {
275  SCLogWarning("Failed to remove corrupted cache file: %s", hash_file_static);
276  }
277  }
278  ret = fclose(db_cache_out);
279  if (ret != 0) {
280  SCLogWarning("Failed to close file: %s", hash_file_static);
281  goto cleanup;
282  }
283 
284 cleanup:
285  if (db_stream)
286  SCFree(db_stream);
287  return ret;
288 }
289 
290 int HSHashDb(const PatternDatabase *pd, char *hash, size_t hash_len)
291 {
292  SCSha256 *hasher = SCSha256New();
293  if (hasher == NULL) {
294  SCLogDebug("sha256 hashing failed");
295  return -1;
296  }
297 
298  SCMutexLock(&g_hs_ref_info_mutex);
299  const char *ref_info = HSGetReferenceDbInfo();
300  if (ref_info != NULL) {
301  SCSha256Update(hasher, (const uint8_t *)ref_info, strlen(ref_info));
302  }
303  SCMutexUnlock(&g_hs_ref_info_mutex);
304 
305  SCSha256Update(hasher, (const uint8_t *)&pd->pattern_cnt, sizeof(pd->pattern_cnt));
306  for (uint32_t i = 0; i < pd->pattern_cnt; i++) {
307  SCHSCachePatternHash(pd->parray[i], hasher);
308  }
309 
310  if (!SCSha256FinalizeToHex(hasher, hash, hash_len)) {
311  hasher = NULL;
312  SCLogDebug("sha256 hashing failed");
313  return -1;
314  }
315 
316  hasher = NULL;
317  return 0;
318 }
319 
320 void HSSaveCacheIterator(void *data, void *aux)
321 {
322  PatternDatabase *pd = (PatternDatabase *)data;
323  struct HsIteratorData *iter_data = (struct HsIteratorData *)aux;
324  if (pd->no_cache)
325  return;
326 
327  // count only cacheable DBs
328  iter_data->pd_stats->hs_cacheable_dbs_cnt++;
329  if (pd->cached) {
330  iter_data->pd_stats->hs_dbs_cache_loaded_cnt++;
331  return;
332  }
333 
334  char hs_db_hash[SC_SHA256_LEN * 2 + 1]; // * 2 for hex +1 for nul terminator
335  if (HSHashDb(pd, hs_db_hash, ARRAY_SIZE(hs_db_hash)) != 0) {
336  return;
337  }
338  if (HSSaveCache(pd->hs_db, hs_db_hash, iter_data->cache_path) == 0) {
339  pd->cached = true; // for rule reloads
340  iter_data->pd_stats->hs_dbs_cache_saved_cnt++;
341  }
342 }
343 
344 void HSCacheFilenameUsedIterator(void *data, void *aux)
345 {
346  PatternDatabase *pd = (PatternDatabase *)data;
347  struct HsInUseCacheFilesIteratorData *iter_data = (struct HsInUseCacheFilesIteratorData *)aux;
348  if (pd->no_cache || !pd->cached)
349  return;
350 
351  char hs_db_hash[SC_SHA256_LEN * 2 + 1]; // * 2 for hex +1 for nul terminator
352  if (HSHashDb(pd, hs_db_hash, ARRAY_SIZE(hs_db_hash)) != 0) {
353  return;
354  }
355 
356  char *fpath = SCCalloc(PATH_MAX, sizeof(char));
357  if (fpath == NULL) {
358  SCLogWarning("Failed to allocate memory for cache file path");
359  return;
360  }
361  if (HSCacheConstructFPath(iter_data->cache_path, hs_db_hash, fpath, PATH_MAX)) {
362  SCFree(fpath);
363  return;
364  }
365 
366  int r = HashTableAdd(iter_data->tbl, (void *)fpath, (uint16_t)strlen(fpath));
367  if (r < 0) {
368  SCLogWarning("Failed to add used cache file path %s to hash table", fpath);
369  SCFree(fpath);
370  }
371 }
372 
373 /**
374  * \brief Check if HS cache file is stale by age.
375  *
376  * \param mtime File modification time.
377  * \param cutoff Time cutoff (files older than this will be removed).
378  *
379  * \retval true if file should be pruned, false otherwise.
380  */
381 static bool HSPruneFileByAge(time_t mtime, time_t cutoff)
382 {
383  return mtime < cutoff;
384 }
385 
386 /**
387  * \brief Check if HS cache file is version-compatible.
388  *
389  * \param filename Cache file name.
390  *
391  * \retval true if file should be pruned, false otherwise.
392  */
393 static bool HSPruneFileByVersion(const char *filename)
394 {
395  if (strlen(filename) < strlen(HS_CACHE_FILE_SUFFIX)) {
396  return true;
397  }
398 
399  const char *underscore = strrchr(filename, '_');
400  if (underscore == NULL || strcmp(underscore, HS_CACHE_FILE_SUFFIX) != 0) {
401  return true;
402  }
403 
404  return false;
405 }
406 
407 int SCHSCachePruneEvaluate(MpmConfig *mpm_conf, HashTable *inuse_caches)
408 {
409  if (mpm_conf == NULL || mpm_conf->cache_dir_path == NULL)
410  return -1;
411  if (mpm_conf->cache_max_age_seconds == 0)
412  return 0; // disabled
413 
414  const time_t now = time(NULL);
415  if (now == (time_t)-1) {
416  return -1;
417  } else if (mpm_conf->cache_max_age_seconds >= (uint64_t)now) {
418  return 0;
419  }
420 
421  DIR *dir = opendir(mpm_conf->cache_dir_path);
422  if (dir == NULL) {
423  return -1;
424  }
425 
426  struct dirent *ent;
427  char path[PATH_MAX];
428  uint32_t considered = 0, removed_by_age = 0, removed_by_version = 0;
429  const time_t cutoff = now - (time_t)mpm_conf->cache_max_age_seconds;
430  while ((ent = readdir(dir)) != NULL) {
431  const char *name = ent->d_name;
432  size_t namelen = strlen(name);
433  if (namelen < 3 || strcmp(name + namelen - 3, ".hs") != 0)
434  continue;
435 
436  if (PathMerge(path, ARRAY_SIZE(path), mpm_conf->cache_dir_path, name) != 0)
437  continue;
438 
439  struct stat st;
440  /* TOCTOU: race window between stat and unlink is acceptable here.
441  * On Linux somebody can still modify (use the cache file) between the
442  * fstat and unlink, on Windows (HS not supported there but still relevant)
443  * TOCTOU happens when closing the file descriptor and unlinking the file.
444  * Cache mechanism is best-effort and e.g. not pruning or pruning an extra
445  * cache file is not problematic.
446  * Stat is used here to ease file handling as fstat doesn't bring any benefit */
447  /* coverity[toctou] */
448  if (SCStatFn(path, &st) != 0 || !S_ISREG(st.st_mode))
449  continue;
450 
451  considered++;
452 
453  const bool prune_by_age = HSPruneFileByAge(st.st_mtime, cutoff);
454  const bool prune_by_version = HSPruneFileByVersion(name);
455  if (!prune_by_age && !prune_by_version)
456  continue;
457 
458  void *cache_inuse = HashTableLookup(inuse_caches, path, (uint16_t)strlen(path));
459  if (cache_inuse != NULL)
460  continue; // in use
461 
462  /* coverity[toctou] */
463  int ret = unlink(path);
464  if (ret == 0 || (ret == -1 && errno == ENOENT)) {
465  if (prune_by_version)
466  removed_by_version++;
467  else if (prune_by_age)
468  removed_by_age++;
469  SCLogDebug("File %s removed because of %s%s%s", path, prune_by_age ? "age" : "",
470  prune_by_age && prune_by_version ? " and " : "",
471  prune_by_version ? "incompatible version" : "");
472  } else {
473  SCLogWarning("Failed to prune \"%s\": %s", path, strerror(errno));
474  }
475  }
476  closedir(dir);
477 
478  PatternDatabaseCache *pd_cache_stats = mpm_conf->cache_stats;
479  if (pd_cache_stats) {
480  pd_cache_stats->hs_dbs_cache_pruned_by_age_cnt = removed_by_age;
481  pd_cache_stats->hs_dbs_cache_pruned_by_version_cnt = removed_by_version;
482  pd_cache_stats->hs_dbs_cache_pruned_considered_cnt = considered;
483  pd_cache_stats->hs_dbs_cache_pruned_cutoff = cutoff;
484  pd_cache_stats->cache_max_age_seconds = mpm_conf->cache_max_age_seconds;
485  }
486  return 0;
487 }
488 
489 void *SCHSCacheStatsInit(void)
490 {
491  PatternDatabaseCache *pd_cache_stats = SCCalloc(1, sizeof(PatternDatabaseCache));
492  if (pd_cache_stats == NULL) {
493  SCLogError("Failed to allocate memory for Hyperscan cache stats");
494  return NULL;
495  }
496  return pd_cache_stats;
497 }
498 
499 void SCHSCacheStatsPrint(void *data)
500 {
501  if (data == NULL) {
502  return;
503  }
504 
505  PatternDatabaseCache *pd_cache_stats = (PatternDatabaseCache *)data;
506 
507  char time_str[64];
508  struct tm tm_s;
509  struct tm *tm_info = SCLocalTime(pd_cache_stats->hs_dbs_cache_pruned_cutoff, &tm_s);
510  if (tm_info != NULL) {
511  strftime(time_str, ARRAY_SIZE(time_str), "%Y-%m-%d %H:%M:%S", tm_info);
512  } else {
513  snprintf(time_str, ARRAY_SIZE(time_str), "%" PRIu64 " seconds",
514  pd_cache_stats->cache_max_age_seconds);
515  }
516 
517  if (pd_cache_stats->hs_cacheable_dbs_cnt) {
518  SCLogPerf("rule group caching - loaded: %u newly cached: %u total cacheable: %u",
519  pd_cache_stats->hs_dbs_cache_loaded_cnt, pd_cache_stats->hs_dbs_cache_saved_cnt,
520  pd_cache_stats->hs_cacheable_dbs_cnt);
521  }
522  if (pd_cache_stats->hs_dbs_cache_pruned_considered_cnt) {
523  if (pd_cache_stats->hs_dbs_cache_pruned_by_version_cnt) {
524  SCLogInfo("rule group cache pruning removed %u/%u of HS caches due to "
525  "version-incompatibility (not v%s)",
526  pd_cache_stats->hs_dbs_cache_pruned_by_version_cnt,
527  pd_cache_stats->hs_dbs_cache_pruned_considered_cnt, HS_CACHE_FILE_VERSION);
528  }
529  if (pd_cache_stats->hs_dbs_cache_pruned_by_age_cnt) {
530  SCLogInfo("rule group cache pruning removed %u/%u of HS caches due to "
531  "age (older than %s)",
532  pd_cache_stats->hs_dbs_cache_pruned_by_age_cnt,
533  pd_cache_stats->hs_dbs_cache_pruned_considered_cnt, time_str);
534  }
535  }
536 }
537 
538 void SCHSCacheStatsDeinit(void *data)
539 {
540  if (data == NULL) {
541  return;
542  }
543  PatternDatabaseCache *pd_cache_stats = (PatternDatabaseCache *)data;
544  SCFree(pd_cache_stats);
545 }
546 
547 void SCHSCacheDeinit(void)
548 {
549  SCMutexLock(&g_hs_ref_info_mutex);
550  if (g_hs_ref_info != NULL) {
551  SCFree(g_hs_ref_info);
552  g_hs_ref_info = NULL;
553  }
554  SCMutexUnlock(&g_hs_ref_info_mutex);
555 }
556 
557 #endif /* BUILD_HYPERSCAN */
detect-engine.h
PathMerge
int PathMerge(char *out_buf, size_t buf_size, const char *const dir, const char *const fname)
Definition: util-path.c:74
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:282
name
const char * name
Definition: detect-engine-proto.c:48
SCTouchFile
int SCTouchFile(const char *path)
Update access and modification time of an existing file to 'now'.
Definition: util-path.c:286
SC_SHA256_LEN
#define SC_SHA256_LEN
Definition: util-file.h:104
SCMutexLock
#define SCMutexLock(mut)
Definition: threads-debug.h:117
HashTable_
Definition: util-hash.h:35
rust.h
SCMUTEX_INITIALIZER
#define SCMUTEX_INITIALIZER
Definition: threads-debug.h:122
SCSha256
struct SCSha256 SCSha256
Definition: util-file.h:103
MpmConfig_::cache_dir_path
const char * cache_dir_path
Definition: util-mpm.h:92
util-debug.h
SCMutexUnlock
#define SCMutexUnlock(mut)
Definition: threads-debug.h:120
HashTableLookup
void * HashTableLookup(HashTable *ht, void *data, uint16_t datalen)
Definition: util-hash.c:182
SCLogWarning
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition: util-debug.h:262
HashTableAdd
int HashTableAdd(HashTable *ht, void *data, uint16_t datalen)
Definition: util-hash.c:120
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:317
SigIntId
#define SigIntId
Definition: detect-engine-state.h:38
SCLocalTime
struct tm * SCLocalTime(time_t timep, struct tm *result)
Definition: util-time.c:267
SCLogInfo
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:232
MpmConfig_::cache_max_age_seconds
uint64_t cache_max_age_seconds
Definition: util-mpm.h:93
SCPathExists
bool SCPathExists(const char *path)
Check if a path exists.
Definition: util-path.c:183
ARRAY_SIZE
#define ARRAY_SIZE(arr)
Definition: suricata-common.h:562
suricata-common.h
util-path.h
util-mpm-hs-core.h
SCLogPerf
#define SCLogPerf(...)
Definition: util-debug.h:241
MpmConfig_::cache_stats
void * cache_stats
Definition: util-mpm.h:94
util-hash-lookup3.h
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:274
SCFree
#define SCFree(p)
Definition: util-mem.h:61
MpmConfig_
Definition: util-mpm.h:91
suricata.h
SCStatFn
#define SCStatFn(pathname, statbuf)
Definition: util-path.h:35
util-mpm-hs-cache.h
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SCMutex
#define SCMutex
Definition: threads-debug.h:114