suricata
util-var-name.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2023 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author Victor Julien <victor@inliniac.net>
22  *
23  * Generic variable name utility functions
24  */
25 
26 #include "suricata-common.h"
27 #include "detect.h"
28 #include "util-hash-string.h"
29 #include "util-hashlist.h"
30 #include "util-var-name.h"
31 #include "util-validate.h"
32 
33 /* Overall Design:
34  *
35  * Base Store: "base"
36  *
37  * Used during keyword registration. Operates under lock. Base is shared
38  * between all detect engines, detect engine versions and tenants.
39  * Each variable name is ref counted.
40  *
41  * During the freeing of a detect engine / tenant, unregistration decreases
42  * the ref cnt.
43  *
44  * Base has both a string to id and a id to string hash table. String to
45  * id is used during parsing/registration. id to string during unregistration.
46  *
47  *
48  * Active Store Pointer (atomic)
49  *
50  * The "active" store atomic pointer points to the active store. The call
51  * to `VarNameStoreActivate` will build a new lookup store and hot swap
52  * the pointer.
53  *
54  * Ensuring memory safety. During the hot swap, the pointer is replaced, so
55  * any new call to the lookup functions will automatically use the new store.
56  * This leaves the case of any lookup happening concurrently with the pointer
57  * swap. For this case we add the old store to a free list. It gets a timestamp
58  * before which it cannot be freed.
59  *
60  *
61  * Free List
62  *
63  * The free list contains old stores that are waiting to get removed. They
64  * contain a timestamp that is checked before they are freed.
65  *
66  */
67 typedef struct VarNameStore_ {
70  uint32_t max_id;
75 
76 /** \brief Name2idx mapping structure for flowbits, flowvars and pktvars. */
77 typedef struct VariableName_ {
78  char *name;
79  enum VarTypes type; /* flowbit, pktvar, etc */
80  uint32_t id;
81  uint32_t ref_cnt;
83 
84 #define VARNAME_HASHSIZE 0x1000
85 #define VARID_HASHSIZE 0x1000
86 
87 static SCMutex base_lock = SCMUTEX_INITIALIZER;
88 static VarNameStore base = { .names = NULL, .ids = NULL, .max_id = 0 };
89 static TAILQ_HEAD(, VarNameStore_) free_list = TAILQ_HEAD_INITIALIZER(free_list);
90 static SC_ATOMIC_DECLARE(VarNameStorePtr, active);
91 
92 static uint32_t VariableNameHash(HashListTable *ht, void *buf, uint16_t buflen);
93 static char VariableNameCompare(void *buf1, uint16_t len1, void *buf2, uint16_t len2);
94 static uint32_t VariableIdHash(HashListTable *ht, void *ptr, uint16_t _unused);
95 static char VariableIdCompare(void *ptr1, uint16_t _unused1, void *ptr2, uint16_t _unused2);
96 static void VariableNameFree(void *data);
97 
98 void VarNameStoreInit(void)
99 {
100  SCMutexLock(&base_lock);
101  base.names = HashListTableInit(
102  VARNAME_HASHSIZE, VariableNameHash, VariableNameCompare, VariableNameFree);
103  if (base.names == NULL) {
104  FatalError("failed to initialize variable name hash (names)");
105  }
106 
107  /* base.names owns the allocation, so use a NULL Free pointer here */
108  base.ids = HashListTableInit(VARID_HASHSIZE, VariableIdHash, VariableIdCompare, NULL);
109  if (base.ids == NULL) {
110  FatalError("failed to initialize variable name hash (names)");
111  }
112  SC_ATOMIC_INITPTR(active);
113  SCMutexUnlock(&base_lock);
114 }
115 
117 {
118  SCMutexLock(&base_lock);
119  VarNameStore *s = SC_ATOMIC_GET(active);
120  if (s) {
123  SCFree(s);
124  s = NULL;
125  }
126  SC_ATOMIC_SET(active, NULL);
127 
128  while ((s = TAILQ_FIRST(&free_list))) {
129  TAILQ_REMOVE(&free_list, s, next);
130  DEBUG_VALIDATE_BUG_ON(TAILQ_FIRST(&free_list) == s);
133  SCFree(s);
134  }
135 
136  for (HashListTableBucket *b = HashListTableGetListHead(base.names); b != NULL;
137  b = HashListTableGetListNext(b)) {
140  if (vn->ref_cnt > 0) {
141  SCLogWarning("%s (type %u, id %u) still has ref_cnt %u", vn->name, vn->type, vn->id,
142  vn->ref_cnt);
143  }
144  }
145  HashListTableFree(base.ids);
146  base.ids = NULL;
147  HashListTableFree(base.names);
148  base.names = NULL;
149  base.max_id = 0;
150  SCMutexUnlock(&base_lock);
151 }
152 
153 /**
154  * \retval id or 0 on error
155  */
156 uint32_t VarNameStoreRegister(const char *name, const enum VarTypes type)
157 {
158  if (name == NULL) {
159  return 0;
160  }
161  SCMutexLock(&base_lock);
162  uint32_t id = 0;
163 
164  SCLogDebug("registering: name %s type %u", name, type);
165  VariableName lookup = { .type = type, .name = (char *)name };
166  VariableName *found = (VariableName *)HashListTableLookup(base.names, (void *)&lookup, 0);
167  if (found == NULL) {
168  VariableName *vn = SCCalloc(1, sizeof(VariableName));
169  if (likely(vn != NULL)) {
170  vn->type = type;
171  vn->name = SCStrdup(name);
172  if (vn->name != NULL) {
173  vn->ref_cnt = 1;
174  id = vn->id = ++base.max_id;
175  HashListTableAdd(base.names, (void *)vn, 0);
176  HashListTableAdd(base.ids, (void *)vn, 0);
177  SCLogDebug(
178  "new registration %s id %u type %u -> %u", vn->name, vn->id, vn->type, id);
179  } else {
180  SCFree(vn);
181  }
182  }
183  } else {
184  id = found->id;
185  found->ref_cnt++;
186  SCLogDebug("existing registration %s ref_cnt %u -> %u", name, found->ref_cnt, id);
187  }
188  SCMutexUnlock(&base_lock);
189  return id;
190 }
191 
192 const char *VarNameStoreSetupLookup(const uint32_t id, const enum VarTypes type)
193 {
194  const char *name = NULL;
195  SCMutexLock(&base_lock);
196  VariableName lookup = { .type = type, .id = id };
197  VariableName *found = (VariableName *)HashListTableLookup(base.ids, (void *)&lookup, 0);
198  if (found) {
199  name = found->name;
200  }
201  SCMutexUnlock(&base_lock);
202  return name;
203 }
204 
205 void VarNameStoreUnregister(const uint32_t id, const enum VarTypes type)
206 {
207  if (unlikely(id == 0)) {
208  /* There was an error registering the varname, so nothing to unregister */
209  return;
210  }
211  SCMutexLock(&base_lock);
212  VariableName lookup = { .type = type, .id = id };
213  VariableName *found = (VariableName *)HashListTableLookup(base.ids, (void *)&lookup, 0);
214  if (found) {
215  SCLogDebug("found %s ref_cnt %u", found->name, found->ref_cnt);
216  DEBUG_VALIDATE_BUG_ON(found->ref_cnt == 0);
217  found->ref_cnt--;
218  }
219  SCMutexUnlock(&base_lock);
220 }
221 
223 {
224  int result = 0;
225  SCMutexLock(&base_lock);
226  SCLogDebug("activating new lookup store");
227 
228  VarNameStore *new_active = NULL;
229 
230  // create lookup hash for id to string, strings should point to base
231  for (HashListTableBucket *b = HashListTableGetListHead(base.names); b != NULL;
232  b = HashListTableGetListNext(b)) {
234  BUG_ON(vn == NULL);
235  SCLogDebug("base: %s/%u/%u", vn->name, vn->id, vn->ref_cnt);
236  if (vn->ref_cnt == 0)
237  continue;
238 
239  if (new_active == NULL) {
240  new_active = SCCalloc(1, sizeof(*new_active));
241  if (new_active == NULL) {
242  result = -1;
243  goto out;
244  }
245 
246  new_active->names = HashListTableInit(
247  VARNAME_HASHSIZE, VariableNameHash, VariableNameCompare, NULL);
248  if (new_active->names == NULL) {
249  SCFree(new_active);
250  result = -1;
251  goto out;
252  }
253  new_active->ids =
254  HashListTableInit(VARID_HASHSIZE, VariableIdHash, VariableIdCompare, NULL);
255  if (new_active->ids == NULL) {
256  HashListTableFree(new_active->names);
257  SCFree(new_active);
258  result = -1;
259  goto out;
260  }
261  }
262 
263  /* memory is still owned by "base" */
264  HashListTableAdd(new_active->names, (void *)vn, 0);
265  HashListTableAdd(new_active->ids, (void *)vn, 0);
266  }
267 
268  if (new_active) {
269  SCTime_t now = SCTimeGetTime();
270 
271  VarNameStore *old_active = SC_ATOMIC_GET(active);
272  if (old_active) {
273  SCTime_t free_after = SCTIME_ADD_SECS(now, 60);
274  old_active->free_after = free_after;
275 
276  TAILQ_INSERT_TAIL(&free_list, old_active, next);
277  SCLogDebug("old active is stored in free list");
278  }
279 
280  SC_ATOMIC_SET(active, new_active);
281  SCLogDebug("new store active");
282 
283  VarNameStore *s = NULL;
284  while ((s = TAILQ_FIRST(&free_list))) {
285  char timebuf[64];
286  CreateIsoTimeString(s->free_after, timebuf, sizeof(timebuf));
287 
288  if (SCTIME_CMP_LTE(now, s->free_after)) {
289  SCLogDebug("not yet freeing store %p before %s", s, timebuf);
290  break;
291  }
292  SCLogDebug("freeing store %p with time %s", s, timebuf);
293  TAILQ_REMOVE(&free_list, s, next);
296  SCFree(s);
297  }
298  }
299 out:
300  SCLogDebug("activating new lookup store: complete %d", result);
301  SCMutexUnlock(&base_lock);
302  return result;
303 }
304 
305 /** \brief find name for id+type at packet time.
306  * As the `active` store won't be modified, we don't need locks. */
307 const char *VarNameStoreLookupById(const uint32_t id, const enum VarTypes type)
308 {
309  const char *name = NULL;
310 
311  /* coverity[missing_lock] */
312  const VarNameStore *current = SC_ATOMIC_GET(active);
313  if (current) {
314  VariableName lookup = { .type = type, .id = id };
315  /* coverity[missing_lock] */
316  const VariableName *found = HashListTableLookup(current->ids, (void *)&lookup, 0);
317  if (found) {
318  return found->name;
319  }
320  }
321 
322  return name;
323 }
324 
325 /** \brief find name for id+type at packet time.
326  * As the `active` store won't be modified, we don't need locks. */
327 uint32_t VarNameStoreLookupByName(const char *name, const enum VarTypes type)
328 {
329  /* coverity[missing_lock] */
330  const VarNameStore *current = SC_ATOMIC_GET(active);
331  if (current) {
332  VariableName lookup = { .name = (char *)name, .type = type };
333  /* coverity[missing_lock] */
334  const VariableName *found = HashListTableLookup(current->names, (void *)&lookup, 0);
335  if (found) {
336  return found->id;
337  }
338  }
339 
340  return 0;
341 }
342 
343 static uint32_t VariableNameHash(HashListTable *ht, void *buf, uint16_t buflen)
344 {
345  VariableName *vn = (VariableName *)buf;
346  uint32_t hash =
347  StringHashDjb2((const uint8_t *)vn->name, (uint32_t)strlen(vn->name)) + vn->type;
348  return (hash % VARNAME_HASHSIZE);
349 }
350 
351 static char VariableNameCompare(void *buf1, uint16_t len1, void *buf2, uint16_t len2)
352 {
353  VariableName *vn1 = (VariableName *)buf1;
354  VariableName *vn2 = (VariableName *)buf2;
355  return (vn1->type == vn2->type && strcmp(vn1->name, vn2->name) == 0);
356 }
357 
358 static uint32_t VariableIdHash(HashListTable *ht, void *ptr, uint16_t _unused)
359 {
360  VariableName *vn = (VariableName *)ptr;
361  uint32_t hash = vn->id << vn->type;
362  return (hash % VARID_HASHSIZE);
363 }
364 
365 static char VariableIdCompare(void *ptr1, uint16_t _unused1, void *ptr2, uint16_t _unused2)
366 {
367  VariableName *vn1 = (VariableName *)ptr1;
368  VariableName *vn2 = (VariableName *)ptr2;
369 
370  return (vn1->id == vn2->id && vn1->type == vn2->type);
371 }
372 
373 static void VariableNameFree(void *data)
374 {
375  VariableName *vn = (VariableName *)data;
376  if (vn == NULL)
377  return;
378  if (vn->name != NULL) {
379  SCFree(vn->name);
380  vn->name = NULL;
381  }
382  SCFree(vn);
383 }
HashListTableGetListData
#define HashListTableGetListData(hb)
Definition: util-hashlist.h:56
util-hash-string.h
VARID_HASHSIZE
#define VARID_HASHSIZE
Definition: util-var-name.c:85
VarNameStore_::ids
HashListTable * ids
Definition: util-var-name.c:69
util-hashlist.h
VarNameStore_::max_id
uint32_t max_id
Definition: util-var-name.c:70
CreateIsoTimeString
void CreateIsoTimeString(const SCTime_t ts, char *str, size_t size)
Definition: util-time.c:210
VarNameStore_
Definition: util-var-name.c:67
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
SC_ATOMIC_SET
#define SC_ATOMIC_SET(name, val)
Set the value for the atomic variable.
Definition: util-atomic.h:386
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:282
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
name
const char * name
Definition: detect-engine-proto.c:48
type
uint8_t type
Definition: decode-sctp.h:0
VarNameStore_::free_after
SCTime_t free_after
Definition: util-var-name.c:71
VarNameStoreInit
void VarNameStoreInit(void)
HashListTableGetListHead
HashListTableBucket * HashListTableGetListHead(HashListTable *ht)
Definition: util-hashlist.c:287
SCMutexLock
#define SCMutexLock(mut)
Definition: threads-debug.h:117
util-var-name.h
VarNameStoreSetupLookup
const char * VarNameStoreSetupLookup(const uint32_t id, const enum VarTypes type)
Definition: util-var-name.c:192
SCMUTEX_INITIALIZER
#define SCMUTEX_INITIALIZER
Definition: threads-debug.h:122
TAILQ_INSERT_TAIL
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:294
VarNameStoreRegister
uint32_t VarNameStoreRegister(const char *name, const enum VarTypes type)
Definition: util-var-name.c:156
HashListTableLookup
void * HashListTableLookup(HashListTable *ht, void *data, uint16_t datalen)
Definition: util-hashlist.c:245
VarNameStore_::names
HashListTable * names
Definition: util-var-name.c:68
VariableName_::ref_cnt
uint32_t ref_cnt
Definition: util-var-name.c:81
HashListTableAdd
int HashListTableAdd(HashListTable *ht, void *data, uint16_t datalen)
Definition: util-hashlist.c:114
TAILQ_ENTRY
#define TAILQ_ENTRY(type)
Definition: queue.h:239
HashListTableGetListNext
#define HashListTableGetListNext(hb)
Definition: util-hashlist.h:55
TAILQ_HEAD_INITIALIZER
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:236
VarNameStoreLookupByName
uint32_t VarNameStoreLookupByName(const char *name, const enum VarTypes type)
find name for id+type at packet time. As the active store won't be modified, we don't need locks.
Definition: util-var-name.c:327
HashListTableInit
HashListTable * HashListTableInit(uint32_t size, uint32_t(*Hash)(struct HashListTable_ *, void *, uint16_t), char(*Compare)(void *, uint16_t, void *, uint16_t), void(*Free)(void *))
Definition: util-hashlist.c:35
TAILQ_REMOVE
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:312
TAILQ_FIRST
#define TAILQ_FIRST(head)
Definition: queue.h:250
VariableName_::name
char * name
Definition: util-var-name.c:78
SCMutexUnlock
#define SCMutexUnlock(mut)
Definition: threads-debug.h:120
detect.h
VarNameStoreUnregister
void VarNameStoreUnregister(const uint32_t id, const enum VarTypes type)
Definition: util-var-name.c:205
VariableName
struct VariableName_ VariableName
Name2idx mapping structure for flowbits, flowvars and pktvars.
VarNameStore
struct VarNameStore_ VarNameStore
VariableName_::id
uint32_t id
Definition: util-var-name.c:80
SCLogWarning
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition: util-debug.h:262
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:325
SC_ATOMIC_DECLARE
#define SC_ATOMIC_DECLARE(type, name)
wrapper for declaring atomic variables.
Definition: util-atomic.h:280
VarNameStoreDestroy
void VarNameStoreDestroy(void)
Definition: util-var-name.c:116
SCTime_t
Definition: util-time.h:40
HashListTable_
Definition: util-hashlist.h:37
suricata-common.h
VarNameStoreLookupById
const char * VarNameStoreLookupById(const uint32_t id, const enum VarTypes type)
find name for id+type at packet time. As the active store won't be modified, we don't need locks.
Definition: util-var-name.c:307
VariableName_
Name2idx mapping structure for flowbits, flowvars and pktvars.
Definition: util-var-name.c:77
HashListTableFree
void HashListTableFree(HashListTable *ht)
Definition: util-hashlist.c:88
SCStrdup
#define SCStrdup(s)
Definition: util-mem.h:56
FatalError
#define FatalError(...)
Definition: util-debug.h:517
VariableName_::type
enum VarTypes type
Definition: util-var-name.c:79
util-validate.h
SCFree
#define SCFree(p)
Definition: util-mem.h:61
HashListTableBucket_
Definition: util-hashlist.h:28
SC_ATOMIC_INITPTR
#define SC_ATOMIC_INITPTR(name)
Definition: util-atomic.h:317
VarNameStorePtr
VarNameStore * VarNameStorePtr
Definition: util-var-name.c:74
TAILQ_HEAD
#define TAILQ_HEAD(name, type)
Definition: queue.h:230
VarTypes
VarTypes
Definition: util-var.h:28
likely
#define likely(expr)
Definition: util-optimize.h:32
SC_ATOMIC_GET
#define SC_ATOMIC_GET(name)
Get the value from the atomic variable.
Definition: util-atomic.h:375
VARNAME_HASHSIZE
#define VARNAME_HASHSIZE
Definition: util-var-name.c:84
SCTIME_ADD_SECS
#define SCTIME_ADD_SECS(ts, s)
Definition: util-time.h:64
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SCMutex
#define SCMutex
Definition: threads-debug.h:114
DEBUG_VALIDATE_BUG_ON
#define DEBUG_VALIDATE_BUG_ON(exp)
Definition: util-validate.h:109
SCTIME_CMP_LTE
#define SCTIME_CMP_LTE(a, b)
Definition: util-time.h:106
VarNameStoreActivate
int VarNameStoreActivate(void)
Definition: util-var-name.c:222
StringHashDjb2
uint32_t StringHashDjb2(const uint8_t *data, uint32_t datalen)
Definition: util-hash-string.c:22