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  SCMutexLock(&g_hs_ref_info_mutex);
137  if (g_hs_ref_info != NULL) {
138  SCMutexUnlock(&g_hs_ref_info_mutex);
139  return g_hs_ref_info;
140  }
141 
142  hs_database_t *ref_db = NULL;
143  hs_compile_error_t *compile_err = NULL;
144  hs_error_t err = hs_compile("Suricata suricatta is the scientific name for the meerkat",
145  HS_FLAG_SINGLEMATCH, HS_MODE_BLOCK, NULL, &ref_db, &compile_err);
146  if (err == HS_SUCCESS && ref_db != NULL) {
147  if (hs_database_info(ref_db, &g_hs_ref_info) != HS_SUCCESS) {
148  if (g_hs_ref_info)
149  SCFree(g_hs_ref_info);
150  g_hs_ref_info = NULL;
151  }
152  hs_free_database(ref_db);
153  }
154  if (compile_err != NULL) {
155  SCLogInfo("Failed to compile reference Hyperscan database: %s", compile_err->message);
156  hs_free_compile_error(compile_err);
157  }
158 
159  SCMutexUnlock(&g_hs_ref_info_mutex);
160  return g_hs_ref_info;
161 }
162 
163 int HSLoadCache(hs_database_t **hs_db, const char *hs_db_hash, const char *dirpath)
164 {
165  char hash_file_static[PATH_MAX];
166  int ret = (int)HSCacheConstructFPath(
167  dirpath, hs_db_hash, hash_file_static, sizeof(hash_file_static));
168 
169  if (ret != 0)
170  return -1;
171 
172  SCLogDebug("Loading the cached HS DB from %s", hash_file_static);
173  if (!SCPathExists(hash_file_static))
174  return -1;
175 
176  char *db_info = NULL;
177  char *buffer = NULL;
178  size_t buffer_size;
179  buffer = HSReadStream(hash_file_static, &buffer_size);
180  if (!buffer) {
181  SCLogWarning("Hyperscan cached DB file %s cannot be read", hash_file_static);
182  return -1;
183  }
184 
185  hs_error_t error = hs_deserialize_database(buffer, buffer_size, hs_db);
186  if (error != HS_SUCCESS) {
187  SCLogWarning("Failed to deserialize Hyperscan database of %s: %s", hash_file_static,
188  HSErrorToStr(error));
189  ret = -1;
190  goto freeup;
191  }
192 
193  // Verify the loaded database is compatible with the current Hyperscan
194  // If both the loaded DB and the reference DB fail to load, consider the cache.
195  const char *ref_info = HSGetReferenceDbInfo();
196  if (ref_info != NULL) {
197  if (hs_database_info(*hs_db, &db_info) != HS_SUCCESS || db_info == NULL) {
198  SCLogDebug("Failed to query info for loaded Hyperscan database %s: %s",
199  hash_file_static, HSErrorToStr(error));
200  ret = -1;
201  goto freeup;
202  }
203  if (strcmp(db_info, ref_info) != 0) {
204  SCLogDebug("Loaded Hyperscan database %s is incompatible with the current "
205  "Hyperscan installation and will be ignored",
206  hash_file_static);
207  ret = -1;
208  goto freeup;
209  }
210  }
211 
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  const char *ref_info = HSGetReferenceDbInfo();
299  if (ref_info != NULL) {
300  SCSha256Update(hasher, (const uint8_t *)ref_info, strlen(ref_info));
301  }
302 
303  SCSha256Update(hasher, (const uint8_t *)&pd->pattern_cnt, sizeof(pd->pattern_cnt));
304  for (uint32_t i = 0; i < pd->pattern_cnt; i++) {
305  SCHSCachePatternHash(pd->parray[i], hasher);
306  }
307 
308  if (!SCSha256FinalizeToHex(hasher, hash, hash_len)) {
309  hasher = NULL;
310  SCLogDebug("sha256 hashing failed");
311  return -1;
312  }
313 
314  hasher = NULL;
315  return 0;
316 }
317 
318 void HSSaveCacheIterator(void *data, void *aux)
319 {
320  PatternDatabase *pd = (PatternDatabase *)data;
321  struct HsIteratorData *iter_data = (struct HsIteratorData *)aux;
322  if (pd->no_cache)
323  return;
324 
325  // count only cacheable DBs
326  iter_data->pd_stats->hs_cacheable_dbs_cnt++;
327  if (pd->cached) {
328  iter_data->pd_stats->hs_dbs_cache_loaded_cnt++;
329  return;
330  }
331 
332  char hs_db_hash[SC_SHA256_LEN * 2 + 1]; // * 2 for hex +1 for nul terminator
333  if (HSHashDb(pd, hs_db_hash, ARRAY_SIZE(hs_db_hash)) != 0) {
334  return;
335  }
336  if (HSSaveCache(pd->hs_db, hs_db_hash, iter_data->cache_path) == 0) {
337  pd->cached = true; // for rule reloads
338  iter_data->pd_stats->hs_dbs_cache_saved_cnt++;
339  }
340 }
341 
342 void HSCacheFilenameUsedIterator(void *data, void *aux)
343 {
344  PatternDatabase *pd = (PatternDatabase *)data;
345  struct HsInUseCacheFilesIteratorData *iter_data = (struct HsInUseCacheFilesIteratorData *)aux;
346  if (pd->no_cache || !pd->cached)
347  return;
348 
349  char hs_db_hash[SC_SHA256_LEN * 2 + 1]; // * 2 for hex +1 for nul terminator
350  if (HSHashDb(pd, hs_db_hash, ARRAY_SIZE(hs_db_hash)) != 0) {
351  return;
352  }
353 
354  char *fpath = SCCalloc(PATH_MAX, sizeof(char));
355  if (fpath == NULL) {
356  SCLogWarning("Failed to allocate memory for cache file path");
357  return;
358  }
359  if (HSCacheConstructFPath(iter_data->cache_path, hs_db_hash, fpath, PATH_MAX)) {
360  SCFree(fpath);
361  return;
362  }
363 
364  int r = HashTableAdd(iter_data->tbl, (void *)fpath, (uint16_t)strlen(fpath));
365  if (r < 0) {
366  SCLogWarning("Failed to add used cache file path %s to hash table", fpath);
367  SCFree(fpath);
368  }
369 }
370 
371 /**
372  * \brief Check if HS cache file is stale by age.
373  *
374  * \param mtime File modification time.
375  * \param cutoff Time cutoff (files older than this will be removed).
376  *
377  * \retval true if file should be pruned, false otherwise.
378  */
379 static bool HSPruneFileByAge(time_t mtime, time_t cutoff)
380 {
381  return mtime < cutoff;
382 }
383 
384 /**
385  * \brief Check if HS cache file is version-compatible.
386  *
387  * \param filename Cache file name.
388  *
389  * \retval true if file should be pruned, false otherwise.
390  */
391 static bool HSPruneFileByVersion(const char *filename)
392 {
393  if (strlen(filename) < strlen(HS_CACHE_FILE_SUFFIX)) {
394  return true;
395  }
396 
397  const char *underscore = strrchr(filename, '_');
398  if (underscore == NULL || strcmp(underscore, HS_CACHE_FILE_SUFFIX) != 0) {
399  return true;
400  }
401 
402  return false;
403 }
404 
405 int SCHSCachePruneEvaluate(MpmConfig *mpm_conf, HashTable *inuse_caches)
406 {
407  if (mpm_conf == NULL || mpm_conf->cache_dir_path == NULL)
408  return -1;
409  if (mpm_conf->cache_max_age_seconds == 0)
410  return 0; // disabled
411 
412  const time_t now = time(NULL);
413  if (now == (time_t)-1) {
414  return -1;
415  } else if (mpm_conf->cache_max_age_seconds >= (uint64_t)now) {
416  return 0;
417  }
418 
419  DIR *dir = opendir(mpm_conf->cache_dir_path);
420  if (dir == NULL) {
421  return -1;
422  }
423 
424  struct dirent *ent;
425  char path[PATH_MAX];
426  uint32_t considered = 0, removed_by_age = 0, removed_by_version = 0;
427  const time_t cutoff = now - (time_t)mpm_conf->cache_max_age_seconds;
428  while ((ent = readdir(dir)) != NULL) {
429  const char *name = ent->d_name;
430  size_t namelen = strlen(name);
431  if (namelen < 3 || strcmp(name + namelen - 3, ".hs") != 0)
432  continue;
433 
434  if (PathMerge(path, ARRAY_SIZE(path), mpm_conf->cache_dir_path, name) != 0)
435  continue;
436 
437  struct stat st;
438  /* TOCTOU: race window between stat and unlink is acceptable here.
439  * On Linux somebody can still modify (use the cache file) between the
440  * fstat and unlink, on Windows (HS not supported there but still relevant)
441  * TOCTOU happens when closing the file descriptor and unlinking the file.
442  * Cache mechanism is best-effort and e.g. not pruning or pruning an extra
443  * cache file is not problematic.
444  * Stat is used here to ease file handling as fstat doesn't bring any benefit */
445  /* coverity[toctou] */
446  if (SCStatFn(path, &st) != 0 || !S_ISREG(st.st_mode))
447  continue;
448 
449  considered++;
450 
451  const bool prune_by_age = HSPruneFileByAge(st.st_mtime, cutoff);
452  const bool prune_by_version = HSPruneFileByVersion(name);
453  if (!prune_by_age && !prune_by_version)
454  continue;
455 
456  void *cache_inuse = HashTableLookup(inuse_caches, path, (uint16_t)strlen(path));
457  if (cache_inuse != NULL)
458  continue; // in use
459 
460  /* coverity[toctou] */
461  int ret = unlink(path);
462  if (ret == 0 || (ret == -1 && errno == ENOENT)) {
463  if (prune_by_version)
464  removed_by_version++;
465  else if (prune_by_age)
466  removed_by_age++;
467  SCLogDebug("File %s removed because of %s%s%s", path, prune_by_age ? "age" : "",
468  prune_by_age && prune_by_version ? " and " : "",
469  prune_by_version ? "incompatible version" : "");
470  } else {
471  SCLogWarning("Failed to prune \"%s\": %s", path, strerror(errno));
472  }
473  }
474  closedir(dir);
475 
476  PatternDatabaseCache *pd_cache_stats = mpm_conf->cache_stats;
477  if (pd_cache_stats) {
478  pd_cache_stats->hs_dbs_cache_pruned_by_age_cnt = removed_by_age;
479  pd_cache_stats->hs_dbs_cache_pruned_by_version_cnt = removed_by_version;
480  pd_cache_stats->hs_dbs_cache_pruned_considered_cnt = considered;
481  pd_cache_stats->hs_dbs_cache_pruned_cutoff = cutoff;
482  pd_cache_stats->cache_max_age_seconds = mpm_conf->cache_max_age_seconds;
483  }
484  return 0;
485 }
486 
487 void *SCHSCacheStatsInit(void)
488 {
489  PatternDatabaseCache *pd_cache_stats = SCCalloc(1, sizeof(PatternDatabaseCache));
490  if (pd_cache_stats == NULL) {
491  SCLogError("Failed to allocate memory for Hyperscan cache stats");
492  return NULL;
493  }
494  return pd_cache_stats;
495 }
496 
497 void SCHSCacheStatsPrint(void *data)
498 {
499  if (data == NULL) {
500  return;
501  }
502 
503  PatternDatabaseCache *pd_cache_stats = (PatternDatabaseCache *)data;
504 
505  char time_str[64];
506  struct tm tm_s;
507  struct tm *tm_info = SCLocalTime(pd_cache_stats->hs_dbs_cache_pruned_cutoff, &tm_s);
508  if (tm_info != NULL) {
509  strftime(time_str, ARRAY_SIZE(time_str), "%Y-%m-%d %H:%M:%S", tm_info);
510  } else {
511  snprintf(time_str, ARRAY_SIZE(time_str), "%" PRIu64 " seconds",
512  pd_cache_stats->cache_max_age_seconds);
513  }
514 
515  if (pd_cache_stats->hs_cacheable_dbs_cnt) {
516  SCLogPerf("rule group caching - loaded: %u newly cached: %u total cacheable: %u",
517  pd_cache_stats->hs_dbs_cache_loaded_cnt, pd_cache_stats->hs_dbs_cache_saved_cnt,
518  pd_cache_stats->hs_cacheable_dbs_cnt);
519  }
520  if (pd_cache_stats->hs_dbs_cache_pruned_considered_cnt) {
521  if (pd_cache_stats->hs_dbs_cache_pruned_by_version_cnt) {
522  SCLogInfo("rule group cache pruning removed %u/%u of HS caches due to "
523  "version-incompatibility (not v%s)",
524  pd_cache_stats->hs_dbs_cache_pruned_by_version_cnt,
525  pd_cache_stats->hs_dbs_cache_pruned_considered_cnt, HS_CACHE_FILE_VERSION);
526  }
527  if (pd_cache_stats->hs_dbs_cache_pruned_by_age_cnt) {
528  SCLogInfo("rule group cache pruning removed %u/%u of HS caches due to "
529  "age (older than %s)",
530  pd_cache_stats->hs_dbs_cache_pruned_by_age_cnt,
531  pd_cache_stats->hs_dbs_cache_pruned_considered_cnt, time_str);
532  }
533  }
534 }
535 
536 void SCHSCacheStatsDeinit(void *data)
537 {
538  if (data == NULL) {
539  return;
540  }
541  PatternDatabaseCache *pd_cache_stats = (PatternDatabaseCache *)data;
542  SCFree(pd_cache_stats);
543 }
544 
545 void SCHSCacheDeinit(void)
546 {
547  SCMutexLock(&g_hs_ref_info_mutex);
548  if (g_hs_ref_info != NULL) {
549  SCFree(g_hs_ref_info);
550  g_hs_ref_info = NULL;
551  }
552  SCMutexUnlock(&g_hs_ref_info_mutex);
553 }
554 
555 #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