]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
var-names: reimplement var name handling
authorVictor Julien <vjulien@oisf.net>
Wed, 2 Aug 2023 06:37:45 +0000 (08:37 +0200)
committerVictor Julien <vjulien@oisf.net>
Thu, 7 Sep 2023 19:15:38 +0000 (21:15 +0200)
Implement a new design for handling var name id's. The old logic
was aware of detection engine versions and generally didn't work
well for multi-tenancy cases. Other than memory leaks and crashes,
logging of var names worked or failed based on which tenant was
loaded last.

This patch implements a new approach, where there is a global store
of vars and their id's for the lifetime of the program.

Overall Design:

Base Store: "base"

Used during keyword registration. Operates under lock. Base is shared
between all detect engines, detect engine versions and tenants.
Each variable name is ref counted.

During the freeing of a detect engine / tenant, unregistration decreases
the ref cnt.

Base has both a string to id and a id to string hash table. String to
id is used during parsing/registration. id to string during unregistration.

Active Store Pointer (atomic)

The "active" store atomic pointer points to the active lookup store. The call
to `VarNameStoreActivate` will build a new lookup store and hot swap
the pointer.

Ensuring memory safety. During the hot swap, the pointer is replaced, so
any new call to the lookup functions will automatically use the new store.
This leaves the case of any lookup happening concurrently with the pointer
swap. For this case we add the old store to a free list. It gets a timestamp
before which it cannot be freed.

Free List

The free list contains old stores that are waiting to get removed. They
contain a timestamp that is checked before they are freed.

Bug: #6044.
Bug: #6201.
(cherry picked from commit b130234b2639842619da4c156ce5164a652202ec)

16 files changed:
src/detect-engine-build.c
src/detect-engine.c
src/detect-flowbits.c
src/detect-flowint.c
src/detect-flowvar.c
src/detect-flowvar.h
src/detect-hostbits.c
src/detect-lua.c
src/detect-pcre.c
src/detect-pktvar.c
src/detect-xbits.c
src/suricata.c
src/util-error.c
src/util-error.h
src/util-var-name.c
src/util-var-name.h

index 3c1d9acde3812a69f9f60e58ce7ef62947028dc1..df9e75a7e6ae6e2b136c8832e2c8d3e19a96c3cb 100644 (file)
@@ -1967,7 +1967,7 @@ int SigGroupBuild(DetectEngineCtx *de_ctx)
     ThresholdHashAllocate(de_ctx);
 
     if (!DetectEngineMultiTenantEnabled()) {
-        VarNameStoreActivateStaging();
+        VarNameStoreActivate();
     }
     return 0;
 }
index 8360041ec1016a3fb497734fe139f77ded55ed9c..e3319d79aa86162b70a02fa4e0e9bf459df2851a 100644 (file)
@@ -2039,7 +2039,6 @@ static DetectEngineCtx *DetectEngineCtxInitReal(enum DetectEngineType type, cons
     }
 
     de_ctx->version = DetectEngineGetVersion();
-    VarNameStoreSetupStaging(de_ctx->version);
     SCLogDebug("dectx with version %u", de_ctx->version);
     return de_ctx;
 error:
@@ -2170,8 +2169,6 @@ void DetectEngineCtxFree(DetectEngineCtx *de_ctx)
     DetectPortCleanupList(de_ctx, de_ctx->udp_whitelist);
 
     DetectBufferTypeFreeDetectEngine(de_ctx);
-    /* freed our var name hash */
-    VarNameStoreFree(de_ctx->version);
 
     SCFree(de_ctx);
     //DetectAddressGroupPrintMemory();
@@ -3768,7 +3765,7 @@ int DetectEngineMultiTenantSetup(void)
             goto error;
         }
 
-        VarNameStoreActivateStaging();
+        VarNameStoreActivate();
 
     } else {
         SCLogDebug("multi-detect not enabled (multi tenancy)");
index f1805b084d0004e474dc2e4febe7d38ad0071e51..871b0e18762a8d4b0a7496e059241235cfc7e1bb 100644 (file)
@@ -109,7 +109,7 @@ static int FlowbitOrAddData(DetectEngineCtx *de_ctx, DetectFlowbitsData *cd, cha
     if (unlikely(cd->or_list == NULL))
         return -1;
     for (uint8_t j = 0; j < cd->or_list_size ; j++) {
-        cd->or_list[j] = VarNameStoreSetupAdd(strarr[j], VAR_TYPE_FLOW_BIT);
+        cd->or_list[j] = VarNameStoreRegister(strarr[j], VAR_TYPE_FLOW_BIT);
         de_ctx->max_fb_id = MAX(cd->or_list[j], de_ctx->max_fb_id);
     }
 
@@ -310,7 +310,7 @@ static int DetectFlowbitParse(
         }
         cd->cmd = cmd;
     } else {
-        cd->idx = VarNameStoreSetupAdd(name, VAR_TYPE_FLOW_BIT);
+        cd->idx = VarNameStoreRegister(name, VAR_TYPE_FLOW_BIT);
         de_ctx->max_fb_id = MAX(cd->idx, de_ctx->max_fb_id);
         cd->cmd = cmd;
         cd->or_list_size = 0;
@@ -386,8 +386,13 @@ void DetectFlowbitFree (DetectEngineCtx *de_ctx, void *ptr)
     DetectFlowbitsData *fd = (DetectFlowbitsData *)ptr;
     if (fd == NULL)
         return;
-    if (fd->or_list != NULL)
+    VarNameStoreUnregister(fd->idx, VAR_TYPE_FLOW_BIT);
+    if (fd->or_list != NULL) {
+        for (uint8_t i = 0; i < fd->or_list_size; i++) {
+            VarNameStoreUnregister(fd->or_list[i], VAR_TYPE_FLOW_BIT);
+        }
         SCFree(fd->or_list);
+    }
     SCFree(fd);
 }
 
@@ -590,7 +595,7 @@ int DetectFlowbitsAnalyze(DetectEngineCtx *de_ctx)
 
     /* walk array to see if all bits make sense */
     for (uint32_t i = 0; i < array_size; i++) {
-        char *varname = VarNameStoreSetupLookup(i, VAR_TYPE_FLOW_BIT);
+        const char *varname = VarNameStoreSetupLookup(i, VAR_TYPE_FLOW_BIT);
         if (varname == NULL)
             continue;
 
@@ -643,7 +648,6 @@ int DetectFlowbitsAnalyze(DetectEngineCtx *de_ctx)
                         "stateful rules that set flowbit %s", s->id, varname);
             }
         }
-        SCFree(varname);
     }
 
     if (rule_engine_analysis_set) {
@@ -673,7 +677,7 @@ static void DetectFlowbitsAnalyzeDump(const DetectEngineCtx *de_ctx,
 
     jb_open_array(js, "flowbits");
     for (uint32_t x = 0; x < elements; x++) {
-        char *varname = VarNameStoreSetupLookup(x, VAR_TYPE_FLOW_BIT);
+        const char *varname = VarNameStoreSetupLookup(x, VAR_TYPE_FLOW_BIT);
         if (varname == NULL)
             continue;
 
@@ -733,7 +737,6 @@ static void DetectFlowbitsAnalyzeDump(const DetectEngineCtx *de_ctx,
             }
             jb_close(js);
         }
-        SCFree(varname);
         jb_close(js);
     }
     jb_close(js); // array
@@ -911,8 +914,8 @@ static int FlowBitsTestSig04(void)
     s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"isset option\"; flowbits:isset,fbt; content:\"GET \"; sid:1;)");
     FAIL_IF_NULL(s);
 
-    idx = VarNameStoreSetupAdd("fbt", VAR_TYPE_FLOW_BIT);
-    FAIL_IF(idx != 1);
+    idx = VarNameStoreRegister("fbt", VAR_TYPE_FLOW_BIT);
+    FAIL_IF(idx == 0);
 
     SigGroupBuild(de_ctx);
     DetectEngineCtxFree(de_ctx);
@@ -994,7 +997,7 @@ static int FlowBitsTestSig06(void)
     s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit set\"; flowbits:set,myflow; sid:10;)");
     FAIL_IF_NULL(s);
 
-    idx = VarNameStoreSetupAdd("myflow", VAR_TYPE_FLOW_BIT);
+    idx = VarNameStoreRegister("myflow", VAR_TYPE_FLOW_BIT);
     SigGroupBuild(de_ctx);
     DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
 
@@ -1068,7 +1071,7 @@ static int FlowBitsTestSig07(void)
     s = s->next = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit unset\"; flowbits:unset,myflow2; sid:11;)");
     FAIL_IF_NULL(s);
 
-    idx = VarNameStoreSetupAdd("myflow", VAR_TYPE_FLOW_BIT);
+    idx = VarNameStoreRegister("myflow", VAR_TYPE_FLOW_BIT);
     SigGroupBuild(de_ctx);
     DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
 
@@ -1144,7 +1147,7 @@ static int FlowBitsTestSig08(void)
     s = s->next  = SigInit(de_ctx,"alert ip any any -> any any (msg:\"Flowbit unset\"; flowbits:toggle,myflow2; sid:11;)");
     FAIL_IF_NULL(s);
 
-    idx = VarNameStoreSetupAdd("myflow", VAR_TYPE_FLOW_BIT);
+    idx = VarNameStoreRegister("myflow", VAR_TYPE_FLOW_BIT);
     SigGroupBuild(de_ctx);
     DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
 
index db97ee9f49d73c139359cb1e329510133bf4d34f..9c3fc9aa35822b24029551367eb185f3c9903a65 100644 (file)
@@ -329,7 +329,7 @@ static DetectFlowintData *DetectFlowintParse(DetectEngineCtx *de_ctx, const char
         SCLogError(SC_ERR_MEM_ALLOC, "malloc from strdup failed");
         goto error;
     }
-    sfd->idx = VarNameStoreSetupAdd(varname, VAR_TYPE_FLOW_INT);
+    sfd->idx = VarNameStoreRegister(varname, VAR_TYPE_FLOW_INT);
     SCLogDebug("sfd->name %s id %u", sfd->name, sfd->idx);
     sfd->modifier = modifier;
 
@@ -416,6 +416,7 @@ void DetectFlowintFree(DetectEngineCtx *de_ctx, void *tmp)
 {
     DetectFlowintData *sfd =(DetectFlowintData*) tmp;
     if (sfd != NULL) {
+        VarNameStoreUnregister(sfd->idx, VAR_TYPE_FLOW_INT);
         if (sfd->name != NULL)
             SCFree(sfd->name);
         if (sfd->targettype == FLOWINT_TARGET_VAR)
index 8db8a14cd1fecf8c18be4a67b5166fc952210755..92e7f0b01819a0fed682d97fb8bf5045d9eb95e3 100644 (file)
@@ -78,6 +78,9 @@ static void DetectFlowvarDataFree(DetectEngineCtx *de_ctx, void *ptr)
         SCReturn;
 
     DetectFlowvarData *fd = (DetectFlowvarData *)ptr;
+    /* leave unregistration to pcre keyword */
+    if (!fd->post_match)
+        VarNameStoreUnregister(fd->idx, VAR_TYPE_FLOW_VAR);
 
     if (fd->name)
         SCFree(fd->name);
@@ -172,7 +175,7 @@ static int DetectFlowvarSetup (DetectEngineCtx *de_ctx, Signature *s, const char
     fd->name = SCStrdup(varname);
     if (unlikely(fd->name == NULL))
         goto error;
-    fd->idx = VarNameStoreSetupAdd(varname, VAR_TYPE_FLOW_VAR);
+    fd->idx = VarNameStoreRegister(varname, VAR_TYPE_FLOW_VAR);
 
     /* Okay so far so good, lets get this into a SigMatch
      * and put it in the Signature. */
@@ -267,6 +270,7 @@ int DetectFlowvarPostMatchSetup(DetectEngineCtx *de_ctx, Signature *s, uint32_t
 
     /* we only need the idx */
     fv->idx = idx;
+    fv->post_match = true;
 
     sm = SigMatchAlloc();
     if (unlikely(sm == NULL))
index f9af869216ac3463c8625dc9b0d35368522aeac0..b2988a63517aadb5641180cf373b749d179ae25a 100644 (file)
@@ -28,8 +28,10 @@ typedef struct DetectFlowvarData_ {
     char *name;
     uint32_t idx;
     uint8_t *content;
-    uint8_t content_len;
-    uint8_t flags;
+    uint16_t content_len;
+    /** set to true if used in a post-match */
+    bool post_match;
+    uint32_t flags;
 } DetectFlowvarData;
 
 /* prototypes */
index 8db5c10760038127851a8ae4edcaa92fe6516d27..e826c11a1e9290bbd1d008d3165214cacd99bb8f 100644 (file)
@@ -387,7 +387,7 @@ int DetectHostbitSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawst
     if (unlikely(cd == NULL))
         goto error;
 
-    cd->idx = VarNameStoreSetupAdd(fb_name, VAR_TYPE_HOST_BIT);
+    cd->idx = VarNameStoreRegister(fb_name, VAR_TYPE_HOST_BIT);
     cd->cmd = fb_cmd;
     cd->tracker = hb_dir;
     cd->type = VAR_TYPE_HOST_BIT;
@@ -443,6 +443,7 @@ void DetectHostbitFree (DetectEngineCtx *de_ctx, void *ptr)
 
     if (fd == NULL)
         return;
+    VarNameStoreUnregister(fd->idx, VAR_TYPE_HOST_BIT);
 
     SCFree(fd);
 }
@@ -760,8 +761,8 @@ static int HostBitsTestSig04(void)
     s = de_ctx->sig_list = SigInit(de_ctx,"alert ip any any -> any any (msg:\"isset option\"; hostbits:isset,fbt; content:\"GET \"; sid:1;)");
     FAIL_IF_NULL(s);
 
-    idx = VarNameStoreSetupAdd("fbt", VAR_TYPE_HOST_BIT);
-    FAIL_IF(idx != 1);
+    idx = VarNameStoreRegister("fbt", VAR_TYPE_HOST_BIT);
+    FAIL_IF(idx == 0);
 
     SigGroupBuild(de_ctx);
     DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
index f7087b4580a17da630ea9a743fe0113f333d34b6..75b548a28334c192f589392101b10e4acecef278 100644 (file)
@@ -810,7 +810,7 @@ static int DetectLuaSetupPrime(DetectEngineCtx *de_ctx, DetectLuaData *ld)
                         goto error;
                     }
 
-                    uint32_t idx = VarNameStoreSetupAdd((char *)value, VAR_TYPE_FLOW_VAR);
+                    uint32_t idx = VarNameStoreRegister(value, VAR_TYPE_FLOW_VAR);
                     ld->flowvar[ld->flowvars++] = idx;
                     SCLogDebug("script uses flowvar %u with script id %u", idx, ld->flowvars - 1);
                 }
@@ -832,7 +832,7 @@ static int DetectLuaSetupPrime(DetectEngineCtx *de_ctx, DetectLuaData *ld)
                         goto error;
                     }
 
-                    uint32_t idx = VarNameStoreSetupAdd((char *)value, VAR_TYPE_FLOW_INT);
+                    uint32_t idx = VarNameStoreRegister(value, VAR_TYPE_FLOW_INT);
                     ld->flowint[ld->flowints++] = idx;
                     SCLogDebug("script uses flowint %u with script id %u", idx, ld->flowints - 1);
                 }
@@ -1156,6 +1156,13 @@ static void DetectLuaFree(DetectEngineCtx *de_ctx, void *ptr)
         if (lua->filename)
             SCFree(lua->filename);
 
+        for (uint16_t i = 0; i < lua->flowints; i++) {
+            VarNameStoreUnregister(lua->flowint[i], VAR_TYPE_FLOW_INT);
+        }
+        for (uint16_t i = 0; i < lua->flowvars; i++) {
+            VarNameStoreUnregister(lua->flowvar[i], VAR_TYPE_FLOW_VAR);
+        }
+
         DetectUnregisterThreadCtxFuncs(de_ctx, NULL, lua, "lua");
 
         SCFree(lua);
@@ -1299,11 +1306,11 @@ static int LuaMatchTest01(void)
         goto end;
     }
 
-    FlowVar *fv = FlowVarGet(&f, 1);
-    if (fv == NULL) {
-        printf("no flowvar: ");
-        goto end;
-    }
+    uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_VAR);
+    FAIL_IF(id == 0);
+
+    FlowVar *fv = FlowVarGet(&f, id);
+    FAIL_IF_NULL(fv);
 
     if (fv->data.fv_str.value_len != 1) {
         printf("%u != %u: ", fv->data.fv_str.value_len, 1);
@@ -1460,11 +1467,11 @@ static int LuaMatchTest01a(void)
         goto end;
     }
 
-    FlowVar *fv = FlowVarGet(&f, 1);
-    if (fv == NULL) {
-        printf("no flowvar: ");
-        goto end;
-    }
+    uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_VAR);
+    FAIL_IF(id == 0);
+
+    FlowVar *fv = FlowVarGet(&f, id);
+    FAIL_IF_NULL(fv);
 
     if (fv->data.fv_str.value_len != 1) {
         printf("%u != %u: ", fv->data.fv_str.value_len, 1);
@@ -1595,11 +1602,11 @@ static int LuaMatchTest02(void)
         goto end;
     }
 
-    FlowVar *fv = FlowVarGet(&f, 1);
-    if (fv == NULL) {
-        printf("no flowvar: ");
-        goto end;
-    }
+    uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_VAR);
+    FAIL_IF(id == 0);
+
+    FlowVar *fv = FlowVarGet(&f, id);
+    FAIL_IF_NULL(fv);
 
     if (fv->data.fv_str.value_len != 1) {
         printf("%u != %u: ", fv->data.fv_str.value_len, 1);
@@ -1728,11 +1735,11 @@ static int LuaMatchTest02a(void)
         goto end;
     }
 
-    FlowVar *fv = FlowVarGet(&f, 1);
-    if (fv == NULL) {
-        printf("no flowvar: ");
-        goto end;
-    }
+    uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_VAR);
+    FAIL_IF(id == 0);
+
+    FlowVar *fv = FlowVarGet(&f, id);
+    FAIL_IF_NULL(fv);
 
     if (fv->data.fv_str.value_len != 1) {
         printf("%u != %u: ", fv->data.fv_str.value_len, 1);
@@ -1861,11 +1868,11 @@ static int LuaMatchTest03(void)
         goto end;
     }
 
-    FlowVar *fv = FlowVarGet(&f, 1);
-    if (fv == NULL) {
-        printf("no flowvar: ");
-        goto end;
-    }
+    uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_VAR);
+    FAIL_IF(id == 0);
+
+    FlowVar *fv = FlowVarGet(&f, id);
+    FAIL_IF_NULL(fv);
 
     if (fv->data.fv_str.value_len != 1) {
         printf("%u != %u: ", fv->data.fv_str.value_len, 1);
@@ -1994,11 +2001,11 @@ static int LuaMatchTest03a(void)
         goto end;
     }
 
-    FlowVar *fv = FlowVarGet(&f, 1);
-    if (fv == NULL) {
-        printf("no flowvar: ");
-        goto end;
-    }
+    uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_VAR);
+    FAIL_IF(id == 0);
+
+    FlowVar *fv = FlowVarGet(&f, id);
+    FAIL_IF_NULL(fv);
 
     if (fv->data.fv_str.value_len != 1) {
         printf("%u != %u: ", fv->data.fv_str.value_len, 1);
@@ -2152,11 +2159,11 @@ static int LuaMatchTest04(void)
         goto end;
     }
 
-    FlowVar *fv = FlowVarGet(&f, 1);
-    if (fv == NULL) {
-        printf("no flowvar: ");
-        goto end;
-    }
+    uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_INT);
+    FAIL_IF(id == 0);
+
+    FlowVar *fv = FlowVarGet(&f, id);
+    FAIL_IF_NULL(fv);
 
     if (fv->data.fv_int.value != 2) {
         printf("%u != %u: ", fv->data.fv_int.value, 2);
@@ -2307,11 +2314,11 @@ static int LuaMatchTest04a(void)
         goto end;
     }
 
-    FlowVar *fv = FlowVarGet(&f, 1);
-    if (fv == NULL) {
-        printf("no flowvar: ");
-        goto end;
-    }
+    uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_INT);
+    FAIL_IF(id == 0);
+
+    FlowVar *fv = FlowVarGet(&f, id);
+    FAIL_IF_NULL(fv);
 
     if (fv->data.fv_int.value != 2) {
         printf("%u != %u: ", fv->data.fv_int.value, 2);
@@ -2455,11 +2462,11 @@ static int LuaMatchTest05(void)
         goto end;
     }
 
-    FlowVar *fv = FlowVarGet(&f, 1);
-    if (fv == NULL) {
-        printf("no flowvar: ");
-        goto end;
-    }
+    uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_INT);
+    FAIL_IF(id == 0);
+
+    FlowVar *fv = FlowVarGet(&f, id);
+    FAIL_IF_NULL(fv);
 
     if (fv->data.fv_int.value != 2) {
         printf("%u != %u: ", fv->data.fv_int.value, 2);
@@ -2604,11 +2611,11 @@ static int LuaMatchTest05a(void)
         goto end;
     }
 
-    FlowVar *fv = FlowVarGet(&f, 1);
-    if (fv == NULL) {
-        printf("no flowvar: ");
-        goto end;
-    }
+    uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_INT);
+    FAIL_IF(id == 0);
+
+    FlowVar *fv = FlowVarGet(&f, id);
+    FAIL_IF_NULL(fv);
 
     if (fv->data.fv_int.value != 2) {
         printf("%u != %u: ", fv->data.fv_int.value, 2);
@@ -2758,11 +2765,11 @@ static int LuaMatchTest06(void)
         goto end;
     }
 
-    FlowVar *fv = FlowVarGet(&f, 1);
-    if (fv == NULL) {
-        printf("no flowvar: ");
-        goto end;
-    }
+    uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_INT);
+    FAIL_IF(id == 0);
+
+    FlowVar *fv = FlowVarGet(&f, id);
+    FAIL_IF_NULL(fv);
 
     if (fv->data.fv_int.value != 0) {
         printf("%u != %u: ", fv->data.fv_int.value, 0);
@@ -2912,11 +2919,11 @@ static int LuaMatchTest06a(void)
         goto end;
     }
 
-    FlowVar *fv = FlowVarGet(&f, 1);
-    if (fv == NULL) {
-        printf("no flowvar: ");
-        goto end;
-    }
+    uint32_t id = VarNameStoreLookupByName("cnt", VAR_TYPE_FLOW_INT);
+    FAIL_IF(id == 0);
+
+    FlowVar *fv = FlowVarGet(&f, id);
+    FAIL_IF_NULL(fv);
 
     if (fv->data.fv_int.value != 0) {
         printf("%u != %u: ", fv->data.fv_int.value, 0);
index 0c7b1aa70a1205084fd33b1f4d0704f25948b278..af8447b3b22cce6d2216e62ee8441416d6ee4195 100644 (file)
@@ -749,12 +749,14 @@ static int DetectPcreParseCapture(const char *regexstr, DetectEngineCtx *de_ctx,
                 return -1;
 
             } else if (strncmp(name_array[name_idx], "flow:", 5) == 0) {
-                pd->capids[pd->idx] = VarNameStoreSetupAdd(name_array[name_idx]+5, VAR_TYPE_FLOW_VAR);
+                pd->capids[pd->idx] =
+                        VarNameStoreRegister(name_array[name_idx] + 5, VAR_TYPE_FLOW_VAR);
                 pd->captypes[pd->idx] = VAR_TYPE_FLOW_VAR;
                 pd->idx++;
 
             } else if (strncmp(name_array[name_idx], "pkt:", 4) == 0) {
-                pd->capids[pd->idx] = VarNameStoreSetupAdd(name_array[name_idx]+4, VAR_TYPE_PKT_VAR);
+                pd->capids[pd->idx] =
+                        VarNameStoreRegister(name_array[name_idx] + 4, VAR_TYPE_PKT_VAR);
                 pd->captypes[pd->idx] = VAR_TYPE_PKT_VAR;
                 SCLogDebug("id %u type %u", pd->capids[pd->idx], pd->captypes[pd->idx]);
                 pd->idx++;
@@ -813,12 +815,12 @@ static int DetectPcreParseCapture(const char *regexstr, DetectEngineCtx *de_ctx,
         }
 
         if (strcmp(type_str, "pkt") == 0) {
-            pd->capids[pd->idx] = VarNameStoreSetupAdd((char *)capture_str, VAR_TYPE_PKT_VAR);
+            pd->capids[pd->idx] = VarNameStoreRegister((char *)capture_str, VAR_TYPE_PKT_VAR);
             pd->captypes[pd->idx] = VAR_TYPE_PKT_VAR;
             SCLogDebug("id %u type %u", pd->capids[pd->idx], pd->captypes[pd->idx]);
             pd->idx++;
         } else if (strcmp(type_str, "flow") == 0) {
-            pd->capids[pd->idx] = VarNameStoreSetupAdd((char *)capture_str, VAR_TYPE_FLOW_VAR);
+            pd->capids[pd->idx] = VarNameStoreRegister((char *)capture_str, VAR_TYPE_FLOW_VAR);
             pd->captypes[pd->idx] = VAR_TYPE_FLOW_VAR;
             pd->idx++;
         }
@@ -971,6 +973,11 @@ static void DetectPcreFree(DetectEngineCtx *de_ctx, void *ptr)
 
     DetectPcreData *pd = (DetectPcreData *)ptr;
     DetectParseFreeRegex(&pd->parse_regex);
+    DetectUnregisterThreadCtxFuncs(de_ctx, NULL, pd, "pcre");
+
+    for (uint8_t i = 0; i < pd->idx; i++) {
+        VarNameStoreUnregister(pd->capids[i], pd->captypes[i]);
+    }
     SCFree(pd);
 
     return;
@@ -1059,7 +1066,7 @@ static int DetectPcreParseTest04 (void)
     FAIL_IF_NULL(pd);
     FAIL_IF_NOT(alproto == ALPROTO_UNKNOWN);
 
-    DetectPcreFree(NULL, pd);
+    DetectPcreFree(de_ctx, pd);
     DetectEngineCtxFree(de_ctx);
     return result;
 }
@@ -1081,7 +1088,7 @@ static int DetectPcreParseTest05 (void)
     FAIL_IF_NULL(pd);
     FAIL_IF_NOT(alproto == ALPROTO_UNKNOWN);
 
-    DetectPcreFree(NULL, pd);
+    DetectPcreFree(de_ctx, pd);
     DetectEngineCtxFree(de_ctx);
     return result;
 }
@@ -1103,7 +1110,7 @@ static int DetectPcreParseTest06 (void)
     FAIL_IF_NULL(pd);
     FAIL_IF_NOT(alproto == ALPROTO_UNKNOWN);
 
-    DetectPcreFree(NULL, pd);
+    DetectPcreFree(de_ctx, pd);
     DetectEngineCtxFree(de_ctx);
     return result;
 }
@@ -1125,7 +1132,7 @@ static int DetectPcreParseTest07 (void)
     FAIL_IF_NULL(pd);
     FAIL_IF_NOT(alproto == ALPROTO_HTTP);
 
-    DetectPcreFree(NULL, pd);
+    DetectPcreFree(de_ctx, pd);
     DetectEngineCtxFree(de_ctx);
     return result;
 }
@@ -1147,7 +1154,7 @@ static int DetectPcreParseTest08 (void)
     FAIL_IF_NULL(pd);
     FAIL_IF_NOT(alproto == ALPROTO_UNKNOWN);
 
-    DetectPcreFree(NULL, pd);
+    DetectPcreFree(de_ctx, pd);
     DetectEngineCtxFree(de_ctx);
     return result;
 }
@@ -1168,7 +1175,7 @@ static int DetectPcreParseTest09 (void)
     pd = DetectPcreParse(de_ctx, teststring, &list, NULL, 0, false, &alproto);
     FAIL_IF_NULL(pd);
 
-    DetectPcreFree(NULL, pd);
+    DetectPcreFree(de_ctx, pd);
     DetectEngineCtxFree(de_ctx);
     PASS;
 }
@@ -3515,7 +3522,7 @@ static int DetectPcreParseHttpHost(void)
 
     DetectPcreData *pd = DetectPcreParse(de_ctx, "/domain\\.com/W", &list, NULL, 0, false, &alproto);
     FAIL_IF(pd == NULL);
-    DetectPcreFree(NULL, pd);
+    DetectPcreFree(de_ctx, pd);
 
     list = DETECT_SM_LIST_NOTSET;
     pd = DetectPcreParse(de_ctx, "/dOmain\\.com/W", &list, NULL, 0, false, &alproto);
@@ -3525,7 +3532,7 @@ static int DetectPcreParseHttpHost(void)
     list = DETECT_SM_LIST_NOTSET;
     pd = DetectPcreParse(de_ctx, "/domain\\D+\\.com/W", &list, NULL, 0, false, &alproto);
     FAIL_IF(pd == NULL);
-    DetectPcreFree(NULL, pd);
+    DetectPcreFree(de_ctx, pd);
 
     /* This should not parse as the first \ escapes the second \, then
      * we have a D. */
@@ -3562,10 +3569,11 @@ static int DetectPcreParseCaptureTest(void)
 
     SigGroupBuild(de_ctx);
 
-    uint32_t capid = VarNameStoreLookupByName("somecapture", VAR_TYPE_FLOW_VAR);
-    FAIL_IF (capid != 1);
-    capid = VarNameStoreLookupByName("anothercap", VAR_TYPE_PKT_VAR);
-    FAIL_IF (capid != 2);
+    uint32_t capid1 = VarNameStoreLookupByName("somecapture", VAR_TYPE_FLOW_VAR);
+    FAIL_IF(capid1 == 0);
+    uint32_t capid2 = VarNameStoreLookupByName("anothercap", VAR_TYPE_PKT_VAR);
+    FAIL_IF(capid2 == 0);
+    FAIL_IF(capid1 == capid2);
 
     DetectEngineCtxFree(de_ctx);
     PASS;
index 9715f2770f42fee960f754a0f512d115af02beb5..045cebeeb348322834b66383cb1b38209ff5769a 100644 (file)
@@ -80,6 +80,7 @@ static void DetectPktvarFree(DetectEngineCtx *de_ctx, void *ptr)
 {
     DetectPktvarData *data = ptr;
     if (data != NULL) {
+        VarNameStoreUnregister(data->id, VAR_TYPE_PKT_VAR);
         SCFree(data->content);
         SCFree(data);
     }
@@ -144,7 +145,7 @@ static int DetectPktvarSetup (DetectEngineCtx *de_ctx, Signature *s, const char
 
     cd->content = content;
     cd->content_len = len;
-    cd->id = VarNameStoreSetupAdd(varname, VAR_TYPE_PKT_VAR);
+    cd->id = VarNameStoreRegister(varname, VAR_TYPE_PKT_VAR);
     pcre_free(varname);
 
     /* Okay so far so good, lets get this into a SigMatch
index 4de72d708aac410a4b981a1264ab94de43ce3441..b8d6792396e458642a6759b36403a070fcc5c40a 100644 (file)
@@ -297,7 +297,7 @@ static int DetectXbitParse(DetectEngineCtx *de_ctx,
     if (unlikely(cd == NULL))
         return -1;
 
-    cd->idx = VarNameStoreSetupAdd(name, var_type);
+    cd->idx = VarNameStoreRegister(name, var_type);
     cd->cmd = cmd;
     cd->tracker = track;
     cd->type = var_type;
@@ -363,6 +363,7 @@ static void DetectXbitFree (DetectEngineCtx *de_ctx, void *ptr)
 
     if (fd == NULL)
         return;
+    VarNameStoreUnregister(fd->idx, fd->type);
 
     SCFree(fd);
 }
index 6ebf24ba293fcda8316af05772677028abe4d092..45d9d5d742b9ea18fc4e764f42631107fc650177 100644 (file)
@@ -47,6 +47,9 @@
 
 #include "util-atomic.h"
 #include "util-spm.h"
+#ifdef BUILD_HYPERSCAN
+#include "util-mpm-hs.h"
+#endif
 #include "util-cpu.h"
 #include "util-action.h"
 #include "util-pidfile.h"
 #include "tmqh-packetpool.h"
 
 #include "util-proto-name.h"
-#include "util-mpm-hs.h"
-#include "util-storage.h"
-#include "host-storage.h"
+#include "util-running-modes.h"
+#include "util-signal.h"
+#include "util-time.h"
+#include "util-validate.h"
+#include "util-var-name.h"
 
 #include "util-lua.h"
 
@@ -442,6 +447,8 @@ static void GlobalsDestroy(SCInstance *suri)
     SCPidfileRemove(suri->pid_filename);
     SCFree(suri->pid_filename);
     suri->pid_filename = NULL;
+
+    VarNameStoreDestroy();
 }
 
 /** \brief make sure threads can stop the engine by calling this
@@ -2821,6 +2828,7 @@ int InitGlobal(void) {
     /* Initialize the configuration module. */
     ConfInit();
 
+    VarNameStoreInit();
     return 0;
 }
 
index b39e05a907eee99b04e8faa38a7cd500c8b432e4..975e12ae9eef0560b4b406c344eee00c089416e3 100644 (file)
@@ -380,6 +380,7 @@ const char * SCErrorToString(SCError err)
         CASE_CODE(SC_WARN_CHOWN);
         CASE_CODE(SC_ERR_HASH_ADD);
         CASE_CODE(SC_ERR_SIGNAL);
+        CASE_CODE(SC_WARN_REFCNT);
 
         CASE_CODE (SC_ERR_MAX);
     }
index 3387c74ae83a576836b93396a6351650c44d6c65..398aee9eebe46a41c61957522fdb83f242298505 100644 (file)
@@ -370,6 +370,7 @@ typedef enum {
     SC_WARN_CHOWN,
     SC_ERR_HASH_ADD,
     SC_ERR_SIGNAL,
+    SC_WARN_REFCNT,
 
     SC_ERR_MAX
 } SCError;
index 3cc1231026a7073fd980ec6545b10552daeb37d9..93fbb74554c57d2a26b4bbac1a68079a9140e7db 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2007-2016 Open Information Security Foundation
+/* Copyright (C) 2007-2023 Open Information Security Foundation
  *
  * You can copy, redistribute or modify this Program under the terms of
  * the GNU General Public License version 2 as published by the Free
 
 #include "suricata-common.h"
 #include "detect.h"
+#include "util-hash-string.h"
 #include "util-hashlist.h"
 #include "util-var-name.h"
+#include "util-validate.h"
 
-/* the way this can be used w/o locking lookups:
- * - Lookups use only g_varnamestore_current which is read only
- * - Detection setups a new ctx in staging, which will include the 'current'
- *   entries keeping ID's stable
- * - Detection hot swaps staging into current after a new detect engine was
- *   created. Current remains available through 'old'.
- * - When detect reload is complete (threads are all moved over), 'old' can
- *   be freed.
+/* Overall Design:
+ *
+ * Base Store: "base"
+ *
+ * Used during keyword registration. Operates under lock. Base is shared
+ * between all detect engines, detect engine versions and tenants.
+ * Each variable name is ref counted.
+ *
+ * During the freeing of a detect engine / tenant, unregistration decreases
+ * the ref cnt.
+ *
+ * Base has both a string to id and a id to string hash table. String to
+ * id is used during parsing/registration. id to string during unregistration.
+ *
+ *
+ * Active Store Pointer (atomic)
+ *
+ * The "active" store atomic pointer points to the active store. The call
+ * to `VarNameStoreActivate` will build a new lookup store and hot swap
+ * the pointer.
+ *
+ * Ensuring memory safety. During the hot swap, the pointer is replaced, so
+ * any new call to the lookup functions will automatically use the new store.
+ * This leaves the case of any lookup happening concurrently with the pointer
+ * swap. For this case we add the old store to a free list. It gets a timestamp
+ * before which it cannot be freed.
+ *
+ *
+ * Free List
+ *
+ * The free list contains old stores that are waiting to get removed. They
+ * contain a timestamp that is checked before they are freed.
+ *
  */
-
 typedef struct VarNameStore_ {
     HashListTable *names;
     HashListTable *ids;
     uint32_t max_id;
-    uint32_t de_ctx_version;    /**< de_ctx version 'owning' this */
+    struct timeval free_after;
+    TAILQ_ENTRY(VarNameStore_) next;
 } VarNameStore;
-
-static int initialized = 0;
-/* currently VarNameStore that is READ ONLY. This way lookups can
- * be done w/o locking or synchronization */
-SC_ATOMIC_DECLARE(VarNameStore *, g_varnamestore_current);
-
-/* old VarNameStore on the way out */
-static VarNameStore *g_varnamestore_old = NULL;
-
-/* new VarNameStore that is being prepared. Multiple DetectLoader threads
- * may be updating it so a lock is used for synchronization. */
-static VarNameStore *g_varnamestore_staging = NULL;
-static SCMutex g_varnamestore_staging_m = SCMUTEX_INITIALIZER;
+typedef VarNameStore *VarNameStorePtr;
 
 /** \brief Name2idx mapping structure for flowbits, flowvars and pktvars. */
 typedef struct VariableName_ {
     char *name;
-    uint8_t type; /* flowbit, pktvar, etc */
-    uint32_t idx;
+    enum VarTypes type; /* flowbit, pktvar, etc */
+    uint32_t id;
+    uint32_t ref_cnt;
 } VariableName;
 
 #define VARNAME_HASHSIZE 0x1000
 #define VARID_HASHSIZE 0x1000
 
-static uint32_t VariableNameHash(HashListTable *ht, void *buf, uint16_t buflen)
-{
-     VariableName *fn = (VariableName *)buf;
-     uint32_t hash = strlen(fn->name) + fn->type;
-     uint16_t u;
-
-     for (u = 0; u < buflen; u++) {
-         hash += fn->name[u];
-     }
+static SCMutex base_lock = SCMUTEX_INITIALIZER;
+static VarNameStore base = { .names = NULL, .ids = NULL, .max_id = 0 };
+static TAILQ_HEAD(, VarNameStore_) free_list = TAILQ_HEAD_INITIALIZER(free_list);
+static SC_ATOMIC_DECLARE(VarNameStorePtr, active);
 
-     return (hash % VARNAME_HASHSIZE);
-}
+static uint32_t VariableNameHash(HashListTable *ht, void *buf, uint16_t buflen);
+static char VariableNameCompare(void *buf1, uint16_t len1, void *buf2, uint16_t len2);
+static uint32_t VariableIdHash(HashListTable *ht, void *ptr, uint16_t _unused);
+static char VariableIdCompare(void *ptr1, uint16_t _unused1, void *ptr2, uint16_t _unused2);
+static void VariableNameFree(void *data);
 
-static char VariableNameCompare(void *buf1, uint16_t len1, void *buf2, uint16_t len2)
+void VarNameStoreInit(void)
 {
-    VariableName *fn1 = (VariableName *)buf1;
-    VariableName *fn2 = (VariableName *)buf2;
-
-    if (fn1->type != fn2->type)
-        return 0;
-
-    if (strcmp(fn1->name,fn2->name) == 0)
-        return 1;
-
-    return 0;
-}
-
-static uint32_t VariableIdxHash(HashListTable *ht, void *buf, uint16_t buflen)
-{
-    VariableName *fn = (VariableName *)buf;
-    uint32_t hash = fn->idx + fn->type;
-    return (hash % VARID_HASHSIZE);
-}
-
-static char VariableIdxCompare(void *buf1, uint16_t len1, void *buf2, uint16_t len2)
-{
-    VariableName *fn1 = (VariableName *)buf1;
-    VariableName *fn2 = (VariableName *)buf2;
-
-    if (fn1->type != fn2->type)
-        return 0;
-
-    if (fn1->idx == fn2->idx)
-        return 1;
-
-    return 0;
-}
-
-static void VariableNameFree(void *data)
-{
-    VariableName *fn = (VariableName *)data;
-
-    if (fn == NULL)
-        return;
-
-    if (fn->name != NULL) {
-        SCFree(fn->name);
-        fn->name = NULL;
+    SCMutexLock(&base_lock);
+    base.names = HashListTableInit(
+            VARNAME_HASHSIZE, VariableNameHash, VariableNameCompare, VariableNameFree);
+    if (base.names == NULL) {
+        FatalError(SC_ERR_HASH_TABLE_INIT, "failed to initialize variable name hash (names)");
     }
 
-    SCFree(fn);
+    /* base.names owns the allocation, so use a NULL Free pointer here */
+    base.ids = HashListTableInit(VARID_HASHSIZE, VariableIdHash, VariableIdCompare, NULL);
+    if (base.ids == NULL) {
+        FatalError(SC_ERR_HASH_TABLE_INIT, "failed to initialize variable name hash (names)");
+    }
+    SC_ATOMIC_INITPTR(active);
+    SCMutexUnlock(&base_lock);
 }
 
-/** \brief Initialize the Name idx hash.
- */
-static VarNameStore *VarNameStoreInit(void)
+void VarNameStoreDestroy(void)
 {
-    VarNameStore *v = SCCalloc(1, sizeof(*v));
-    if (v == NULL)
-        return NULL;
-
-    v->names = HashListTableInit(VARNAME_HASHSIZE, VariableNameHash, VariableNameCompare, VariableNameFree);
-    if (v->names == NULL) {
-        SCFree(v);
-        return NULL;
+    SCMutexLock(&base_lock);
+    VarNameStore *s = SC_ATOMIC_GET(active);
+    if (s) {
+        HashListTableFree(s->names);
+        HashListTableFree(s->ids);
+        SCFree(s);
+        s = NULL;
     }
+    SC_ATOMIC_SET(active, NULL);
 
-    v->ids = HashListTableInit(VARID_HASHSIZE, VariableIdxHash, VariableIdxCompare, NULL);
-    if (v->ids == NULL) {
-        HashListTableFree(v->names);
-        SCFree(v);
-        return NULL;
+    while ((s = TAILQ_FIRST(&free_list))) {
+        TAILQ_REMOVE(&free_list, s, next);
+        HashListTableFree(s->names);
+        HashListTableFree(s->ids);
+        SCFree(s);
     }
 
-    v->max_id = 0;
-    return v;
-}
-
-static void VarNameStoreDoFree(VarNameStore *v)
-{
-    if (v) {
-        HashListTableFree(v->names);
-        HashListTableFree(v->ids);
-        SCFree(v);
+    for (HashListTableBucket *b = HashListTableGetListHead(base.names); b != NULL;
+            b = HashListTableGetListNext(b)) {
+        VariableName *vn = HashListTableGetListData(b);
+        DEBUG_VALIDATE_BUG_ON(vn->ref_cnt > 0);
+        if (vn->ref_cnt > 0) {
+            SCLogWarning(SC_WARN_REFCNT, "%s (type %u, id %u) still has ref_cnt %u", vn->name,
+                    vn->type, vn->id, vn->ref_cnt);
+        }
     }
+    HashListTableFree(base.ids);
+    base.ids = NULL;
+    HashListTableFree(base.names);
+    base.names = NULL;
+    base.max_id = 0;
+    SCMutexUnlock(&base_lock);
 }
 
-
-/** \brief Get a name idx for a name. If the name is already used reuse the idx.
- *  \param name nul terminated string with the name
- *  \param type variable type
- *  \retval 0 in case of error
- *  \retval idx the idx or 0
+/**
+ *  \retval id or 0 on error
  */
-static uint32_t VariableNameGetIdx(VarNameStore *v, const char *name, enum VarTypes type)
+uint32_t VarNameStoreRegister(const char *name, const enum VarTypes type)
 {
-    uint32_t idx = 0;
-
-    VariableName *fn = SCMalloc(sizeof(VariableName));
-    if (unlikely(fn == NULL))
-        goto error;
+    SCMutexLock(&base_lock);
+    uint32_t id = 0;
 
-    memset(fn, 0, sizeof(VariableName));
-
-    fn->type = type;
-    fn->name = SCStrdup(name);
-    if (fn->name == NULL)
-        goto error;
-
-    VariableName *lookup_fn = (VariableName *)HashListTableLookup(v->names, (void *)fn, 0);
-    if (lookup_fn == NULL) {
-        v->max_id++;
-
-        idx = fn->idx = v->max_id;
-        HashListTableAdd(v->names, (void *)fn, 0);
-        HashListTableAdd(v->ids, (void *)fn, 0);
-        SCLogDebug("new registration %s id %u type %u", fn->name, fn->idx, fn->type);
+    SCLogDebug("registering: name %s type %u", name, type);
+    VariableName lookup = { .type = type, .name = (char *)name };
+    VariableName *found = (VariableName *)HashListTableLookup(base.names, (void *)&lookup, 0);
+    if (found == NULL) {
+        VariableName *vn = SCCalloc(1, sizeof(VariableName));
+        if (likely(vn != NULL)) {
+            vn->type = type;
+            vn->name = SCStrdup(name);
+            if (vn->name != NULL) {
+                vn->ref_cnt = 1;
+                id = vn->id = ++base.max_id;
+                HashListTableAdd(base.names, (void *)vn, 0);
+                HashListTableAdd(base.ids, (void *)vn, 0);
+                SCLogDebug(
+                        "new registration %s id %u type %u -> %u", vn->name, vn->id, vn->type, id);
+            } else {
+                SCFree(vn);
+            }
+        }
     } else {
-        idx = lookup_fn->idx;
-        VariableNameFree(fn);
+        id = found->id;
+        found->ref_cnt++;
+        SCLogDebug("existing registration %s ref_cnt %u -> %u", name, found->ref_cnt, id);
     }
-
-    return idx;
-error:
-    VariableNameFree(fn);
-    return 0;
+    SCMutexUnlock(&base_lock);
+    return id;
 }
 
-/** \brief Get a name from the idx.
- *  \param idx index of the variable whose name is to be fetched
- *  \param type variable type
- *  \retval NULL in case of error
- *  \retval name of the variable if successful.
- *  \todo no alloc on lookup
- */
-static char *VariableIdxGetName(VarNameStore *v, uint32_t idx, enum VarTypes type)
+const char *VarNameStoreSetupLookup(const uint32_t id, const enum VarTypes type)
 {
-    VariableName *fn = SCMalloc(sizeof(VariableName));
-    if (unlikely(fn == NULL))
-        goto error;
-
-    char *name = NULL;
-    memset(fn, 0, sizeof(VariableName));
-
-    fn->type = type;
-    fn->idx = idx;
-
-    VariableName *lookup_fn = (VariableName *)HashListTableLookup(v->ids, (void *)fn, 0);
-    if (lookup_fn != NULL) {
-        name = SCStrdup(lookup_fn->name);
-        if (unlikely(name == NULL))
-            goto error;
-
-        VariableNameFree(fn);
-    } else {
-        goto error;
+    const char *name = NULL;
+    SCMutexLock(&base_lock);
+    VariableName lookup = { .type = type, .id = id };
+    VariableName *found = (VariableName *)HashListTableLookup(base.ids, (void *)&lookup, 0);
+    if (found) {
+        name = found->name;
     }
-
+    SCMutexUnlock(&base_lock);
     return name;
-error:
-    VariableNameFree(fn);
-    return NULL;
 }
 
-/** \brief setup staging store. Include current store if there is one.
- */
-int VarNameStoreSetupStaging(uint32_t de_ctx_version)
+void VarNameStoreUnregister(const uint32_t id, const enum VarTypes type)
 {
-    SCMutexLock(&g_varnamestore_staging_m);
-
-    if (!initialized) {
-        SC_ATOMIC_INITPTR(g_varnamestore_current);
-        initialized = 1;
+    SCMutexLock(&base_lock);
+    VariableName lookup = { .type = type, .id = id };
+    VariableName *found = (VariableName *)HashListTableLookup(base.ids, (void *)&lookup, 0);
+    if (found) {
+        SCLogDebug("found %s ref_cnt %u", found->name, found->ref_cnt);
+        DEBUG_VALIDATE_BUG_ON(found->ref_cnt == 0);
+        found->ref_cnt--;
     }
+    SCMutexUnlock(&base_lock);
+}
 
-    if (g_varnamestore_staging != NULL &&
-        g_varnamestore_staging->de_ctx_version == de_ctx_version) {
-        SCMutexUnlock(&g_varnamestore_staging_m);
-        return 0;
-    }
+int VarNameStoreActivate(void)
+{
+    int result = 0;
+    SCMutexLock(&base_lock);
+    SCLogDebug("activating new lookup store");
+
+    VarNameStore *new_active = NULL;
+
+    // create lookup hash for id to string, strings should point to base
+    for (HashListTableBucket *b = HashListTableGetListHead(base.names); b != NULL;
+            b = HashListTableGetListNext(b)) {
+        VariableName *vn = HashListTableGetListData(b);
+        BUG_ON(vn == NULL);
+        SCLogDebug("base: %s/%u/%u", vn->name, vn->id, vn->ref_cnt);
+        if (vn->ref_cnt == 0)
+            continue;
+
+        if (new_active == NULL) {
+            new_active = SCCalloc(1, sizeof(*new_active));
+            if (new_active == NULL) {
+                result = -1;
+                goto out;
+            }
+
+            new_active->names = HashListTableInit(
+                    VARNAME_HASHSIZE, VariableNameHash, VariableNameCompare, NULL);
+            if (new_active->names == NULL) {
+                SCFree(new_active);
+                result = -1;
+                goto out;
+            }
+            new_active->ids =
+                    HashListTableInit(VARID_HASHSIZE, VariableIdHash, VariableIdCompare, NULL);
+            if (new_active->ids == NULL) {
+                HashListTableFree(new_active->names);
+                SCFree(new_active);
+                result = -1;
+                goto out;
+            }
+        }
 
-    VarNameStore *nv = VarNameStoreInit();
-    if (nv == NULL) {
-        SCMutexUnlock(&g_varnamestore_staging_m);
-        return -1;
+        /* memory is still owned by "base" */
+        HashListTableAdd(new_active->names, (void *)vn, 0);
+        HashListTableAdd(new_active->ids, (void *)vn, 0);
     }
-    g_varnamestore_staging = nv;
-    nv->de_ctx_version = de_ctx_version;
 
-    VarNameStore *current = SC_ATOMIC_GET(g_varnamestore_current);
-    if (current) {
-        /* add all entries from the current hash into this new one. */
-        HashListTableBucket *b = HashListTableGetListHead(current->names);
-        while (b) {
-            VariableName *var = HashListTableGetListData(b);
-
-            VariableName *newvar = SCCalloc(1, sizeof(*newvar));
-            BUG_ON(newvar == NULL);
-            memcpy(newvar, var, sizeof(*newvar));
-            newvar->name = SCStrdup(var->name);
-            BUG_ON(newvar->name == NULL);
-
-            HashListTableAdd(nv->names, (void *)newvar, 0);
-            HashListTableAdd(nv->ids, (void *)newvar, 0);
-            nv->max_id = MAX(nv->max_id, newvar->idx);
-            SCLogDebug("xfer %s id %u type %u", newvar->name, newvar->idx, newvar->type);
-
-            b = HashListTableGetListNext(b);
+    if (new_active) {
+        VarNameStore *old_active = SC_ATOMIC_GET(active);
+        if (old_active) {
+            struct timeval ts, add;
+            memset(&ts, 0, sizeof(ts));
+            memset(&add, 0, sizeof(add));
+            gettimeofday(&ts, NULL);
+            add.tv_sec = 60;
+            timeradd(&ts, &add, &ts);
+            old_active->free_after = ts;
+
+            TAILQ_INSERT_TAIL(&free_list, old_active, next);
+            SCLogDebug("old active is stored in free list");
         }
-    }
 
-    SCLogDebug("set up staging with detect engine ver %u", nv->de_ctx_version);
-    SCMutexUnlock(&g_varnamestore_staging_m);
-    return 0;
+        SC_ATOMIC_SET(active, new_active);
+        SCLogDebug("new store active");
+
+        struct timeval now;
+        memset(&now, 0, sizeof(now));
+        gettimeofday(&now, NULL);
+
+        VarNameStore *s = NULL;
+        while ((s = TAILQ_FIRST(&free_list))) {
+            char timebuf[64];
+            CreateIsoTimeString(&s->free_after, timebuf, sizeof(timebuf));
+
+            if (!timercmp(&now, &s->free_after, >)) {
+                SCLogDebug("not yet freeing store %p before %s", s, timebuf);
+                break;
+            }
+            SCLogDebug("freeing store %p with time %s", s, timebuf);
+            TAILQ_REMOVE(&free_list, s, next);
+            HashListTableFree(s->names);
+            HashListTableFree(s->ids);
+            SCFree(s);
+        }
+    }
+out:
+    SCLogDebug("activating new lookup store: complete %d", result);
+    SCMutexUnlock(&base_lock);
+    return result;
 }
 
+/** \brief find name for id+type at packet time. */
 const char *VarNameStoreLookupById(const uint32_t id, const enum VarTypes type)
 {
-    VarNameStore *current = SC_ATOMIC_GET(g_varnamestore_current);
-    BUG_ON(current == NULL);
-    VariableName lookup = { NULL, type, id };
-    VariableName *found = (VariableName *)HashListTableLookup(current->ids, (void *)&lookup, 0);
-    if (found == NULL) {
-        return NULL;
+    const char *name = NULL;
+
+    const VarNameStore *current = SC_ATOMIC_GET(active);
+    if (current) {
+        VariableName lookup = { .type = type, .id = id };
+        const VariableName *found = HashListTableLookup(current->ids, (void *)&lookup, 0);
+        if (found) {
+            return found->name;
+        }
     }
-    return found->name;
+
+    return name;
 }
 
+/** \brief find name for id+type at packet time. */
 uint32_t VarNameStoreLookupByName(const char *name, const enum VarTypes type)
 {
-    VarNameStore *current = SC_ATOMIC_GET(g_varnamestore_current);
-    BUG_ON(current == NULL);
-    VariableName lookup = { (char *)name, type, 0 };
-    VariableName *found = (VariableName *)HashListTableLookup(current->names, (void *)&lookup, 0);
-    if (found == NULL) {
-        return 0;
+    const VarNameStore *current = SC_ATOMIC_GET(active);
+    if (current) {
+        VariableName lookup = { .name = (char *)name, .type = type };
+        const VariableName *found = HashListTableLookup(current->names, (void *)&lookup, 0);
+        if (found) {
+            return found->id;
+        }
     }
-    SCLogDebug("found %u for %s type %u", found->idx, name, type);
-    return found->idx;
-}
 
-/** \brief add to staging or return existing id if already in there */
-uint32_t VarNameStoreSetupAdd(const char *name, const enum VarTypes type)
-{
-    uint32_t id;
-    SCMutexLock(&g_varnamestore_staging_m);
-    id = VariableNameGetIdx(g_varnamestore_staging, name, type);
-    SCMutexUnlock(&g_varnamestore_staging_m);
-    return id;
+    return 0;
 }
 
-char *VarNameStoreSetupLookup(uint32_t idx, const enum VarTypes type)
+static uint32_t VariableNameHash(HashListTable *ht, void *buf, uint16_t buflen)
 {
-    SCMutexLock(&g_varnamestore_staging_m);
-    char *name = VariableIdxGetName(g_varnamestore_staging, idx, type);
-    SCMutexUnlock(&g_varnamestore_staging_m);
-    return name;
+    VariableName *vn = (VariableName *)buf;
+    uint32_t hash = StringHashDjb2((uint8_t *)vn->name, strlen(vn->name)) + vn->type;
+    return (hash % VARNAME_HASHSIZE);
 }
 
-void VarNameStoreActivateStaging(void)
+static char VariableNameCompare(void *buf1, uint16_t len1, void *buf2, uint16_t len2)
 {
-    SCMutexLock(&g_varnamestore_staging_m);
-    if (g_varnamestore_old) {
-        VarNameStoreDoFree(g_varnamestore_old);
-        g_varnamestore_old = NULL;
-    }
-    g_varnamestore_old = SC_ATOMIC_GET(g_varnamestore_current);
-    SC_ATOMIC_SET(g_varnamestore_current, g_varnamestore_staging);
-    g_varnamestore_staging = NULL;
-    SCMutexUnlock(&g_varnamestore_staging_m);
+    VariableName *vn1 = (VariableName *)buf1;
+    VariableName *vn2 = (VariableName *)buf2;
+    return (vn1->type == vn2->type && strcmp(vn1->name, vn2->name) == 0);
 }
 
-void VarNameStoreFreeOld(void)
+static uint32_t VariableIdHash(HashListTable *ht, void *ptr, uint16_t _unused)
 {
-    SCMutexLock(&g_varnamestore_staging_m);
-    SCLogDebug("freeing g_varnamestore_old %p", g_varnamestore_old);
-    if (g_varnamestore_old) {
-        VarNameStoreDoFree(g_varnamestore_old);
-        g_varnamestore_old = NULL;
-    }
-    SCMutexUnlock(&g_varnamestore_staging_m);
+    VariableName *vn = (VariableName *)ptr;
+    uint32_t hash = vn->id << vn->type;
+    return (hash % VARID_HASHSIZE);
 }
 
-void VarNameStoreFree(uint32_t de_ctx_version)
+static char VariableIdCompare(void *ptr1, uint16_t _unused1, void *ptr2, uint16_t _unused2)
 {
-    SCLogDebug("freeing detect engine version %u", de_ctx_version);
-    SCMutexLock(&g_varnamestore_staging_m);
-    if (g_varnamestore_old && g_varnamestore_old->de_ctx_version == de_ctx_version) {
-        VarNameStoreDoFree(g_varnamestore_old);
-        g_varnamestore_old = NULL;
-        SCLogDebug("freeing detect engine version %u: old done", de_ctx_version);
-    }
+    VariableName *vn1 = (VariableName *)ptr1;
+    VariableName *vn2 = (VariableName *)ptr2;
 
-    /* if at this point we have a staging area which matches our version
-     * we didn't complete the setup and are cleaning up the mess. */
-    if (g_varnamestore_staging && g_varnamestore_staging->de_ctx_version == de_ctx_version) {
-        VarNameStoreDoFree(g_varnamestore_staging);
-        g_varnamestore_staging = NULL;
-        SCLogDebug("freeing detect engine version %u: staging done", de_ctx_version);
-    }
+    return (vn1->id == vn2->id && vn1->type == vn2->type);
+}
 
-    VarNameStore *current = SC_ATOMIC_GET(g_varnamestore_current);
-    if (current && current->de_ctx_version == de_ctx_version) {
-        VarNameStoreDoFree(current);
-        SC_ATOMIC_SET(g_varnamestore_current, NULL);
-        SCLogDebug("freeing detect engine version %u: current done", de_ctx_version);
+static void VariableNameFree(void *data)
+{
+    VariableName *vn = (VariableName *)data;
+    if (vn == NULL)
+        return;
+    if (vn->name != NULL) {
+        SCFree(vn->name);
+        vn->name = NULL;
     }
-    SCMutexUnlock(&g_varnamestore_staging_m);
+    SCFree(vn);
 }
index 5b0b18d716c78c9fcb7678ed276f3175facfacdf..5f21ea3fa4ca3cbd1a027be4b6018caafbc1afec 100644 (file)
 #ifndef __UTIL_VAR_NAME_H__
 #define __UTIL_VAR_NAME_H__
 
-int VarNameStoreSetupStaging(uint32_t de_ctx_version);
+void VarNameStoreInit(void);
+void VarNameStoreDestroy(void);
+
+uint32_t VarNameStoreRegister(const char *name, const enum VarTypes type);
+const char *VarNameStoreSetupLookup(const uint32_t id, const enum VarTypes type);
+void VarNameStoreUnregister(const uint32_t id, const enum VarTypes type);
+int VarNameStoreActivate(void);
+
 const char *VarNameStoreLookupById(const uint32_t id, const enum VarTypes type);
-uint32_t VarNameStoreLookupByName(const char *name, const enum VarTypes type);
-uint32_t VarNameStoreSetupAdd(const char *name, const enum VarTypes type);
-char *VarNameStoreSetupLookup(uint32_t idx, const enum VarTypes type);
-void VarNameStoreActivateStaging(void);
-void VarNameStoreFreeOld(void);
-void VarNameStoreFree(uint32_t de_ctx_version);
+uint32_t VarNameStoreLookupByName(const char *, const enum VarTypes type);
 
 #endif