From 57b67d044ae75c5e2f643fc7fedbec3c96d479fa Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Mon, 13 May 2024 16:05:19 +0530 Subject: [PATCH 1/5] util/thash: add a length getter fn In order to have access to the length of datatypes with variable lengths to correctly update memuse to calculate memcaps. Bug 3910 --- src/app-layer-htp-range.c | 2 +- src/datasets-string.c | 6 ++++++ src/datasets-string.h | 1 + src/datasets.c | 13 +++++++------ src/util-thash.c | 6 ++++-- src/util-thash.h | 7 ++++--- 6 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/app-layer-htp-range.c b/src/app-layer-htp-range.c index 3cdde35ba288..b4d9a1fd4f09 100644 --- a/src/app-layer-htp-range.c +++ b/src/app-layer-htp-range.c @@ -174,7 +174,7 @@ void HttpRangeContainersInit(void) ContainerUrlRangeList.ht = THashInit("app-layer.protocols.http.byterange", sizeof(HttpRangeContainerFile), ContainerUrlRangeSet, ContainerUrlRangeFree, ContainerUrlRangeHash, - ContainerUrlRangeCompare, false, memcap, CONTAINER_URLRANGE_HASH_SIZE); + ContainerUrlRangeCompare, NULL, false, memcap, CONTAINER_URLRANGE_HASH_SIZE); ContainerUrlRangeList.timeout = timeout; SCLogDebug("containers started"); diff --git a/src/datasets-string.c b/src/datasets-string.c index 4a572898ceb3..91e44bfb2a9b 100644 --- a/src/datasets-string.c +++ b/src/datasets-string.c @@ -98,6 +98,12 @@ uint32_t StringHash(void *s) return hash; } +uint32_t StringGetLength(void *s) +{ + StringType *str = s; + return str->len; +} + // base data stays in hash void StringFree(void *s) { diff --git a/src/datasets-string.h b/src/datasets-string.h index b9c3c3002454..1d5463cd9c0a 100644 --- a/src/datasets-string.h +++ b/src/datasets-string.h @@ -35,6 +35,7 @@ typedef struct StringType { int StringSet(void *dst, void *src); bool StringCompare(void *a, void *b); uint32_t StringHash(void *s); +uint32_t StringGetLength(void *s); void StringFree(void *s); int StringAsBase64(const void *s, char *out, size_t out_size); diff --git a/src/datasets.c b/src/datasets.c index 01ef5bb47c90..a72304ccbcdf 100644 --- a/src/datasets.c +++ b/src/datasets.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2017-2020 Open Information Security Foundation +/* Copyright (C) 2017-2024 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 @@ -700,7 +700,7 @@ Dataset *DatasetGet(const char *name, enum DatasetTypes type, const char *save, switch (type) { case DATASET_TYPE_MD5: set->hash = THashInit(cnf_name, sizeof(Md5Type), Md5StrSet, Md5StrFree, Md5StrHash, - Md5StrCompare, load != NULL ? 1 : 0, memcap > 0 ? memcap : default_memcap, + Md5StrCompare, NULL, load != NULL ? 1 : 0, memcap > 0 ? memcap : default_memcap, hashsize > 0 ? hashsize : default_hashsize); if (set->hash == NULL) goto out_err; @@ -709,7 +709,8 @@ Dataset *DatasetGet(const char *name, enum DatasetTypes type, const char *save, break; case DATASET_TYPE_STRING: set->hash = THashInit(cnf_name, sizeof(StringType), StringSet, StringFree, StringHash, - StringCompare, load != NULL ? 1 : 0, memcap > 0 ? memcap : default_memcap, + StringCompare, StringGetLength, load != NULL ? 1 : 0, + memcap > 0 ? memcap : default_memcap, hashsize > 0 ? hashsize : default_hashsize); if (set->hash == NULL) goto out_err; @@ -718,7 +719,7 @@ Dataset *DatasetGet(const char *name, enum DatasetTypes type, const char *save, break; case DATASET_TYPE_SHA256: set->hash = THashInit(cnf_name, sizeof(Sha256Type), Sha256StrSet, Sha256StrFree, - Sha256StrHash, Sha256StrCompare, load != NULL ? 1 : 0, + Sha256StrHash, Sha256StrCompare, NULL, load != NULL ? 1 : 0, memcap > 0 ? memcap : default_memcap, hashsize > 0 ? hashsize : default_hashsize); if (set->hash == NULL) @@ -728,7 +729,7 @@ Dataset *DatasetGet(const char *name, enum DatasetTypes type, const char *save, break; case DATASET_TYPE_IPV4: set->hash = THashInit(cnf_name, sizeof(IPv4Type), IPv4Set, IPv4Free, IPv4Hash, - IPv4Compare, load != NULL ? 1 : 0, memcap > 0 ? memcap : default_memcap, + IPv4Compare, NULL, load != NULL ? 1 : 0, memcap > 0 ? memcap : default_memcap, hashsize > 0 ? hashsize : default_hashsize); if (set->hash == NULL) goto out_err; @@ -737,7 +738,7 @@ Dataset *DatasetGet(const char *name, enum DatasetTypes type, const char *save, break; case DATASET_TYPE_IPV6: set->hash = THashInit(cnf_name, sizeof(IPv6Type), IPv6Set, IPv6Free, IPv6Hash, - IPv6Compare, load != NULL ? 1 : 0, memcap > 0 ? memcap : default_memcap, + IPv6Compare, NULL, load != NULL ? 1 : 0, memcap > 0 ? memcap : default_memcap, hashsize > 0 ? hashsize : default_hashsize); if (set->hash == NULL) goto out_err; diff --git a/src/util-thash.c b/src/util-thash.c index 6443990bc219..728a8b60754a 100644 --- a/src/util-thash.c +++ b/src/util-thash.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2016 Open Information Security Foundation +/* Copyright (C) 2007-2024 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 @@ -294,7 +294,8 @@ static int THashInitConfig(THashTableContext *ctx, const char *cnf_prefix) THashTableContext *THashInit(const char *cnf_prefix, size_t data_size, int (*DataSet)(void *, void *), void (*DataFree)(void *), uint32_t (*DataHash)(void *), - bool (*DataCompare)(void *, void *), bool reset_memcap, uint64_t memcap, uint32_t hashsize) + bool (*DataCompare)(void *, void *), uint32_t (*DataSize)(void *), bool reset_memcap, + uint64_t memcap, uint32_t hashsize) { THashTableContext *ctx = SCCalloc(1, sizeof(*ctx)); BUG_ON(!ctx); @@ -304,6 +305,7 @@ THashTableContext *THashInit(const char *cnf_prefix, size_t data_size, ctx->config.DataFree = DataFree; ctx->config.DataHash = DataHash; ctx->config.DataCompare = DataCompare; + ctx->config.DataSize = DataSize; /* set defaults */ ctx->config.hash_rand = (uint32_t)RandomGet(); diff --git a/src/util-thash.h b/src/util-thash.h index f45b6e843167..fbfa981be4c6 100644 --- a/src/util-thash.h +++ b/src/util-thash.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2016 Open Information Security Foundation +/* Copyright (C) 2007-2024 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 @@ -132,6 +132,7 @@ typedef struct THashDataConfig_ { void (*DataFree)(void *); uint32_t (*DataHash)(void *); bool (*DataCompare)(void *, void *); + uint32_t (*DataSize)(void *); } THashConfig; #define THASH_DATA_SIZE(ctx) (sizeof(THashData) + (ctx)->config.data_size) @@ -169,8 +170,8 @@ typedef struct THashTableContext_ { THashTableContext *THashInit(const char *cnf_prefix, size_t data_size, int (*DataSet)(void *dst, void *src), void (*DataFree)(void *), - uint32_t (*DataHash)(void *), bool (*DataCompare)(void *, void *), bool reset_memcap, - uint64_t memcap, uint32_t hashsize); + uint32_t (*DataHash)(void *), bool (*DataCompare)(void *, void *), + uint32_t (*DataSize)(void *), bool reset_memcap, uint64_t memcap, uint32_t hashsize); void THashShutdown(THashTableContext *ctx); From 61bf26b0156844e96891403d54076c8f60590a0a Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Mon, 13 May 2024 16:07:26 +0530 Subject: [PATCH 2/5] datasets: fix memuse to include string len So far, when the data size was passed to the THash API, it was sent as a sizeof(Struct) which works fine for the other data types as they have a fixed length but not for the StringType. However, because of the sizeof construct, the length of a string type dataset was always taken to be 16 Bytes which is only the size of the struct itself. It did not accomodate the actual size of the string that the StringType holds. Fix this so that the memuse that is used to determine whether memcap was reached also takes into consideration the size of the actual string. Bug 3910 --- src/util-thash.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/util-thash.c b/src/util-thash.c index 728a8b60754a..45820e098a25 100644 --- a/src/util-thash.c +++ b/src/util-thash.c @@ -160,11 +160,9 @@ static uint32_t THashDataQueueLen(THashDataQueue *q) static THashData *THashDataAlloc(THashTableContext *ctx) { const size_t data_size = THASH_DATA_SIZE(ctx); - if (!(THASH_CHECK_MEMCAP(ctx, data_size))) { return NULL; } - (void) SC_ATOMIC_ADD(ctx->memuse, data_size); THashData *h = SCCalloc(1, data_size); @@ -173,8 +171,7 @@ static THashData *THashDataAlloc(THashTableContext *ctx) /* points to data right after THashData block */ h->data = (uint8_t *)h + sizeof(THashData); - -// memset(h, 0x00, data_size); + // memset(h, 0x00, data_size); SCMutexInit(&h->m, NULL); SC_ATOMIC_INIT(h->use_cnt); @@ -189,12 +186,16 @@ static void THashDataFree(THashTableContext *ctx, THashData *h) if (h != NULL) { DEBUG_VALIDATE_BUG_ON(SC_ATOMIC_GET(h->use_cnt) != 0); + uint32_t len = 0; if (h->data != NULL) { + if (ctx->config.DataSize) { + len = ctx->config.DataSize(h->data); + } ctx->config.DataFree(h->data); } SCMutexDestroy(&h->m); SCFree(h); - (void) SC_ATOMIC_SUB(ctx->memuse, THASH_DATA_SIZE(ctx)); + (void)SC_ATOMIC_SUB(ctx->memuse, THASH_DATA_SIZE(ctx) + (uint64_t)len); } } @@ -373,6 +374,8 @@ void THashShutdown(THashTableContext *ctx) } (void) SC_ATOMIC_SUB(ctx->memuse, ctx->config.hash_size * sizeof(THashHashRow)); THashDataQueueDestroy(&ctx->spare_q); + SCLogDebug("memuse: %ld", (uint64_t)SC_ATOMIC_GET(ctx->memuse)); + DEBUG_VALIDATE_BUG_ON(SC_ATOMIC_GET(ctx->memuse) != 0); SCFree(ctx); return; } @@ -518,7 +521,10 @@ static THashData *THashDataGetNew(THashTableContext *ctx, void *data) // setup the data BUG_ON(ctx->config.DataSet(h->data, data) != 0); - + if (ctx->config.DataSize) { + uint32_t len = ctx->config.DataSize(data); + (void)SC_ATOMIC_ADD(ctx->memuse, (uint64_t)len); + } (void) SC_ATOMIC_ADD(ctx->counter, 1); SCMutexLock(&h->m); return h; @@ -760,6 +766,10 @@ static THashData *THashGetUsed(THashTableContext *ctx) if (h->data != NULL) { ctx->config.DataFree(h->data); + if (ctx->config.DataSize) { + uint32_t len = ctx->config.DataSize(h->data); + (void)SC_ATOMIC_SUB(ctx->memuse, (uint64_t)len); + } } SCMutexUnlock(&h->m); From 818e0dc0fdd74ab389214ad07ba59507fcb5bb9c Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Thu, 9 May 2024 21:21:25 +0530 Subject: [PATCH 3/5] doc: add note about datasets string memcaps Bug 3910 --- doc/userguide/upgrade.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/userguide/upgrade.rst b/doc/userguide/upgrade.rst index 4af0ad1c727f..d5e00d2b4c82 100644 --- a/doc/userguide/upgrade.rst +++ b/doc/userguide/upgrade.rst @@ -57,6 +57,9 @@ Major changes Instead, both the SDP parser and logger depend on being invoked by another parser (or logger). - ARP decoder and logger have been introduced. Since ARP can be quite verbose and produce many events, the logger is disabled by default. +- Datasets of the type String had a bug fix which takes into account the length of the string + as well when checking for memcaps. This might lead to memcaps being hit on older deployments. + For more details, check https://redmine.openinfosecfoundation.org/issues/3910 Upgrading 6.0 to 7.0 -------------------- From 06e132fdfed0b5f6dc13cd14532f65670f27fba0 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Wed, 10 Jan 2024 09:49:10 +0100 Subject: [PATCH 4/5] thash: add expiration logic Add a callback and helper function to handle data expiration. Update datasets to explicitly not use expiration. --- src/app-layer-htp-range.c | 11 ++++---- src/datasets.c | 21 ++++++++------- src/util-thash.c | 57 +++++++++++++++++++++++++++++++++++++-- src/util-thash.h | 5 +++- 4 files changed, 77 insertions(+), 17 deletions(-) diff --git a/src/app-layer-htp-range.c b/src/app-layer-htp-range.c index b4d9a1fd4f09..25b0b66a4ce5 100644 --- a/src/app-layer-htp-range.c +++ b/src/app-layer-htp-range.c @@ -127,8 +127,9 @@ static void ContainerUrlRangeFree(void *s) } } -static inline bool ContainerValueRangeTimeout(HttpRangeContainerFile *cu, const SCTime_t ts) +static inline bool ContainerValueRangeTimeout(void *data, const SCTime_t ts) { + HttpRangeContainerFile *cu = data; // we only timeout if we have no flow referencing us if ((uint32_t)SCTIME_SECS(ts) > cu->expire || cu->error) { if (SC_ATOMIC_GET(cu->hdata->use_cnt) == 0) { @@ -171,10 +172,10 @@ void HttpRangeContainersInit(void) } } - ContainerUrlRangeList.ht = - THashInit("app-layer.protocols.http.byterange", sizeof(HttpRangeContainerFile), - ContainerUrlRangeSet, ContainerUrlRangeFree, ContainerUrlRangeHash, - ContainerUrlRangeCompare, NULL, false, memcap, CONTAINER_URLRANGE_HASH_SIZE); + ContainerUrlRangeList.ht = THashInit("app-layer.protocols.http.byterange", + sizeof(HttpRangeContainerFile), ContainerUrlRangeSet, ContainerUrlRangeFree, + ContainerUrlRangeHash, ContainerUrlRangeCompare, ContainerValueRangeTimeout, NULL, + false, memcap, CONTAINER_URLRANGE_HASH_SIZE); ContainerUrlRangeList.timeout = timeout; SCLogDebug("containers started"); diff --git a/src/datasets.c b/src/datasets.c index a72304ccbcdf..a1534f90c136 100644 --- a/src/datasets.c +++ b/src/datasets.c @@ -700,7 +700,8 @@ Dataset *DatasetGet(const char *name, enum DatasetTypes type, const char *save, switch (type) { case DATASET_TYPE_MD5: set->hash = THashInit(cnf_name, sizeof(Md5Type), Md5StrSet, Md5StrFree, Md5StrHash, - Md5StrCompare, NULL, load != NULL ? 1 : 0, memcap > 0 ? memcap : default_memcap, + Md5StrCompare, NULL, NULL, load != NULL ? 1 : 0, + memcap > 0 ? memcap : default_memcap, hashsize > 0 ? hashsize : default_hashsize); if (set->hash == NULL) goto out_err; @@ -709,7 +710,7 @@ Dataset *DatasetGet(const char *name, enum DatasetTypes type, const char *save, break; case DATASET_TYPE_STRING: set->hash = THashInit(cnf_name, sizeof(StringType), StringSet, StringFree, StringHash, - StringCompare, StringGetLength, load != NULL ? 1 : 0, + StringCompare, NULL, StringGetLength, load != NULL ? 1 : 0, memcap > 0 ? memcap : default_memcap, hashsize > 0 ? hashsize : default_hashsize); if (set->hash == NULL) @@ -719,7 +720,7 @@ Dataset *DatasetGet(const char *name, enum DatasetTypes type, const char *save, break; case DATASET_TYPE_SHA256: set->hash = THashInit(cnf_name, sizeof(Sha256Type), Sha256StrSet, Sha256StrFree, - Sha256StrHash, Sha256StrCompare, NULL, load != NULL ? 1 : 0, + Sha256StrHash, Sha256StrCompare, NULL, NULL, load != NULL ? 1 : 0, memcap > 0 ? memcap : default_memcap, hashsize > 0 ? hashsize : default_hashsize); if (set->hash == NULL) @@ -728,18 +729,20 @@ Dataset *DatasetGet(const char *name, enum DatasetTypes type, const char *save, goto out_err; break; case DATASET_TYPE_IPV4: - set->hash = THashInit(cnf_name, sizeof(IPv4Type), IPv4Set, IPv4Free, IPv4Hash, - IPv4Compare, NULL, load != NULL ? 1 : 0, memcap > 0 ? memcap : default_memcap, - hashsize > 0 ? hashsize : default_hashsize); + set->hash = + THashInit(cnf_name, sizeof(IPv4Type), IPv4Set, IPv4Free, IPv4Hash, IPv4Compare, + NULL, NULL, load != NULL ? 1 : 0, memcap > 0 ? memcap : default_memcap, + hashsize > 0 ? hashsize : default_hashsize); if (set->hash == NULL) goto out_err; if (DatasetLoadIPv4(set) < 0) goto out_err; break; case DATASET_TYPE_IPV6: - set->hash = THashInit(cnf_name, sizeof(IPv6Type), IPv6Set, IPv6Free, IPv6Hash, - IPv6Compare, NULL, load != NULL ? 1 : 0, memcap > 0 ? memcap : default_memcap, - hashsize > 0 ? hashsize : default_hashsize); + set->hash = + THashInit(cnf_name, sizeof(IPv6Type), IPv6Set, IPv6Free, IPv6Hash, IPv6Compare, + NULL, NULL, load != NULL ? 1 : 0, memcap > 0 ? memcap : default_memcap, + hashsize > 0 ? hashsize : default_hashsize); if (set->hash == NULL) goto out_err; if (DatasetLoadIPv6(set) < 0) diff --git a/src/util-thash.c b/src/util-thash.c index 45820e098a25..c1b2e168c955 100644 --- a/src/util-thash.c +++ b/src/util-thash.c @@ -295,8 +295,8 @@ static int THashInitConfig(THashTableContext *ctx, const char *cnf_prefix) THashTableContext *THashInit(const char *cnf_prefix, size_t data_size, int (*DataSet)(void *, void *), void (*DataFree)(void *), uint32_t (*DataHash)(void *), - bool (*DataCompare)(void *, void *), uint32_t (*DataSize)(void *), bool reset_memcap, - uint64_t memcap, uint32_t hashsize) + bool (*DataCompare)(void *, void *), bool (*DataExpired)(void *, SCTime_t), + uint32_t (*DataSize)(void *), bool reset_memcap, uint64_t memcap, uint32_t hashsize) { THashTableContext *ctx = SCCalloc(1, sizeof(*ctx)); BUG_ON(!ctx); @@ -306,6 +306,7 @@ THashTableContext *THashInit(const char *cnf_prefix, size_t data_size, ctx->config.DataFree = DataFree; ctx->config.DataHash = DataHash; ctx->config.DataCompare = DataCompare; + ctx->config.DataExpired = DataExpired; ctx->config.DataSize = DataSize; /* set defaults */ @@ -413,6 +414,58 @@ int THashWalk(THashTableContext *ctx, THashFormatFunc FormatterFunc, THashOutput return 0; } +/** \brief expire data from the hash + * Walk the hash table and remove data that is exprired according to the + * DataExpired callback. + * \retval cnt number of items successfully expired/removed + */ +uint32_t THashExpire(THashTableContext *ctx, const SCTime_t ts) +{ + if (ctx->config.DataExpired == NULL) + return 0; + + SCLogDebug("timeout: starting"); + uint32_t cnt = 0; + + for (uint32_t i = 0; i < ctx->config.hash_size; i++) { + THashHashRow *hb = &ctx->array[i]; + if (HRLOCK_TRYLOCK(hb) != 0) + continue; + /* hash bucket is now locked */ + THashData *h = hb->head; + while (h) { + THashData *next = h->next; + THashDataLock(h); + DEBUG_VALIDATE_BUG_ON(SC_ATOMIC_GET(h->use_cnt) > (uint32_t)INT_MAX); + /* only consider items with no references to it */ + if (SC_ATOMIC_GET(h->use_cnt) == 0 && ctx->config.DataExpired(h->data, ts)) { + /* remove from the hash */ + if (h->prev != NULL) + h->prev->next = h->next; + if (h->next != NULL) + h->next->prev = h->prev; + if (hb->head == h) + hb->head = h->next; + if (hb->tail == h) + hb->tail = h->prev; + h->next = NULL; + h->prev = NULL; + SCLogDebug("timeout: removing data %p", h); + ctx->config.DataFree(h->data); + THashDataUnlock(h); + THashDataMoveToSpare(ctx, h); + } else { + THashDataUnlock(h); + } + h = next; + } + HRLOCK_UNLOCK(hb); + } + + SCLogDebug("timeout: ending: %u entries expired", cnt); + return cnt; +} + /** \brief Cleanup the thash engine * * Cleanup the thash engine from tag and threshold. diff --git a/src/util-thash.h b/src/util-thash.h index fbfa981be4c6..346c528a292a 100644 --- a/src/util-thash.h +++ b/src/util-thash.h @@ -132,6 +132,7 @@ typedef struct THashDataConfig_ { void (*DataFree)(void *); uint32_t (*DataHash)(void *); bool (*DataCompare)(void *, void *); + bool (*DataExpired)(void *, SCTime_t ts); uint32_t (*DataSize)(void *); } THashConfig; @@ -171,7 +172,8 @@ typedef struct THashTableContext_ { THashTableContext *THashInit(const char *cnf_prefix, size_t data_size, int (*DataSet)(void *dst, void *src), void (*DataFree)(void *), uint32_t (*DataHash)(void *), bool (*DataCompare)(void *, void *), - uint32_t (*DataSize)(void *), bool reset_memcap, uint64_t memcap, uint32_t hashsize); + bool (*DataExpired)(void *, SCTime_t), uint32_t (*DataSize)(void *), bool reset_memcap, + uint64_t memcap, uint32_t hashsize); void THashShutdown(THashTableContext *ctx); @@ -198,5 +200,6 @@ int THashWalk(THashTableContext *, THashFormatFunc, THashOutputFunc, void *); int THashRemoveFromHash (THashTableContext *ctx, void *data); void THashConsolidateMemcap(THashTableContext *ctx); void THashDataMoveToSpare(THashTableContext *ctx, THashData *h); +uint32_t THashExpire(THashTableContext *ctx, const SCTime_t ts); #endif /* SURICATA_THASH_H */ From 03a00a1cfdd2203f2aba0c559406ea863711e5fe Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Tue, 21 May 2024 16:17:03 +0530 Subject: [PATCH 5/5] util/thash: update memuse during expiration --- src/util-thash.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util-thash.c b/src/util-thash.c index c1b2e168c955..1f60b02f5bfd 100644 --- a/src/util-thash.c +++ b/src/util-thash.c @@ -452,6 +452,10 @@ uint32_t THashExpire(THashTableContext *ctx, const SCTime_t ts) h->prev = NULL; SCLogDebug("timeout: removing data %p", h); ctx->config.DataFree(h->data); + if (ctx->config.DataSize) { + uint32_t len = ctx->config.DataSize(h->data); + (void)SC_ATOMIC_SUB(ctx->memuse, (uint64_t)len); + } THashDataUnlock(h); THashDataMoveToSpare(ctx, h); } else {