From 586aa6645e45a5d492496e35222e5b4108953ed7 Mon Sep 17 00:00:00 2001 From: Denis Balashov Date: Thu, 4 Jun 2026 12:30:39 +0300 Subject: [PATCH 1/8] util/log-redis: guard SCCalloc result for redis stream format When Redis output is configured in stream/xadd mode with a positive stream-maxlen, SCConfLogOpenRedis() allocates redis_setup.stream_format and immediately passes it to snprintf(). If SCCalloc() fails, snprintf() receives a NULL destination pointer and the process can crash during Redis output initialization. Handle this unrecoverable setup failure with FatalError(), matching the surrounding Redis initialization error handling. Ticket: 8588 --- src/util-log-redis.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/util-log-redis.c b/src/util-log-redis.c index f0755e7473eb..adc6b23f7e59 100644 --- a/src/util-log-redis.c +++ b/src/util-log-redis.c @@ -673,6 +673,9 @@ int SCConfLogOpenRedis(SCConfNode *redis_node, void *lf_ctx) format string, whose length is limited by the length of the maxlen integer formatted as a string */ log_ctx->redis_setup.stream_format = SCCalloc(100, sizeof(char)); + if (unlikely(log_ctx->redis_setup.stream_format == NULL)) { + FatalError("Unable to allocate redis stream format"); + } snprintf(log_ctx->redis_setup.stream_format, 100, redis_stream_format_maxlen_tmpl, "%s", "%s", exact ? '=' : '~', maxlen, "%s"); log_ctx->redis_setup.format = log_ctx->redis_setup.stream_format; From d8b5b4304c931d47734df8ba31acd58173a7000c Mon Sep 17 00:00:00 2001 From: Denis Balashov Date: Thu, 4 Jun 2026 12:30:53 +0300 Subject: [PATCH 2/8] detect/alert: guard SCStrdup result before use SCStrdup result was stored and immediately used without checking for NULL, which would cause a NULL dereference if allocation fails. --- src/detect-engine-alert.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/detect-engine-alert.c b/src/detect-engine-alert.c index 5bf150115f53..eb5373d59116 100644 --- a/src/detect-engine-alert.c +++ b/src/detect-engine-alert.c @@ -360,6 +360,9 @@ static inline int PacketAlertSetContext( } } current_json->json_string = SCStrdup(det_ctx->json_content[i].json_content); + if (current_json->json_string == NULL) { + return -1; + } SCLogDebug("json content %u, value '%s' (%p)", (unsigned int)i, current_json->json_string, s); } From f14b81cc426e63d802660ecf9868c36c058acc04 Mon Sep 17 00:00:00 2001 From: Denis Balashov Date: Thu, 4 Jun 2026 12:31:02 +0300 Subject: [PATCH 3/8] detect/flowbits: check SCRealloc result before overwriting pointer The original pointer was overwritten with the SCRealloc result before checking for NULL, causing a memory leak if reallocation fails. Check the temporary pointer first before assigning. --- src/detect-flowbits.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/detect-flowbits.c b/src/detect-flowbits.c index 6e7726020dd3..91cbc11b753e 100644 --- a/src/detect-flowbits.c +++ b/src/detect-flowbits.c @@ -793,11 +793,11 @@ int DetectFlowbitsAnalyze(DetectEngineCtx *de_ctx) uint32_t new_fb_array_size = s->init_data->rule_state_flowbits_ids_size + 1; void *tmp_fb_ptr = SCRealloc(s->init_data->rule_state_flowbits_ids_array, new_fb_array_size * sizeof(uint32_t)); - s->init_data->rule_state_flowbits_ids_array = tmp_fb_ptr; - if (s->init_data->rule_state_flowbits_ids_array == NULL) { + if (tmp_fb_ptr == NULL) { SCLogError("Failed to reallocate memory for rule_state_variable_idx"); goto error; } + s->init_data->rule_state_flowbits_ids_array = tmp_fb_ptr; SCLogDebug( "realloc'ed array for flowbits ids, new size is %u", new_fb_array_size); s->init_data->rule_state_dependant_sids_size = new_array_size; From 84b46f4b6ba70f8f989a3ffab5abf7e378aa078f Mon Sep 17 00:00:00 2001 From: Denis Balashov Date: Thu, 4 Jun 2026 12:31:09 +0300 Subject: [PATCH 4/8] detect/reference: guard SCStrdup calls in DetectReferenceParse Two SCStrdup calls that set ref->key had no NULL check. On allocation failure the pointer would be used immediately, causing a NULL dereference. --- src/detect-reference.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/detect-reference.c b/src/detect-reference.c index 2981f3e5b68c..700d149c8c21 100644 --- a/src/detect-reference.c +++ b/src/detect-reference.c @@ -153,6 +153,9 @@ static DetectReference *DetectReferenceParse(const char *rawstr, DetectEngineCtx if (strlen(scheme)) { SCLogConfig("scheme value %s overrides key %s", scheme, key); ref->key = SCStrdup(scheme); + if (ref->key == NULL) { + goto error; + } /* already bound checked to be REFERENCE_SYSTEM_NAME_MAX or less */ ref->key_len = (uint16_t)strlen(scheme); } else { @@ -160,6 +163,9 @@ static DetectReference *DetectReferenceParse(const char *rawstr, DetectEngineCtx SCRConfReference *lookup_ref_conf = SCRConfGetReference(key, de_ctx); if (lookup_ref_conf != NULL) { ref->key = SCStrdup(lookup_ref_conf->url); + if (ref->key == NULL) { + goto error; + } /* already bound checked to be REFERENCE_SYSTEM_NAME_MAX or less */ ref->key_len = (uint16_t)strlen(ref->key); } else { From 7c9db6221eb6f283020fe17774c7fcbe041366d0 Mon Sep 17 00:00:00 2001 From: Denis Balashov Date: Thu, 4 Jun 2026 16:35:43 +0300 Subject: [PATCH 5/8] util/mpm-hs: fix null check parentheses; simplify SCHSConfigInit Two fixes: - Remove extra parentheses in existing NULL check: (*ext) -> *ext, which was causing the cocci script to miss the check as a false negative. - Simplify SCHSConfigInit to return SCCalloc() directly; the caller in detect-engine.c already checks the return value for NULL. --- src/util-mpm-hs.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/util-mpm-hs.c b/src/util-mpm-hs.c index 2d4daba19992..3b77a4de44c5 100644 --- a/src/util-mpm-hs.c +++ b/src/util-mpm-hs.c @@ -641,7 +641,7 @@ static int CompileDataExtensionsInit(hs_expr_ext_t **ext, const SCHSPattern *p) { if (p->flags & (MPM_PATTERN_FLAG_OFFSET | MPM_PATTERN_FLAG_DEPTH)) { *ext = SCCalloc(1, sizeof(hs_expr_ext_t)); - if ((*ext) == NULL) { + if (*ext == NULL) { return -1; } if (p->flags & MPM_PATTERN_FLAG_OFFSET) { @@ -1178,8 +1178,7 @@ void SCHSPrintInfo(MpmCtx *mpm_ctx) static MpmConfig *SCHSConfigInit(void) { - MpmConfig *c = SCCalloc(1, sizeof(MpmConfig)); - return c; + return SCCalloc(1, sizeof(MpmConfig)); } static void SCHSConfigDeinit(MpmConfig **c) From ca1f8a09e899e0bcad9e2703f6023726cf885fcc Mon Sep 17 00:00:00 2001 From: Denis Balashov Date: Thu, 4 Jun 2026 12:33:03 +0300 Subject: [PATCH 6/8] tests/fuzz: guard SCCalloc result in fuzz_decodebase64 If SCCalloc fails, decoded is NULL and the subsequent SCBase64Decode call would dereference it. Return early on allocation failure. --- src/tests/fuzz/fuzz_decodebase64.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tests/fuzz/fuzz_decodebase64.c b/src/tests/fuzz/fuzz_decodebase64.c index 294b708f45d8..ea721e6c9768 100644 --- a/src/tests/fuzz/fuzz_decodebase64.c +++ b/src/tests/fuzz/fuzz_decodebase64.c @@ -19,6 +19,8 @@ static void Base64FuzzTest(const uint8_t *src, size_t len) { uint32_t decoded_len = SCBase64DecodeBufferSize((uint32_t)len); uint8_t *decoded = SCCalloc(decoded_len, sizeof(uint8_t)); + if (decoded == NULL) + return; for (uint8_t mode = SCBase64ModeRFC2045; mode <= SCBase64ModeStrict; mode++) { (void)SCBase64Decode(src, len, mode, decoded); From 72cca4ce5c50e651f1dc47e1c09fd6cd888e02ad Mon Sep 17 00:00:00 2001 From: Denis Balashov Date: Tue, 9 Jun 2026 11:17:31 +0300 Subject: [PATCH 7/8] qa/cocci: fix broken regex alternation in malloc-error-check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coccinelle uses OCaml Str, not PCRE. The '|' and '()' characters are literals in OCaml Str, so 'identifier func =~ "(SCMalloc|SCCalloc|...)"' never matched anything — making the entire script a no-op since its introduction. Replace all five patterns with OCaml Str alternation syntax 'A\|B'. Ticket: 8641 --- qa/coccinelle/malloc-error-check.cocci | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qa/coccinelle/malloc-error-check.cocci b/qa/coccinelle/malloc-error-check.cocci index ce4d0cc47f96..d7d1bae8cf5b 100644 --- a/qa/coccinelle/malloc-error-check.cocci +++ b/qa/coccinelle/malloc-error-check.cocci @@ -1,7 +1,7 @@ @malloced@ expression x; position p1; -identifier func =~ "(SCMalloc|SCStrdup|SCCalloc|SCMallocAligned|SCRealloc)"; +identifier func =~ "SCMalloc\|SCStrdup\|SCCalloc\|SCMallocAligned\|SCRealloc"; @@ x@p1 = func(...) @@ -10,7 +10,7 @@ x@p1 = func(...) expression x, E; statement S; position malloced.p1; -identifier func =~ "(SCMalloc|SCStrdup|SCCalloc|SCMallocAligned|SCRealloc)"; +identifier func =~ "SCMalloc\|SCStrdup\|SCCalloc\|SCMallocAligned\|SCRealloc"; @@ ( @@ -22,7 +22,7 @@ if (E && (x@p1 = func(...)) == NULL) S @realloc exists@ position malloced.p1; expression x, E1; -identifier func =~ "(SCMalloc|SCCalloc|SCMallocAligned)"; +identifier func =~ "SCMalloc\|SCCalloc\|SCMallocAligned"; @@ x@p1 = func(...) @@ -33,7 +33,7 @@ x = SCRealloc(x, E1) expression x, E1; position malloced.p1; statement S1, S2; -identifier func =~ "(SCMalloc|SCStrdup|SCCalloc|SCMallocAligned|SCRealloc)"; +identifier func =~ "SCMalloc\|SCStrdup\|SCCalloc\|SCMallocAligned\|SCRealloc"; @@ x@p1 = func(...) From 1dd6f8c70fce017f0a26c7b3271dd8d76bb46577 Mon Sep 17 00:00:00 2001 From: Denis Balashov Date: Thu, 4 Jun 2026 12:32:44 +0300 Subject: [PATCH 8/8] decode: propagate PacketAlertCreate failure instead of crashing PacketAlertCreate is called from PacketInit on the packet allocation path. Make PacketInit return bool and propagate the NULL result from PacketAlertCreate up through PacketGetFromAlloc, which already returns NULL to signal allocation failure to its callers. Update the UNITTESTS-only helpers in defrag.c accordingly: helpers returning Packet * use an explicit NULL check; the one returning int keeps the existing FAIL_IF style. --- src/decode.c | 10 +++++----- src/defrag.c | 17 +++++++++++++---- src/packet.c | 6 +++++- src/packet.h | 2 +- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/decode.c b/src/decode.c index ce452930aa21..3bc326b71764 100644 --- a/src/decode.c +++ b/src/decode.c @@ -143,10 +143,7 @@ ExceptionPolicyStatsSetts flow_memcap_eps_stats = { */ PacketAlert *PacketAlertCreate(void) { - PacketAlert *pa_array = SCCalloc(packet_alert_max, sizeof(PacketAlert)); - DEBUG_VALIDATE_BUG_ON(pa_array == NULL); - - return pa_array; + return SCCalloc(packet_alert_max, sizeof(PacketAlert)); } void PacketAlertRecycle(PacketAlert *pa_array, uint16_t cnt) @@ -267,7 +264,10 @@ Packet *PacketGetFromAlloc(void) if (unlikely(p == NULL)) { return NULL; } - PacketInit(p); + if (!PacketInit(p)) { + SCFree(p); + return NULL; + } p->ReleasePacket = PacketFree; SCLogDebug("allocated a new packet only using alloc..."); diff --git a/src/defrag.c b/src/defrag.c index ff785434592c..397041aa3f81 100644 --- a/src/defrag.c +++ b/src/defrag.c @@ -1158,7 +1158,10 @@ static Packet *BuildIpv4TestPacket( if (unlikely(p == NULL)) return NULL; - PacketInit(p); + if (!PacketInit(p)) { + SCFree(p); + return NULL; + } struct timeval tval; gettimeofday(&tval, NULL); @@ -1222,7 +1225,7 @@ static int BuildIpv4TestPacketWithContent(Packet **packet, uint8_t proto, uint16 p = SCCalloc(1, sizeof(*p) + default_packet_size); FAIL_IF_NULL(p); - PacketInit(p); + FAIL_IF(!PacketInit(p)); struct timeval tval; gettimeofday(&tval, NULL); @@ -1277,7 +1280,10 @@ static Packet *BuildIpv6TestPacket( if (unlikely(p == NULL)) return NULL; - PacketInit(p); + if (!PacketInit(p)) { + SCFree(p); + return NULL; + } struct timeval tval; gettimeofday(&tval, NULL); @@ -1347,7 +1353,10 @@ static Packet *BuildIpv6TestPacketWithContent( if (unlikely(p == NULL)) return NULL; - PacketInit(p); + if (!PacketInit(p)) { + SCFree(p); + return NULL; + } struct timeval tval; gettimeofday(&tval, NULL); diff --git a/src/packet.c b/src/packet.c index 37caada75b90..f2847c3bc49d 100644 --- a/src/packet.c +++ b/src/packet.c @@ -70,12 +70,16 @@ uint8_t PacketGetAction(const Packet *p) /** * \brief Initialize a packet structure for use. */ -void PacketInit(Packet *p) +bool PacketInit(Packet *p) { SCSpinInit(&p->persistent.tunnel_lock, 0); p->alerts.alerts = PacketAlertCreate(); + if (unlikely(p->alerts.alerts == NULL)) { + return false; + } p->livedev_id = 0; p->livedev_dst_id = 0; + return true; } void PacketReleaseRefs(Packet *p) diff --git a/src/packet.h b/src/packet.h index 5ad43c3ddfcc..044068ad17fc 100644 --- a/src/packet.h +++ b/src/packet.h @@ -32,7 +32,7 @@ static inline uint8_t PacketTestAction(const Packet *p, const uint8_t a) } #endif -void PacketInit(Packet *p); +bool PacketInit(Packet *p); void PacketReleaseRefs(Packet *p); void PacketReinit(Packet *p); void PacketRecycle(Packet *p);