From e0152178da6f4e0b66d3bdab29495ca407d649e5 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Thu, 11 Jun 2026 22:22:52 +0200 Subject: [PATCH 01/23] rust: format detect files Ticket: 3836 --- rust/src/detect/byte_extract.rs | 3 ++- rust/src/detect/byte_math.rs | 5 ++-- rust/src/detect/entropy.rs | 8 +++---- rust/src/detect/float.rs | 22 +++++++++--------- rust/src/detect/iprep.rs | 34 +++++++++++++++++++++------- rust/src/detect/requires.rs | 9 +++++--- rust/src/detect/tojson/mod.rs | 12 +++------- rust/src/detect/transforms/base64.rs | 3 ++- rust/src/detect/transforms/domain.rs | 4 ++-- rust/src/detect/transforms/hash.rs | 4 ++-- rust/src/detect/transforms/xor.rs | 2 +- rust/src/detect/uint.rs | 26 ++++++++++++++------- scripts/rustfmt.sh | 2 +- 13 files changed, 81 insertions(+), 53 deletions(-) diff --git a/rust/src/detect/byte_extract.rs b/rust/src/detect/byte_extract.rs index d8ff6a747db7..930c19904c4c 100644 --- a/rust/src/detect/byte_extract.rs +++ b/rust/src/detect/byte_extract.rs @@ -98,7 +98,8 @@ fn parse_byteextract(input: &str) -> IResult<&str, SCDetectByteExtractData, Rule let (_, values) = nom8::multi::separated_list1( tag(","), preceded(multispace0, nom8::bytes::complete::is_not(",")), - ).parse(input)?; + ) + .parse(input)?; if values.len() < DETECT_BYTE_EXTRACT_FIXED_PARAM_COUNT || values.len() > DETECT_BYTE_EXTRACT_MAX_PARAM_COUNT diff --git a/rust/src/detect/byte_math.rs b/rust/src/detect/byte_math.rs index accb162a1dcf..6ebf5955f768 100644 --- a/rust/src/detect/byte_math.rs +++ b/rust/src/detect/byte_math.rs @@ -158,7 +158,8 @@ fn parse_bytemath(input: &str) -> IResult<&str, DetectByteMathData, RuleParseErr let (_, values) = nom8::multi::separated_list1( tag(","), preceded(multispace0, nom8::bytes::complete::is_not(",")), - ).parse(input)?; + ) + .parse(input)?; if values.len() < DETECT_BYTEMATH_FIXED_PARAM_COUNT || values.len() > DETECT_BYTEMATH_MAX_PARAM_COUNT @@ -350,7 +351,7 @@ fn parse_bytemath(input: &str) -> IResult<&str, DetectByteMathData, RuleParseErr // Using left/right shift further restricts the value of nbytes. Note that // validation has already ensured nbytes is in [1..10] match byte_math.oper { - ByteMathOperator::LeftShift | ByteMathOperator::RightShift if byte_math.nbytes > 4 => { + ByteMathOperator::LeftShift | ByteMathOperator::RightShift if byte_math.nbytes > 4 => { return Err(make_error(format!("nbytes must be 1 through 4 (inclusive) when used with \"<<\" or \">>\"; {} is not valid", byte_math.nbytes))); } _ => {} diff --git a/rust/src/detect/entropy.rs b/rust/src/detect/entropy.rs index d9d4bca8435b..db960eac0d15 100644 --- a/rust/src/detect/entropy.rs +++ b/rust/src/detect/entropy.rs @@ -27,7 +27,7 @@ use nom8::sequence::preceded; use nom8::{Err, IResult, Parser}; use std::ffi::CStr; -use std::os::raw::{c_double, c_char, c_void}; +use std::os::raw::{c_char, c_double, c_void}; use std::slice; #[repr(C)] @@ -74,7 +74,8 @@ fn parse_entropy<'a>( let (_, values) = nom8::multi::separated_list1( tag(","), preceded(multispace0, nom8::bytes::complete::is_not(",")), - ).parse(input)?; + ) + .parse(input)?; if values.len() < DETECT_ENTROPY_FIXED_PARAM_COUNT || values.len() > DETECT_ENTROPY_MAX_PARAM_COUNT @@ -168,8 +169,7 @@ fn calculate_entropy(data: &[u8]) -> f64 { #[no_mangle] pub unsafe extern "C" fn SCDetectEntropyMatch( - c_data: *const c_void, length: i32, ctx: &DetectEntropyData, - calculated_entropy: *mut c_double, + c_data: *const c_void, length: i32, ctx: &DetectEntropyData, calculated_entropy: *mut c_double, ) -> bool { if c_data.is_null() { return false; diff --git a/rust/src/detect/float.rs b/rust/src/detect/float.rs index c657b6e4bf13..fe7c40c7b471 100644 --- a/rust/src/detect/float.rs +++ b/rust/src/detect/float.rs @@ -94,18 +94,15 @@ pub fn parse_float_value(input: &str) -> IResult<&str, T> { // Handle numeric parsing, including scientific notation map_opt( recognize(( - opt(alt((tag("+"), tag("-")))), // Handle optional signs + opt(alt((tag("+"), tag("-")))), // Handle optional signs alt((digit1, recognize((tag("."), digit1)))), // Handle integers & `.5` - opt((tag("."), digit1)), // Handle decimals like `5.` - opt(( - tag_no_case("e"), - opt(alt((tag("+"), tag("-")))), - digit1, - )), // Handle `1e10`, `-1e-5` + opt((tag("."), digit1)), // Handle decimals like `5.` + opt((tag_no_case("e"), opt(alt((tag("+"), tag("-")))), digit1)), // Handle `1e10`, `-1e-5` )), |float_str: &str| ::from_str(float_str), ), - )).parse(input) + )) + .parse(input) } fn detect_parse_float_start_equal( i: &str, @@ -133,7 +130,8 @@ pub fn detect_parse_float_start_interval( let (i, _) = opt(is_a(" ")).parse(i)?; let (i, arg2) = verify(parse_float_value::, |x| { *x > arg1 && *x - arg1 > ::epsilon() - }).parse(i)?; + }) + .parse(i)?; let mode = if neg.is_some() { DetectFloatMode::DetectFloatModeNegRg } else { @@ -150,7 +148,8 @@ fn detect_parse_float_mode(i: &str) -> IResult<&str, DetectFloatMode> { value(DetectFloatMode::DetectFloatModeLt, tag("<")), value(DetectFloatMode::DetectFloatModeNe, tag("!=")), value(DetectFloatMode::DetectFloatModeEqual, tag("=")), - )).parse(i)?; + )) + .parse(i)?; Ok((i, mode)) } @@ -223,7 +222,8 @@ fn detect_parse_float_notending(i: &str) -> IResult<&str, De detect_parse_float_start_interval, detect_parse_float_start_equal, detect_parse_float_start_symbol, - )).parse(i)?; + )) + .parse(i)?; Ok((i, float)) } diff --git a/rust/src/detect/iprep.rs b/rust/src/detect/iprep.rs index da042be24aaf..68f4d20061ce 100644 --- a/rust/src/detect/iprep.rs +++ b/rust/src/detect/iprep.rs @@ -77,7 +77,8 @@ pub fn detect_parse_iprep(i: &str) -> IResult<&str, DetectIPRepData, RuleParseEr let (_, values) = nom8::multi::separated_list1( tag(","), preceded(multispace0, nom8::bytes::complete::is_not(",")), - ).parse(i)?; + ) + .parse(i)?; let args = values.len(); if args == 4 || args == 3 { @@ -112,26 +113,43 @@ pub fn detect_parse_iprep(i: &str) -> IResult<&str, DetectIPRepData, RuleParseEr arg2: 0, mode, }; - return Ok((i, DetectIPRepData { du8, cat, cmd, isnotset: false, })); + return Ok(( + i, + DetectIPRepData { + du8, + cat, + cmd, + isnotset: false, + }, + )); } else { let (isnotset, mode, arg1) = match values[2].trim() { - "isset" => { (false, DetectUintMode::DetectUintModeGte, 0) }, - "isnotset" => { (true, DetectUintMode::DetectUintModeEqual, 0) }, - _ => { return Err(make_error("invalid mode".to_string())); }, + "isset" => (false, DetectUintMode::DetectUintModeGte, 0), + "isnotset" => (true, DetectUintMode::DetectUintModeEqual, 0), + _ => { + return Err(make_error("invalid mode".to_string())); + } }; let du8 = DetectUintData:: { arg1, arg2: 0, mode, }; - return Ok((i, DetectIPRepData { du8, cat, cmd, isnotset, })); + return Ok(( + i, + DetectIPRepData { + du8, + cat, + cmd, + isnotset, + }, + )); } } else if args < 3 { return Err(make_error("too few arguments".to_string())); - } else { + } else { return Err(make_error("too many arguments".to_string())); } - } #[no_mangle] diff --git a/rust/src/detect/requires.rs b/rust/src/detect/requires.rs index 57cd2d7feaa0..c874d768912c 100644 --- a/rust/src/detect/requires.rs +++ b/rust/src/detect/requires.rs @@ -194,7 +194,8 @@ fn parse_op(input: &str) -> IResult<&str, VersionCompareOp> { map(tag("<="), |_| VersionCompareOp::Lte), map(tag("<"), |_| VersionCompareOp::Lt), )), - ).parse(input) + ) + .parse(input) } /// Parse the next part of the version. @@ -204,7 +205,8 @@ fn parse_next_version_part(input: &str) -> IResult<&str, u8> { map_res( take_till(|c| c == '.' || c == '-' || c == ' '), |s: &str| s.parse::(), - ).parse(input) + ) + .parse(input) } /// Parse a version string into a SuricataVersion. @@ -229,7 +231,8 @@ fn parse_key_value(input: &str) -> IResult<&str, (&str, &str)> { let (input, key) = preceded( multispace0, take_while(|c: char| c.is_alphanumeric() || c == '-' || c == '_'), - ).parse(input)?; + ) + .parse(input)?; let (input, value) = preceded(multispace0, take_till(|c: char| c == ',')).parse(input)?; Ok((input, (key, value))) } diff --git a/rust/src/detect/tojson/mod.rs b/rust/src/detect/tojson/mod.rs index 1ad8a504dbe1..82a6cbcec0e8 100644 --- a/rust/src/detect/tojson/mod.rs +++ b/rust/src/detect/tojson/mod.rs @@ -74,22 +74,16 @@ where } #[no_mangle] -pub unsafe extern "C" fn SCDetectU8ToJson( - js: &mut JsonBuilder, du: &DetectUintData, -) -> bool { +pub unsafe extern "C" fn SCDetectU8ToJson(js: &mut JsonBuilder, du: &DetectUintData) -> bool { return detect_uint_to_json(js, du).is_ok(); } #[no_mangle] -pub unsafe extern "C" fn SCDetectU16ToJson( - js: &mut JsonBuilder, du: &DetectUintData, -) -> bool { +pub unsafe extern "C" fn SCDetectU16ToJson(js: &mut JsonBuilder, du: &DetectUintData) -> bool { return detect_uint_to_json(js, du).is_ok(); } #[no_mangle] -pub unsafe extern "C" fn SCDetectU32ToJson( - js: &mut JsonBuilder, du: &DetectUintData, -) -> bool { +pub unsafe extern "C" fn SCDetectU32ToJson(js: &mut JsonBuilder, du: &DetectUintData) -> bool { return detect_uint_to_json(js, du).is_ok(); } diff --git a/rust/src/detect/transforms/base64.rs b/rust/src/detect/transforms/base64.rs index 4a2cfffc83b0..d6e2975292d2 100644 --- a/rust/src/detect/transforms/base64.rs +++ b/rust/src/detect/transforms/base64.rs @@ -99,7 +99,8 @@ fn parse_transform_base64( let (_, values) = nom8::multi::separated_list1( tag(","), preceded(multispace0, nom8::bytes::complete::is_not(",")), - ).parse(input)?; + ) + .parse(input)?; // Too many options? if values.len() > DETECT_TRANSFORM_BASE64_MAX_PARAM_COUNT { diff --git a/rust/src/detect/transforms/domain.rs b/rust/src/detect/transforms/domain.rs index d59f475c6e28..4d2bb56f3d65 100644 --- a/rust/src/detect/transforms/domain.rs +++ b/rust/src/detect/transforms/domain.rs @@ -18,8 +18,8 @@ use crate::detect::SIGMATCH_NOOPT; use suricata_sys::sys::{ DetectEngineCtx, DetectEngineThreadCtx, InspectionBuffer, SCDetectHelperTransformRegister, - SCDetectSignatureAddTransform, SCTransformTableElmt, Signature, SCInspectionBufferCheckAndExpand, - SCInspectionBufferTruncate, + SCDetectSignatureAddTransform, SCInspectionBufferCheckAndExpand, SCInspectionBufferTruncate, + SCTransformTableElmt, Signature, }; use std::os::raw::{c_int, c_void}; diff --git a/rust/src/detect/transforms/hash.rs b/rust/src/detect/transforms/hash.rs index 763dbc042cdb..92b55467a6f1 100644 --- a/rust/src/detect/transforms/hash.rs +++ b/rust/src/detect/transforms/hash.rs @@ -18,8 +18,8 @@ use crate::detect::SIGMATCH_NOOPT; use suricata_sys::sys::{ DetectEngineCtx, DetectEngineThreadCtx, InspectionBuffer, SCDetectHelperTransformRegister, - SCDetectSignatureAddTransform, SCTransformTableElmt, Signature, SCInspectionBufferCheckAndExpand, - SCInspectionBufferTruncate, + SCDetectSignatureAddTransform, SCInspectionBufferCheckAndExpand, SCInspectionBufferTruncate, + SCTransformTableElmt, Signature, }; use crate::ffi::hashing::{G_DISABLE_HASHING, SC_SHA1_LEN, SC_SHA256_LEN}; diff --git a/rust/src/detect/transforms/xor.rs b/rust/src/detect/transforms/xor.rs index 42454caac632..e7372c51589e 100644 --- a/rust/src/detect/transforms/xor.rs +++ b/rust/src/detect/transforms/xor.rs @@ -108,7 +108,7 @@ unsafe extern "C" fn xor_free(_de: *mut DetectEngineCtx, ctx: *mut c_void) { std::mem::drop(Box::from_raw(ctx as *mut DetectTransformXorData)); } -unsafe extern "C" fn xor_id(data: *mut *const u8, length: *mut u32, ctx: *const c_void,) { +unsafe extern "C" fn xor_id(data: *mut *const u8, length: *mut u32, ctx: *const c_void) { if data.is_null() || length.is_null() || ctx.is_null() { return; } diff --git a/rust/src/detect/uint.rs b/rust/src/detect/uint.rs index 665f2ec9ee1d..e210a245d7c0 100644 --- a/rust/src/detect/uint.rs +++ b/rust/src/detect/uint.rs @@ -85,7 +85,9 @@ fn parse_uint_index_nb(s: &str) -> IResult<&str, DetectUintIndex> { } fn parse_uint_index_val(s: &str) -> Option { - let (_s, arg1) = alt((parse_uint_index_precise, parse_uint_index_nb)).parse(s).ok()?; + let (_s, arg1) = alt((parse_uint_index_precise, parse_uint_index_nb)) + .parse(s) + .ok()?; Some(arg1) } @@ -386,7 +388,9 @@ pub fn detect_parse_uint_bitflags>( } // otherwise, try strings for bitmask let (s, modifier) = parse_bitchars_modifier(s, defmod).ok()?; - let (s, _) = take_while::<_, &str, Error<_>>(|c| c == ' ' || c == '\t').parse(s).ok()?; + let (s, _) = take_while::<_, &str, Error<_>>(|c| c == ' ' || c == '\t') + .parse(s) + .ok()?; if let Ok((rem, l)) = parse_flag_list::(s, singlechar) { if !rem.is_empty() { SCLogError!("junk at the end of bitflags"); @@ -528,7 +532,8 @@ pub fn detect_parse_uint_unit(i: &str) -> IResult<&str, u64> { value(1024 * 1024, tag_no_case("mb")), value(1024 * 1024 * 1024, tag_no_case("gib")), value(1024 * 1024 * 1024, tag_no_case("gb")), - )).parse(i)?; + )) + .parse(i)?; return Ok((i, unit)); } @@ -587,7 +592,8 @@ pub fn detect_parse_uint_start_interval( let (i, _) = opt(is_a(" ")).parse(i)?; let (i, arg2) = verify(detect_parse_uint_value, |x| { x > &arg1 && *x - arg1 > T::one() - }).parse(i)?; + }) + .parse(i)?; let mode = if neg.is_some() { DetectUintMode::DetectUintModeNegRg } else { @@ -628,7 +634,8 @@ fn detect_parse_uint_start_interval_inclusive( let (i, _) = opt(is_a(" ")).parse(i)?; let (i, arg2) = verify(detect_parse_uint_value::, |x| { *x > arg1 && *x < T::max_value() - }).parse(i)?; + }) + .parse(i)?; let mode = if neg.is_some() { DetectUintMode::DetectUintModeNegRg } else { @@ -653,7 +660,8 @@ pub fn detect_parse_uint_mode(i: &str) -> IResult<&str, DetectUintMode> { value(DetectUintMode::DetectUintModeNe, tag("!=")), value(DetectUintMode::DetectUintModeNe, tag("!")), value(DetectUintMode::DetectUintModeEqual, tag("=")), - )).parse(i)?; + )) + .parse(i)?; return Ok((i, mode)); } @@ -770,7 +778,8 @@ pub(crate) fn detect_parse_uint_notending( detect_parse_uint_start_interval, detect_parse_uint_start_equal, detect_parse_uint_start_symbol, - )).parse(i)?; + )) + .parse(i)?; Ok((i, uint)) } @@ -786,7 +795,8 @@ pub fn detect_parse_uint_inclusive(i: &str) -> IResult<&str, D detect_parse_uint_start_interval_inclusive, detect_parse_uint_start_equal, detect_parse_uint_start_symbol, - )).parse(i)?; + )) + .parse(i)?; let (i, _) = all_consuming(take_while(|c| c == ' ')).parse(i)?; Ok((i, uint)) } diff --git a/scripts/rustfmt.sh b/scripts/rustfmt.sh index 67a5f2558b4e..2472b46e818d 100755 --- a/scripts/rustfmt.sh +++ b/scripts/rustfmt.sh @@ -41,4 +41,4 @@ rustfmt --check rust/src/dns/*.rs rust/src/applayertemplate/*.rs rust/src/asn1/* rust/src/http2/*.rs rust/src/ike/*.rs rust/src/modbus/*.rs rust/src/mqtt/*.rs \ rust/src/nfs/*.rs rust/src/pgsql/*.rs rust/src/rdp/*.rs rust/src/sdp/*.rs \ rust/src/sip/*.rs rust/src/telnet/*.rs rust/src/tftp/*.rs rust/src/x509/*.rs \ - rust/src/snmp/*.rs rust/src/llmnr/*.rs + rust/src/snmp/*.rs rust/src/llmnr/*.rs rust/src/detect/*.rs From a9e1dff4a6e2138364a9fb1a7879e34f4415747c Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Fri, 12 Jun 2026 09:44:25 +0200 Subject: [PATCH 02/23] conf: introduce SCConfGetNonNull Ticket: 8651 Behaves like SCConfGet but returns 0 on null value --- rust/sys/src/sys.rs | 5 +++++ src/conf.c | 30 +++++++++++++++++++++++++++--- src/conf.h | 1 + 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/rust/sys/src/sys.rs b/rust/sys/src/sys.rs index d33e598230d3..78a6671369b5 100644 --- a/rust/sys/src/sys.rs +++ b/rust/sys/src/sys.rs @@ -262,6 +262,11 @@ extern "C" { name: *const ::std::os::raw::c_char, vptr: *mut *const ::std::os::raw::c_char, ) -> ::std::os::raw::c_int; } +extern "C" { + pub fn SCConfGetNonNull( + name: *const ::std::os::raw::c_char, vptr: *mut *const ::std::os::raw::c_char, + ) -> ::std::os::raw::c_int; +} extern "C" { pub fn SCConfGetInt( name: *const ::std::os::raw::c_char, val: *mut intmax_t, diff --git a/src/conf.c b/src/conf.c index 9c0c3e0ad4eb..ff40575d1226 100644 --- a/src/conf.c +++ b/src/conf.c @@ -361,6 +361,30 @@ int SCConfGet(const char *name, const char **vptr) } } +/** + * \brief Retrieve the non-null value of a configuration node. + * + * This function will return the value for a configuration node based + * on the full name of the node. If the value were NULL, return 0 + * (this could happen if the requested node does exist but is not a node + * that contains a value, but contains children SCConfNodes instead.) + * + * \param name Name of configuration parameter to get. + * \param vptr Pointer that will be set to the configuration value parameter. + * Note that this is just a reference to the actual value, not a copy. + * + * \retval 1 will be returned if the value is found, otherwise 0 will + * be returned. + */ +int SCConfGetNonNull(const char *name, const char **vptr) +{ + int r = SCConfGet(name, vptr); + if (r == 1 && *vptr == NULL) { + return 0; + } + return r; +} + int SCConfGetChildValue(const SCConfNode *base, const char *name, const char **vptr) { SCConfNode *node = SCConfNodeLookupChild(base, name); @@ -500,7 +524,7 @@ int SCConfGetBool(const char *name, int *val) const char *strval = NULL; *val = 0; - if (SCConfGet(name, &strval) != 1) + if (SCConfGetNonNull(name, &strval) != 1) return 0; *val = SCConfValIsTrue(strval); @@ -604,7 +628,7 @@ int SCConfGetDouble(const char *name, double *val) double tmpdo; char *endptr; - if (SCConfGet(name, &strval) == 0) + if (SCConfGetNonNull(name, &strval) == 0) return 0; errno = 0; @@ -634,7 +658,7 @@ int SCConfGetFloat(const char *name, float *val) double tmpfl; char *endptr; - if (SCConfGet(name, &strval) == 0) + if (SCConfGetNonNull(name, &strval) == 0) return 0; errno = 0; diff --git a/src/conf.h b/src/conf.h index 0f3a881aca97..34f1c0709f8d 100644 --- a/src/conf.h +++ b/src/conf.h @@ -63,6 +63,7 @@ void SCConfInit(void); void SCConfDeInit(void); SCConfNode *SCConfGetRootNode(void); int SCConfGet(const char *name, const char **vptr); +int SCConfGetNonNull(const char *name, const char **vptr); int SCConfGetInt(const char *name, intmax_t *val); int SCConfGetBool(const char *name, int *val); int SCConfGetDouble(const char *name, double *val); From 6bb271cee97304025e76fc9799d0107af5995dd2 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Fri, 12 Jun 2026 09:34:00 +0200 Subject: [PATCH 03/23] conf: uses SCConfGetNonNull Ticke: 8651 Uses it in place when we dereferenced the value straight away after checking SCConfGet result but not its value --- plugins/pfring/runmode-pfring.c | 8 ++++---- src/app-layer-htp-mem.c | 2 +- src/app-layer-htp-range.c | 4 ++-- src/app-layer-smtp.c | 2 +- src/app-layer-ssh.c | 2 +- src/app-layer-ssl.c | 4 ++-- src/counters.c | 2 +- src/datasets.c | 8 ++++---- src/defrag-hash.c | 8 ++++---- src/detect-engine-threshold.c | 2 +- src/detect-engine.c | 2 +- src/detect-uricontent.c | 2 +- src/host.c | 6 +++--- src/ippair.c | 6 +++--- src/reputation.c | 2 +- src/runmode-erf-file.c | 4 ++-- src/runmode-pcap-file.c | 4 ++-- src/runmode-pcap.c | 2 +- src/source-nfq.c | 2 +- src/source-pcap-file-helper.c | 2 +- src/source-pcap-file.c | 6 +++--- src/stream-tcp.c | 14 +++++++------- src/suricata.c | 10 +++++----- src/tests/stream-tcp.c | 2 +- src/tmqh-flow.c | 2 +- src/unix-manager.c | 4 ++-- src/util-bpf.c | 2 +- src/util-classification-config.c | 6 +++--- src/util-conf.c | 4 ++-- src/util-daemon.c | 2 +- src/util-debug.c | 4 ++-- src/util-landlock.c | 6 +++--- src/util-reference-config.c | 6 +++--- src/util-rule-vars.c | 2 +- src/util-thash.c | 6 +++--- src/util-threshold-config.c | 6 +++--- 36 files changed, 78 insertions(+), 78 deletions(-) diff --git a/plugins/pfring/runmode-pfring.c b/plugins/pfring/runmode-pfring.c index 6d2e97c8d961..d657799cc741 100644 --- a/plugins/pfring/runmode-pfring.c +++ b/plugins/pfring/runmode-pfring.c @@ -134,7 +134,7 @@ static void *OldParsePfringConfig(const char *iface) SCLogInfo("%s: ZC interface detected, not setting cluster-id", pfconf->iface); } else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) { SCLogInfo("DNA interface detected, not setting cluster-id"); - } else if (SCConfGet("pfring.cluster-id", &tmpclusterid) != 1) { + } else if (SCConfGetNonNull("pfring.cluster-id", &tmpclusterid) != 1) { SCLogError("Could not get cluster-id from config"); } else { if (StringParseInt32(&pfconf->cluster_id, 10, 0, (const char *)tmpclusterid) < 0) { @@ -152,7 +152,7 @@ static void *OldParsePfringConfig(const char *iface) } else if ((pfconf->threads == 1) && (strncmp(pfconf->iface, "dna", 3) == 0)) { SCLogInfo( "%s: DNA interface detected, not setting cluster type for PF_RING", pfconf->iface); - } else if (SCConfGet("pfring.cluster-type", &tmpctype) != 1) { + } else if (SCConfGetNonNull("pfring.cluster-type", &tmpctype) != 1) { SCLogError("Could not get cluster-type from config"); } else if (strcmp(tmpctype, "cluster_round_robin") == 0) { SCLogInfo("%s: Using round-robin cluster mode for PF_RING", pfconf->iface); @@ -275,7 +275,7 @@ static void *ParsePfringConfig(const char *iface) (void)SC_ATOMIC_ADD(pfconf->ref, pfconf->threads); /* command line value has precedence */ - if (SCConfGet("pfring.cluster-id", &tmpclusterid) == 1) { + if (SCConfGetNonNull("pfring.cluster-id", &tmpclusterid) == 1) { if (StringParseInt32(&pfconf->cluster_id, 10, 0, (const char *)tmpclusterid) < 0) { SCLogWarning("Invalid value for " "pfring.cluster-id: '%s'. Resetting to 1.", @@ -425,7 +425,7 @@ static int GetDevAndParser(const char **live_dev, ConfigIfaceParserFunc *parser) *parser = OldParsePfringConfig; /* In v1: try to get interface name from config */ if (*live_dev == NULL) { - if (SCConfGet("pfring.interface", live_dev) == 1) { + if (SCConfGetNonNull("pfring.interface", live_dev) == 1) { SCLogInfo("Using interface %s", *live_dev); LiveRegisterDevice(*live_dev); } else { diff --git a/src/app-layer-htp-mem.c b/src/app-layer-htp-mem.c index e72416bec87d..e332499cf251 100644 --- a/src/app-layer-htp-mem.c +++ b/src/app-layer-htp-mem.c @@ -48,7 +48,7 @@ void HTPParseMemcap(void) /** set config values for memcap, prealloc and hash_size */ uint64_t memcap; - if ((SCConfGet("app-layer.protocols.http.memcap", &conf_val)) == 1) { + if ((SCConfGetNonNull("app-layer.protocols.http.memcap", &conf_val)) == 1) { if (ParseSizeStringU64(conf_val, &memcap) < 0) { SCLogError("Error parsing http.memcap " "from conf file - %s. Killing engine", diff --git a/src/app-layer-htp-range.c b/src/app-layer-htp-range.c index cff08144ba47..b60fb8283ab4 100644 --- a/src/app-layer-htp-range.c +++ b/src/app-layer-htp-range.c @@ -172,7 +172,7 @@ void HttpRangeContainersInit(void) const char *str = NULL; uint64_t memcap = HTTP_RANGE_DEFAULT_MEMCAP; uint32_t timeout = HTTP_RANGE_DEFAULT_TIMEOUT; - if (SCConfGet("app-layer.protocols.http.byterange.memcap", &str) == 1) { + if (SCConfGetNonNull("app-layer.protocols.http.byterange.memcap", &str) == 1) { if (ParseSizeStringU64(str, &memcap) < 0) { SCLogWarning("memcap value cannot be deduced: %s," " resetting to default", @@ -180,7 +180,7 @@ void HttpRangeContainersInit(void) memcap = 0; } } - if (SCConfGet("app-layer.protocols.http.byterange.timeout", &str) == 1) { + if (SCConfGetNonNull("app-layer.protocols.http.byterange.timeout", &str) == 1) { size_t slen = strlen(str); if (slen > UINT16_MAX || StringParseUint32(&timeout, 10, (uint16_t)slen, str) <= 0) { SCLogWarning("timeout value cannot be deduced: %s," diff --git a/src/app-layer-smtp.c b/src/app-layer-smtp.c index 14ae13715ddf..0e5a599e6df4 100644 --- a/src/app-layer-smtp.c +++ b/src/app-layer-smtp.c @@ -452,7 +452,7 @@ static void SMTPConfigure(void) { uint64_t value = SMTP_DEFAULT_MAX_TX; smtp_config.max_tx = SMTP_DEFAULT_MAX_TX; const char *str = NULL; - if (SCConfGet("app-layer.protocols.smtp.max-tx", &str) == 1) { + if (SCConfGetNonNull("app-layer.protocols.smtp.max-tx", &str) == 1) { if (ParseSizeStringU64(str, &value) < 0) { SCLogWarning("max-tx value cannot be deduced: %s," " keeping default", diff --git a/src/app-layer-ssh.c b/src/app-layer-ssh.c index 53460791a821..6e75c4247104 100644 --- a/src/app-layer-ssh.c +++ b/src/app-layer-ssh.c @@ -90,7 +90,7 @@ void RegisterSSHParsers(void) /* Check if we should generate Hassh fingerprints */ int enable_hassh = SSH_CONFIG_DEFAULT_HASSH; const char *strval = NULL; - if (SCConfGet("app-layer.protocols.ssh.hassh", &strval) != 1) { + if (SCConfGetNonNull("app-layer.protocols.ssh.hassh", &strval) != 1) { enable_hassh = SSH_CONFIG_DEFAULT_HASSH; } else if (strcmp(strval, "auto") == 0) { enable_hassh = SSH_CONFIG_DEFAULT_HASSH; diff --git a/src/app-layer-ssl.c b/src/app-layer-ssl.c index 33f2d77922bb..ad41e7163949 100644 --- a/src/app-layer-ssl.c +++ b/src/app-layer-ssl.c @@ -3137,7 +3137,7 @@ static void CheckJA3Enabled(void) const char *strval = NULL; /* Check if we should generate JA3 fingerprints */ int enable_ja3 = SSL_CONFIG_DEFAULT_JA3; - if (SCConfGet("app-layer.protocols.tls.ja3-fingerprints", &strval) != 1) { + if (SCConfGetNonNull("app-layer.protocols.tls.ja3-fingerprints", &strval) != 1) { enable_ja3 = SSL_CONFIG_DEFAULT_JA3; } else if (strcmp(strval, "auto") == 0) { enable_ja3 = SSL_CONFIG_DEFAULT_JA3; @@ -3162,7 +3162,7 @@ static void CheckJA4Enabled(void) const char *strval = NULL; /* Check if we should generate JA4 fingerprints */ int enable_ja4 = SSL_CONFIG_DEFAULT_JA4; - if (SCConfGet("app-layer.protocols.tls.ja4-fingerprints", &strval) != 1) { + if (SCConfGetNonNull("app-layer.protocols.tls.ja4-fingerprints", &strval) != 1) { enable_ja4 = SSL_CONFIG_DEFAULT_JA4; } else if (strcmp(strval, "auto") == 0) { enable_ja4 = SSL_CONFIG_DEFAULT_JA4; diff --git a/src/counters.c b/src/counters.c index 52d87482d64a..0c2fa55c5b11 100644 --- a/src/counters.c +++ b/src/counters.c @@ -312,7 +312,7 @@ static void StatsInitCtxPreOutput(void) } const char *prefix = NULL; - if (SCConfGet("stats.decoder-events-prefix", &prefix) != 1) { + if (SCConfGetNonNull("stats.decoder-events-prefix", &prefix) != 1) { prefix = "decoder.event"; } stats_decoder_events_prefix = prefix; diff --git a/src/datasets.c b/src/datasets.c index e2bad8aa6dd9..6ab64527d4e2 100644 --- a/src/datasets.c +++ b/src/datasets.c @@ -595,7 +595,7 @@ void DatasetPostReloadCleanup(void) void DatasetGetDefaultMemcap(uint64_t *memcap, uint32_t *hashsize) { const char *str = NULL; - if (SCConfGet("datasets.defaults.memcap", &str) == 1) { + if (SCConfGetNonNull("datasets.defaults.memcap", &str) == 1) { if (ParseSizeStringU64(str, memcap) < 0) { SCLogWarning("memcap value cannot be deduced: %s," " resetting to default", @@ -605,7 +605,7 @@ void DatasetGetDefaultMemcap(uint64_t *memcap, uint32_t *hashsize) } *hashsize = (uint32_t)DATASETS_HASHSIZE_DEFAULT; - if (SCConfGet("datasets.defaults.hashsize", &str) == 1) { + if (SCConfGetNonNull("datasets.defaults.hashsize", &str) == 1) { if (ParseSizeStringU32(str, hashsize) < 0) { *hashsize = (uint32_t)DATASETS_HASHSIZE_DEFAULT; SCLogWarning("hashsize value cannot be deduced: %s," @@ -624,12 +624,12 @@ int DatasetsInit(void) DatasetGetDefaultMemcap(&default_memcap, &default_hashsize); if (datasets != NULL) { const char *str = NULL; - if (SCConfGet("datasets.limits.total-hashsizes", &str) == 1) { + if (SCConfGetNonNull("datasets.limits.total-hashsizes", &str) == 1) { if (ParseSizeStringU32(str, &dataset_max_total_hashsize) < 0) { FatalError("failed to parse datasets.limits.total-hashsizes value: %s", str); } } - if (SCConfGet("datasets.limits.single-hashsize", &str) == 1) { + if (SCConfGetNonNull("datasets.limits.single-hashsize", &str) == 1) { if (ParseSizeStringU32(str, &dataset_max_one_hashsize) < 0) { FatalError("failed to parse datasets.limits.single-hashsize value: %s", str); } diff --git a/src/defrag-hash.c b/src/defrag-hash.c index fae8a882b694..03e9123f146e 100644 --- a/src/defrag-hash.c +++ b/src/defrag-hash.c @@ -188,7 +188,7 @@ void DefragInitConfig(bool quiet) uint64_t defrag_memcap; /** set config values for memcap, prealloc and hash_size */ - if ((SCConfGet("defrag.memcap", &conf_val)) == 1) { + if ((SCConfGetNonNull("defrag.memcap", &conf_val)) == 1) { if (ParseSizeStringU64(conf_val, &defrag_memcap) < 0) { SCLogError("Error parsing defrag.memcap " "from conf file - %s. Killing engine", @@ -198,7 +198,7 @@ void DefragInitConfig(bool quiet) SC_ATOMIC_SET(defrag_config.memcap, defrag_memcap); } } - if ((SCConfGet("defrag.hash-size", &conf_val)) == 1) { + if ((SCConfGetNonNull("defrag.hash-size", &conf_val)) == 1) { if (StringParseUint32(&configval, 10, strlen(conf_val), conf_val) > 0) { defrag_config.hash_size = configval; @@ -207,7 +207,7 @@ void DefragInitConfig(bool quiet) } } - if ((SCConfGet("defrag.trackers", &conf_val)) == 1) { + if ((SCConfGetNonNull("defrag.trackers", &conf_val)) == 1) { if (StringParseUint32(&configval, 10, strlen(conf_val), conf_val) > 0) { defrag_config.prealloc = configval; @@ -250,7 +250,7 @@ void DefragInitConfig(bool quiet) (uintmax_t)sizeof(DefragTrackerHashRow)); } - if ((SCConfGet("defrag.prealloc", &conf_val)) == 1) { + if ((SCConfGetNonNull("defrag.prealloc", &conf_val)) == 1) { if (SCConfValIsTrue(conf_val)) { /* pre allocate defrag trackers */ for (i = 0; i < defrag_config.prealloc; i++) { diff --git a/src/detect-engine-threshold.c b/src/detect-engine-threshold.c index 71ba8076e04b..2a15146fc375 100644 --- a/src/detect-engine-threshold.c +++ b/src/detect-engine-threshold.c @@ -342,7 +342,7 @@ static int ThresholdsInit(struct Thresholds *t) uint64_t memcap = 16 * 1024 * 1024; const char *str; - if (SCConfGet("detect.thresholds.memcap", &str) == 1) { + if (SCConfGetNonNull("detect.thresholds.memcap", &str) == 1) { if (ParseSizeStringU64(str, &memcap) < 0) { SCLogError("Error parsing detect.thresholds.memcap from conf file - %s", str); return -1; diff --git a/src/detect-engine.c b/src/detect-engine.c index 4c0abddb0aac..2912eef0be29 100644 --- a/src/detect-engine.c +++ b/src/detect-engine.c @@ -4464,7 +4464,7 @@ int DetectEngineMultiTenantSetup(const bool unix_socket) master->multi_tenant_enabled = 1; const char *handler = NULL; - if (SCConfGet("multi-detect.selector", &handler) == 1) { + if (SCConfGetNonNull("multi-detect.selector", &handler) == 1) { SCLogConfig("multi-tenant selector type %s", handler); if (strcmp(handler, "vlan") == 0) { diff --git a/src/detect-uricontent.c b/src/detect-uricontent.c index 5ec75a4d93ff..aaa9d9d1e442 100644 --- a/src/detect-uricontent.c +++ b/src/detect-uricontent.c @@ -113,7 +113,7 @@ int DetectUricontentSetup(DetectEngineCtx *de_ctx, Signature *s, const char *con SCEnter(); const char *legacy = NULL; - if (SCConfGet("legacy.uricontent", &legacy) == 1) { + if (SCConfGetNonNull("legacy.uricontent", &legacy) == 1) { if (strcasecmp("disabled", legacy) == 0) { SCLogError("uricontent deprecated. To " "use a rule with \"uricontent\", either set the " diff --git a/src/host.c b/src/host.c index af8e30487227..c055f7436ad5 100644 --- a/src/host.c +++ b/src/host.c @@ -192,7 +192,7 @@ void HostInitConfig(bool quiet) uint32_t configval = 0; /** set config values for memcap, prealloc and hash_size */ - if ((SCConfGet("host.memcap", &conf_val)) == 1) { + if ((SCConfGetNonNull("host.memcap", &conf_val)) == 1) { uint64_t host_memcap = 0; if (ParseSizeStringU64(conf_val, &host_memcap) < 0) { SCLogError("Error parsing host.memcap " @@ -203,14 +203,14 @@ void HostInitConfig(bool quiet) SC_ATOMIC_SET(host_config.memcap, host_memcap); } } - if ((SCConfGet("host.hash-size", &conf_val)) == 1) { + if ((SCConfGetNonNull("host.hash-size", &conf_val)) == 1) { if (StringParseUint32(&configval, 10, strlen(conf_val), conf_val) > 0) { host_config.hash_size = configval; } } - if ((SCConfGet("host.prealloc", &conf_val)) == 1) { + if ((SCConfGetNonNull("host.prealloc", &conf_val)) == 1) { if (StringParseUint32(&configval, 10, strlen(conf_val), conf_val) > 0) { host_config.prealloc = configval; diff --git a/src/ippair.c b/src/ippair.c index 6683461aa833..e92435bdb3ab 100644 --- a/src/ippair.c +++ b/src/ippair.c @@ -187,7 +187,7 @@ void IPPairInitConfig(bool quiet) /** set config values for memcap, prealloc and hash_size */ uint64_t ippair_memcap; - if ((SCConfGet("ippair.memcap", &conf_val)) == 1) { + if ((SCConfGetNonNull("ippair.memcap", &conf_val)) == 1) { if (ParseSizeStringU64(conf_val, &ippair_memcap) < 0) { SCLogError("Error parsing ippair.memcap " "from conf file - %s. Killing engine", @@ -197,14 +197,14 @@ void IPPairInitConfig(bool quiet) SC_ATOMIC_SET(ippair_config.memcap, ippair_memcap); } } - if ((SCConfGet("ippair.hash-size", &conf_val)) == 1) { + if ((SCConfGetNonNull("ippair.hash-size", &conf_val)) == 1) { if (StringParseUint32(&configval, 10, strlen(conf_val), conf_val) > 0) { ippair_config.hash_size = configval; } } - if ((SCConfGet("ippair.prealloc", &conf_val)) == 1) { + if ((SCConfGetNonNull("ippair.prealloc", &conf_val)) == 1) { if (StringParseUint32(&configval, 10, strlen(conf_val), conf_val) > 0) { ippair_config.prealloc = configval; diff --git a/src/reputation.c b/src/reputation.c index 5c38fa6dca6d..f3e049d4b2c2 100644 --- a/src/reputation.c +++ b/src/reputation.c @@ -515,7 +515,7 @@ static char *SRepCompleteFilePath(char *file) /* Path not specified */ if (PathIsRelative(file)) { - if (SCConfGet("default-reputation-path", &defaultpath) == 1) { + if (SCConfGetNonNull("default-reputation-path", &defaultpath) == 1) { SCLogDebug("Default path: %s", defaultpath); size_t path_len = sizeof(char) * (strlen(defaultpath) + strlen(file) + 2); diff --git a/src/runmode-erf-file.c b/src/runmode-erf-file.c index 6d957a4f86c2..5dd3223191a3 100644 --- a/src/runmode-erf-file.c +++ b/src/runmode-erf-file.c @@ -53,7 +53,7 @@ int RunModeErfFileSingle(void) SCEnter(); - if (SCConfGet("erf-file.file", &file) == 0) { + if (SCConfGetNonNull("erf-file.file", &file) == 0) { FatalError("Failed to get erf-file.file from config."); } @@ -110,7 +110,7 @@ int RunModeErfFileAutoFp(void) uint16_t thread; const char *file = NULL; - if (SCConfGet("erf-file.file", &file) == 0) { + if (SCConfGetNonNull("erf-file.file", &file) == 0) { FatalError("Failed retrieving erf-file.file from config"); } diff --git a/src/runmode-pcap-file.c b/src/runmode-pcap-file.c index b5574d30049a..70c06cbc8a5a 100644 --- a/src/runmode-pcap-file.c +++ b/src/runmode-pcap-file.c @@ -55,7 +55,7 @@ int RunModeFilePcapSingle(void) const char *file = NULL; char tname[TM_THREAD_NAME_MAX]; - if (SCConfGet("pcap-file.file", &file) == 0) { + if (SCConfGetNonNull("pcap-file.file", &file) == 0) { FatalError("Failed retrieving pcap-file from Conf"); } @@ -125,7 +125,7 @@ int RunModeFilePcapAutoFp(void) uint16_t thread; const char *file = NULL; - if (SCConfGet("pcap-file.file", &file) == 0) { + if (SCConfGetNonNull("pcap-file.file", &file) == 0) { FatalError("Failed retrieving pcap-file from Conf"); } SCLogDebug("file %s", file); diff --git a/src/runmode-pcap.c b/src/runmode-pcap.c index 80c01cc1158e..2ee868bd1ec8 100644 --- a/src/runmode-pcap.c +++ b/src/runmode-pcap.c @@ -100,7 +100,7 @@ static void *ParsePcapConfig(const char *iface) aconf->checksum_mode = CHECKSUM_VALIDATION_AUTO; aconf->bpf_filter = NULL; - if ((SCConfGet("bpf-filter", &tmpbpf)) == 1) { + if ((SCConfGetNonNull("bpf-filter", &tmpbpf)) == 1) { aconf->bpf_filter = tmpbpf; } diff --git a/src/source-nfq.c b/src/source-nfq.c index 7f75cd171c1b..456bbd54e44c 100644 --- a/src/source-nfq.c +++ b/src/source-nfq.c @@ -215,7 +215,7 @@ void NFQInitConfig(bool quiet) memset(&nfq_config, 0, sizeof(nfq_config)); - if ((SCConfGet("nfq.mode", &nfq_mode)) == 0) { + if ((SCConfGetNonNull("nfq.mode", &nfq_mode)) == 0) { nfq_config.mode = NFQ_ACCEPT_MODE; } else { if (!strcmp("accept", nfq_mode)) { diff --git a/src/source-pcap-file-helper.c b/src/source-pcap-file-helper.c index e88db6d51547..6e49324648c1 100644 --- a/src/source-pcap-file-helper.c +++ b/src/source-pcap-file-helper.c @@ -441,7 +441,7 @@ PcapFileDeleteMode PcapFileParseDeleteMode(void) PcapFileDeleteMode delete_mode = PCAP_FILE_DELETE_NONE; const char *delete_when_done_str = NULL; - if (SCConfGet("pcap-file.delete-when-done", &delete_when_done_str) == 1) { + if (SCConfGetNonNull("pcap-file.delete-when-done", &delete_when_done_str) == 1) { if (strcmp(delete_when_done_str, "non-alerts") == 0) { delete_mode = PCAP_FILE_DELETE_NON_ALERTS; } else { diff --git a/src/source-pcap-file.c b/src/source-pcap-file.c index 808fe27b69cb..47f2b2b4b5ec 100644 --- a/src/source-pcap-file.c +++ b/src/source-pcap-file.c @@ -155,7 +155,7 @@ void PcapFileGlobalInit(void) pcap_g.read_buffer_size = PCAP_FILE_BUFFER_SIZE_DEFAULT; const char *str = NULL; - if (SCConfGet("pcap-file.buffer-size", &str) == 1) { + if (SCConfGetNonNull("pcap-file.buffer-size", &str) == 1) { uint32_t value = 0; if (ParseSizeStringU32(str, &value) < 0) { SCLogWarning("failed to parse pcap-file.buffer-size %s", str); @@ -267,7 +267,7 @@ TmEcode ReceivePcapFileThreadInit(ThreadVars *tv, const void *initdata, void **d } } - if (SCConfGet("bpf-filter", &(tmp_bpf_string)) != 1) { + if (SCConfGetNonNull("bpf-filter", &(tmp_bpf_string)) != 1) { SCLogDebug("could not get bpf or none specified"); } else { ptv->shared.bpf_string = SCStrdup(tmp_bpf_string); @@ -388,7 +388,7 @@ TmEcode ReceivePcapFileThreadInit(ThreadVars *tv, const void *initdata, void **d ptv->behavior.directory = pv; } - if (SCConfGet("pcap-file.checksum-checks", &tmpstring) != 1) { + if (SCConfGetNonNull("pcap-file.checksum-checks", &tmpstring) != 1) { pcap_g.conf_checksum_mode = CHECKSUM_VALIDATION_AUTO; } else { if (strcmp(tmpstring, "auto") == 0) { diff --git a/src/stream-tcp.c b/src/stream-tcp.c index 98a6458f92ca..47ca5a7bd0ed 100644 --- a/src/stream-tcp.c +++ b/src/stream-tcp.c @@ -533,7 +533,7 @@ void StreamTcpInitConfig(bool quiet) } const char *temp_stream_memcap_str; - if (SCConfGet("stream.memcap", &temp_stream_memcap_str) == 1) { + if (SCConfGetNonNull("stream.memcap", &temp_stream_memcap_str) == 1) { uint64_t stream_memcap_copy; if (ParseSizeStringU64(temp_stream_memcap_str, &stream_memcap_copy) < 0) { SCLogError("Error parsing stream.memcap " @@ -585,7 +585,7 @@ void StreamTcpInitConfig(bool quiet) } const char *temp_stream_inline_str; - if (SCConfGet("stream.inline", &temp_stream_inline_str) == 1) { + if (SCConfGetNonNull("stream.inline", &temp_stream_inline_str) == 1) { int inl = 0; /* checking for "auto" and falling back to boolean to provide @@ -705,7 +705,7 @@ void StreamTcpInitConfig(bool quiet) } const char *temp_stream_reassembly_memcap_str; - if (SCConfGet("stream.reassembly.memcap", &temp_stream_reassembly_memcap_str) == 1) { + if (SCConfGetNonNull("stream.reassembly.memcap", &temp_stream_reassembly_memcap_str) == 1) { uint64_t stream_reassembly_memcap_copy; if (ParseSizeStringU64(temp_stream_reassembly_memcap_str, &stream_reassembly_memcap_copy) < 0) { @@ -727,7 +727,7 @@ void StreamTcpInitConfig(bool quiet) } const char *temp_stream_reassembly_depth_str; - if (SCConfGet("stream.reassembly.depth", &temp_stream_reassembly_depth_str) == 1) { + if (SCConfGetNonNull("stream.reassembly.depth", &temp_stream_reassembly_depth_str) == 1) { if (ParseSizeStringU32(temp_stream_reassembly_depth_str, &stream_config.reassembly_depth) < 0) { SCLogError("Error parsing " @@ -754,7 +754,7 @@ void StreamTcpInitConfig(bool quiet) if (randomize) { const char *temp_rdrange; - if (SCConfGet("stream.reassembly.randomize-chunk-range", &temp_rdrange) == 1) { + if (SCConfGetNonNull("stream.reassembly.randomize-chunk-range", &temp_rdrange) == 1) { if (ParseSizeStringU16(temp_rdrange, &rdrange) < 0) { SCLogError("Error parsing " "stream.reassembly.randomize-chunk-range " @@ -769,7 +769,7 @@ void StreamTcpInitConfig(bool quiet) } const char *temp_stream_reassembly_toserver_chunk_size_str; - if (SCConfGet("stream.reassembly.toserver-chunk-size", + if (SCConfGetNonNull("stream.reassembly.toserver-chunk-size", &temp_stream_reassembly_toserver_chunk_size_str) == 1) { if (ParseSizeStringU16(temp_stream_reassembly_toserver_chunk_size_str, &stream_config.reassembly_toserver_chunk_size) < 0) { @@ -791,7 +791,7 @@ void StreamTcpInitConfig(bool quiet) rdrange / 100); } const char *temp_stream_reassembly_toclient_chunk_size_str; - if (SCConfGet("stream.reassembly.toclient-chunk-size", + if (SCConfGetNonNull("stream.reassembly.toclient-chunk-size", &temp_stream_reassembly_toclient_chunk_size_str) == 1) { if (ParseSizeStringU16(temp_stream_reassembly_toclient_chunk_size_str, &stream_config.reassembly_toclient_chunk_size) < 0) { diff --git a/src/suricata.c b/src/suricata.c index e6165b5f5fbf..f49fa4b29259 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -2237,7 +2237,7 @@ static int MayDaemonize(SCInstance *suri) if (suri->daemon == 1 && suri->pid_filename == NULL) { const char *pid_filename; - if (SCConfGet("pid-file", &pid_filename) == 1) { + if (SCConfGetNonNull("pid-file", &pid_filename) == 1) { SCLogInfo("Use pid file %s from config file.", pid_filename); } else { pid_filename = DEFAULT_PID_FILENAME; @@ -2602,7 +2602,7 @@ static int ConfigGetCaptureValue(SCInstance *suri) /* Pull the default packet size from the config, if not found fall * back on a sane default. */ const char *temp_default_packet_size; - if ((SCConfGet("default-packet-size", &temp_default_packet_size)) != 1) { + if ((SCConfGetNonNull("default-packet-size", &temp_default_packet_size)) != 1) { int lthread; int nlive; int strip_trailing_plus = 0; @@ -2739,7 +2739,7 @@ static void PostConfLoadedSetupHostMode(void) { const char *hostmode = NULL; - if (SCConfGet("host-mode", &hostmode) == 1) { + if (SCConfGetNonNull("host-mode", &hostmode) == 1) { if (!strcmp(hostmode, "router")) { g_engine_host_mode = ENGINE_HOST_IS_ROUTER; } else if (!strcmp(hostmode, "bridge")) { @@ -2834,7 +2834,7 @@ int PostConfLoadedSetup(SCInstance *suri) if (suri->checksum_validation == -1) { const char *cv = NULL; - if (SCConfGet("capture.checksum-validation", &cv) == 1) { + if (SCConfGetNonNull("capture.checksum-validation", &cv) == 1) { if (strcmp(cv, "none") == 0) { suri->checksum_validation = 0; } else if (strcmp(cv, "all") == 0) { @@ -2903,7 +2903,7 @@ int PostConfLoadedSetup(SCInstance *suri) /* Suricata will use this umask if provided. By default it will use the umask passed on from the shell. */ const char *custom_umask; - if (SCConfGet("umask", &custom_umask) == 1) { + if (SCConfGetNonNull("umask", &custom_umask) == 1) { uint16_t mask; if (StringParseUint16(&mask, 8, (uint16_t)strlen(custom_umask), custom_umask) > 0) { umask((mode_t)mask); diff --git a/src/tests/stream-tcp.c b/src/tests/stream-tcp.c index cbee60f089de..0c88cd81b196 100644 --- a/src/tests/stream-tcp.c +++ b/src/tests/stream-tcp.c @@ -1074,7 +1074,7 @@ static const char *StreamTcpParseOSPolicy(char *conf_var_name) goto end; } - if (SCConfGet(conf_var_full_name, &conf_var_value) != 1) { + if (SCConfGetNonNull(conf_var_full_name, &conf_var_value) != 1) { SCLogError("Error in getting conf value for conf name %s", conf_var_full_name); goto end; } diff --git a/src/tmqh-flow.c b/src/tmqh-flow.c index 4007533e2c14..665b1feffa7f 100644 --- a/src/tmqh-flow.c +++ b/src/tmqh-flow.c @@ -57,7 +57,7 @@ void TmqhFlowRegister(void) tmqh_table[TMQH_FLOW].RegisterTests = TmqhFlowRegisterTests; const char *scheduler = NULL; - if (SCConfGet("autofp-scheduler", &scheduler) == 1) { + if (SCConfGetNonNull("autofp-scheduler", &scheduler) == 1) { if (strcasecmp(scheduler, "round-robin") == 0) { SCLogNotice("using flow hash instead of round robin"); tmqh_table[TMQH_FLOW].OutHandler = TmqhOutputFlowHash; diff --git a/src/unix-manager.c b/src/unix-manager.c index ab200ef205b3..6885d205348d 100644 --- a/src/unix-manager.c +++ b/src/unix-manager.c @@ -121,7 +121,7 @@ static int UnixNew(UnixCommand * this) TAILQ_INIT(&this->clients); int check_dir = 0; - if (SCConfGet("unix-command.filename", &socketname) == 1) { + if (SCConfGetNonNull("unix-command.filename", &socketname) == 1) { if (PathIsAbsolute(socketname)) { strlcpy(sockettarget, socketname, sizeof(sockettarget)); } else { @@ -888,7 +888,7 @@ static TmEcode UnixManagerConfGetCommand(json_t *cmd, } variable = (char *)json_string_value(jarg); - if (SCConfGet(variable, &confval) != 1) { + if (SCConfGetNonNull(variable, &confval) != 1) { json_object_set_new(server_msg, "message", json_string("Unable to get value")); SCReturnInt(TM_ECODE_FAILED); } diff --git a/src/util-bpf.c b/src/util-bpf.c index bf54819d7633..a58da2ece595 100644 --- a/src/util-bpf.c +++ b/src/util-bpf.c @@ -36,7 +36,7 @@ void ConfSetBPFFilter( } /* command line value has precedence */ - if (SCConfGet("bpf-filter", bpf_filter) == 1) { + if (SCConfGetNonNull("bpf-filter", bpf_filter) == 1) { if (strlen(*bpf_filter) > 0) { SCLogConfig("%s: using command-line provided bpf filter '%s'", iface, *bpf_filter); } diff --git a/src/util-classification-config.c b/src/util-classification-config.c index b4cd39969cbe..9bf9b880a4bb 100644 --- a/src/util-classification-config.c +++ b/src/util-classification-config.c @@ -161,13 +161,13 @@ static const char *SCClassConfGetConfFilename(const DetectEngineCtx *de_ctx) /* try loading prefix setting, fall back to global if that * fails. */ - if (SCConfGet(config_value, &log_filename) != 1) { - if (SCConfGet("classification-file", &log_filename) != 1) { + if (SCConfGetNonNull(config_value, &log_filename) != 1) { + if (SCConfGetNonNull("classification-file", &log_filename) != 1) { log_filename = (char *)SC_CLASS_CONF_DEF_CONF_FILEPATH; } } } else { - if (SCConfGet("classification-file", &log_filename) != 1) { + if (SCConfGetNonNull("classification-file", &log_filename) != 1) { log_filename = (char *)SC_CLASS_CONF_DEF_CONF_FILEPATH; } } diff --git a/src/util-conf.c b/src/util-conf.c index 2ac630f12b82..214d7c433834 100644 --- a/src/util-conf.c +++ b/src/util-conf.c @@ -39,7 +39,7 @@ const char *SCConfigGetLogDirectory(void) { const char *log_dir = NULL; - if (SCConfGet("default-log-dir", &log_dir) != 1) { + if (SCConfGetNonNull("default-log-dir", &log_dir) != 1) { #ifdef OS_WIN32 log_dir = _getcwd(NULL, 0); if (log_dir == NULL) { @@ -86,7 +86,7 @@ const char *ConfigGetDataDirectory(void) { const char *data_dir = NULL; - if (SCConfGet("default-data-dir", &data_dir) != 1) { + if (SCConfGetNonNull("default-data-dir", &data_dir) != 1) { #ifdef OS_WIN32 data_dir = _getcwd(NULL, 0); if (data_dir == NULL) { diff --git a/src/util-daemon.c b/src/util-daemon.c index d191077f910c..35d2e7d1fab5 100644 --- a/src/util-daemon.c +++ b/src/util-daemon.c @@ -131,7 +131,7 @@ void Daemonize (void) FatalError("Error creating new session"); } - if (SCConfGet("daemon-directory", &daemondir) == 1) { + if (SCConfGetNonNull("daemon-directory", &daemondir) == 1) { if ((chdir(daemondir)) < 0) { FatalError("Error changing to working directory"); } diff --git a/src/util-debug.c b/src/util-debug.c index 639de02a1b35..e6028c95a405 100644 --- a/src/util-debug.c +++ b/src/util-debug.c @@ -1451,7 +1451,7 @@ void SCLogLoadConfig(int daemon, int verbose, uint32_t userid, uint32_t groupid) /* Get default log level and format. */ const char *default_log_level_s = NULL; - if (SCConfGet("logging.default-log-level", &default_log_level_s) == 1) { + if (SCConfGetNonNull("logging.default-log-level", &default_log_level_s) == 1) { SCLogLevel default_log_level = SCMapEnumNameToValue(default_log_level_s, sc_log_level_map); if (default_log_level == -1) { @@ -1463,7 +1463,7 @@ void SCLogLoadConfig(int daemon, int verbose, uint32_t userid, uint32_t groupid) sc_lid->global_log_level = MAX(min_level, SC_LOG_NOTICE); } - if (SCConfGet("logging.default-log-format", &sc_lid->global_log_format) != 1) + if (SCConfGetNonNull("logging.default-log-format", &sc_lid->global_log_format) != 1) sc_lid->global_log_format = SCLogGetDefaultLogFormat(sc_lid->global_log_level); (void)SCConfGet("logging.default-output-filter", &sc_lid->op_filter); diff --git a/src/util-landlock.c b/src/util-landlock.c index e028192e5e9a..f7fa2b036670 100644 --- a/src/util-landlock.c +++ b/src/util-landlock.c @@ -208,7 +208,7 @@ void LandlockSandboxing(SCInstance *suri) } if (suri->run_mode == RUNMODE_PCAP_FILE) { const char *pcap_file; - if (SCConfGet("pcap-file.file", &pcap_file) == 1) { + if (SCConfGetNonNull("pcap-file.file", &pcap_file) == 1) { char *file_name = SCStrdup(pcap_file); if (file_name != NULL) { struct stat statbuf; @@ -241,7 +241,7 @@ void LandlockSandboxing(SCInstance *suri) } if (ConfUnixSocketIsEnable()) { const char *socketname; - if (SCConfGet("unix-command.filename", &socketname) == 1) { + if (SCConfGetNonNull("unix-command.filename", &socketname) == 1) { if (PathIsAbsolute(socketname)) { char *file_name = SCStrdup(socketname); if (file_name != NULL) { @@ -257,7 +257,7 @@ void LandlockSandboxing(SCInstance *suri) } if (!suri->sig_file_exclusive) { const char *rule_path; - if (SCConfGet("default-rule-path", &rule_path) == 1 && rule_path) { + if (SCConfGetNonNull("default-rule-path", &rule_path) == 1 && rule_path) { LandlockSandboxingReadPath(ruleset, rule_path); } } diff --git a/src/util-reference-config.c b/src/util-reference-config.c index 92854e4d1861..f4a2a9f6c055 100644 --- a/src/util-reference-config.c +++ b/src/util-reference-config.c @@ -151,13 +151,13 @@ static const char *SCRConfGetConfFilename(const DetectEngineCtx *de_ctx) /* try loading prefix setting, fall back to global if that * fails. */ - if (SCConfGet(config_value, &path) != 1) { - if (SCConfGet("reference-config-file", &path) != 1) { + if (SCConfGetNonNull(config_value, &path) != 1) { + if (SCConfGetNonNull("reference-config-file", &path) != 1) { return (char *)SC_RCONF_DEFAULT_FILE_PATH; } } } else { - if (SCConfGet("reference-config-file", &path) != 1) { + if (SCConfGetNonNull("reference-config-file", &path) != 1) { return (char *)SC_RCONF_DEFAULT_FILE_PATH; } } diff --git a/src/util-rule-vars.c b/src/util-rule-vars.c index 795b1c0e2a61..6e5f2147c383 100644 --- a/src/util-rule-vars.c +++ b/src/util-rule-vars.c @@ -97,7 +97,7 @@ const char *SCRuleVarsGetConfVar(const DetectEngineCtx *de_ctx, } } - if (SCConfGet(conf_var_full_name, &conf_var_full_name_value) != 1) { + if (SCConfGetNonNull(conf_var_full_name, &conf_var_full_name_value) != 1) { SCLogError("Variable \"%s\" is not defined in " "configuration file", conf_var_name); diff --git a/src/util-thash.c b/src/util-thash.c index a006d3cc522b..e6363d944688 100644 --- a/src/util-thash.c +++ b/src/util-thash.c @@ -226,7 +226,7 @@ static int THashInitConfig(THashTableContext *ctx, const char *cnf_prefix) /** set config values for memcap, prealloc and hash_size */ GET_VAR(cnf_prefix, "memcap"); - if ((SCConfGet(varname, &conf_val)) == 1) { + if ((SCConfGetNonNull(varname, &conf_val)) == 1) { uint64_t memcap; if (ParseSizeStringU64(conf_val, &memcap) < 0) { SCLogError("Error parsing %s " @@ -238,14 +238,14 @@ static int THashInitConfig(THashTableContext *ctx, const char *cnf_prefix) SC_ATOMIC_SET(ctx->config.memcap, memcap); } GET_VAR(cnf_prefix, "hash-size"); - if ((SCConfGet(varname, &conf_val)) == 1) { + if ((SCConfGetNonNull(varname, &conf_val)) == 1) { if (StringParseUint32(&configval, 10, (uint16_t)strlen(conf_val), conf_val) > 0) { ctx->config.hash_size = configval; } } GET_VAR(cnf_prefix, "prealloc"); - if ((SCConfGet(varname, &conf_val)) == 1) { + if ((SCConfGetNonNull(varname, &conf_val)) == 1) { if (StringParseUint32(&configval, 10, (uint16_t)strlen(conf_val), conf_val) > 0) { ctx->config.prealloc = configval; } else { diff --git a/src/util-threshold-config.c b/src/util-threshold-config.c index 28a8163dce6a..b1d58242ff8a 100644 --- a/src/util-threshold-config.c +++ b/src/util-threshold-config.c @@ -139,13 +139,13 @@ static const char *SCThresholdConfGetConfFilename(const DetectEngineCtx *de_ctx) /* try loading prefix setting, fall back to global if that * fails. */ - if (SCConfGet(config_value, &log_filename) != 1) { - if (SCConfGet("threshold-file", &log_filename) != 1) { + if (SCConfGetNonNull(config_value, &log_filename) != 1) { + if (SCConfGetNonNull("threshold-file", &log_filename) != 1) { log_filename = (char *)THRESHOLD_CONF_DEF_CONF_FILEPATH; } } } else { - if (SCConfGet("threshold-file", &log_filename) != 1) { + if (SCConfGetNonNull("threshold-file", &log_filename) != 1) { log_filename = (char *)THRESHOLD_CONF_DEF_CONF_FILEPATH; } } From df353242bd1520489dd9b1a0292151626d23bb9f Mon Sep 17 00:00:00 2001 From: Samaresh Kumar Singh Date: Fri, 12 Jun 2026 09:00:56 -0500 Subject: [PATCH 04/23] detect: fail thread init on keyword ctx error DetectEngineThreadCtxInitKeywords returns TM_ECODE_FAILED when a per-thread keyword init fails (for example DetectFilemagicThreadInit), but ThreadCtxDoInit discarded that result and still returned OK. The detect thread then ran with a partially initialized keyword context array, producing indeterminate results. Propagate the failure so the callers abort thread init and clean up. Add a unit test that registers a keyword whose thread init fails and verifies that DetectEngineThreadCtxInit reports the failure. Ticket: #8237 --- src/detect-engine.c | 52 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/src/detect-engine.c b/src/detect-engine.c index 2912eef0be29..b2f1ae47e95f 100644 --- a/src/detect-engine.c +++ b/src/detect-engine.c @@ -3468,8 +3468,8 @@ static TmEcode ThreadCtxDoInit (DetectEngineCtx *de_ctx, DetectEngineThreadCtx * } det_ctx->multi_inspect.to_clear_idx = 0; - - DetectEngineThreadCtxInitKeywords(de_ctx, det_ctx); + if (DetectEngineThreadCtxInitKeywords(de_ctx, det_ctx) != TM_ECODE_OK) + return TM_ECODE_FAILED; DetectEngineThreadCtxInitGlobalKeywords(det_ctx); #ifdef PROFILE_RULES SCProfilingRuleThreadSetup(de_ctx->profile_ctx, det_ctx); @@ -5485,6 +5485,52 @@ static int DetectEngineTest09(void) PASS; } +/** \brief keyword thread-context init stub that always fails, used to mimic a + * failing per-thread keyword init such as DetectFilemagicThreadInit. */ +static void *DetectEngineFailingThreadKeywordInit(void *data) +{ + (void)data; + return NULL; +} + +static void DetectEngineNoopThreadKeywordFree(void *ctx) +{ + (void)ctx; +} + +/** \test Ticket #8237: a failing per-thread keyword init must make the detect + * thread context init fail rather than silently continuing with a partially + * initialized keyword context array. */ +static int DetectEngineThreadCtxInitKeywordFailTest(void) +{ + ThreadVars th_v; + memset(&th_v, 0, sizeof(th_v)); + DetectEngineThreadCtx *det_ctx = NULL; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + Signature *s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (sid:1;)"); + FAIL_IF_NULL(s); + + SigGroupBuild(de_ctx); + + /* Register a keyword whose per-thread init fails (returns NULL). */ + int id = DetectRegisterThreadCtxFuncs(de_ctx, "test_failing_keyword", + DetectEngineFailingThreadKeywordInit, NULL, DetectEngineNoopThreadKeywordFree, 0); + FAIL_IF(id < 0); + + /* Thread context init must report failure and not hand back a context. */ + TmEcode r = DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + FAIL_IF(r != TM_ECODE_FAILED); + FAIL_IF_NOT_NULL(det_ctx); + + DetectEngineCtxFree(de_ctx); + + PASS; +} + #endif void DetectEngineRegisterTests(void) @@ -5496,5 +5542,7 @@ void DetectEngineRegisterTests(void) UtRegisterTest("DetectEngineTest04", DetectEngineTest04); UtRegisterTest("DetectEngineTest08", DetectEngineTest08); UtRegisterTest("DetectEngineTest09", DetectEngineTest09); + UtRegisterTest( + "DetectEngineThreadCtxInitKeywordFailTest", DetectEngineThreadCtxInitKeywordFailTest); #endif } From 565e138754ffc858a007bd07153b2b5a7464d687 Mon Sep 17 00:00:00 2001 From: Samaresh Kumar Singh Date: Fri, 12 Jun 2026 09:28:09 -0500 Subject: [PATCH 05/23] pcap-file: skip setvbuf on non-seekable streams Reading a pcap from /dev/stdin or a named pipe currently fails with "failed to get first packet timestamp. pcap_next_ex(): -1" because InitPcapFile calls setvbuf on the FILE* underlying the pcap handle after libpcap has already consumed the pcap header. On a non-seekable fd glibc cannot recover from that and the next read returns -1. Detect non-regular files via fstat and skip setvbuf in that case so the read keeps working on pipes, fifos and stdin. Accept pcap-file.buffer-size values of 0, which disables setvbuf buffering as an explicit opt-out, or PCAP_FILE_BUFFER_SIZE_MIN (4 KiB) to PCAP_FILE_BUFFER_SIZE_MAX (64 MiB). Treat any non-zero setvbuf return value as an error, not just negative values. When pcap-file.buffer-size fails to parse, retain the default buffer size instead of falling through and setting it to 0. The branches are now mutually exclusive so only one of the parse-error, accepted, or out-of-range messages is logged. Update the user guide: --pcap-file-buffer-size now documents valid values of 0 (disables setvbuf buffering) or 4 KiB to 64 MiB, and pcap-file.rst notes that 0 is the opt-out for non-seekable sources such as stdin and named pipes. Bug: #8464. --- doc/userguide/capture-hardware/pcap-file.rst | 4 ++++ doc/userguide/partials/options.rst | 3 ++- src/source-pcap-file-helper.c | 14 ++++++++++---- src/source-pcap-file.c | 18 ++++++++++++------ 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/doc/userguide/capture-hardware/pcap-file.rst b/doc/userguide/capture-hardware/pcap-file.rst index d5ed628dde36..2d0dde98ca15 100644 --- a/doc/userguide/capture-hardware/pcap-file.rst +++ b/doc/userguide/capture-hardware/pcap-file.rst @@ -33,6 +33,10 @@ This can improve performance, especially for large files. The size can be specified through the command line option, see :ref:`--pcap-file-buffer-size ` +Setting ``buffer-size`` to ``0`` disables ``setvbuf`` buffering. This is the +explicit opt-out for non-seekable sources such as ``/dev/stdin`` or named +pipes, where buffering the underlying file descriptor is not supported. + Directory-related options ------------------------- diff --git a/doc/userguide/partials/options.rst b/doc/userguide/partials/options.rst index 5264db813337..bcbca0f62206 100644 --- a/doc/userguide/partials/options.rst +++ b/doc/userguide/partials/options.rst @@ -103,7 +103,8 @@ .. option:: --pcap-file-buffer-size Set read buffer size using ``setvbuf`` to speed up pcap reading. Valid values - are 4 KiB to 64 MiB. Default value is 128 KiB. Supported on Linux only. + are 0, which disables ``setvbuf`` buffering, or 4 KiB to 64 MiB. Default + value is 128 KiB. Supported on Linux only. .. option:: -i diff --git a/src/source-pcap-file-helper.c b/src/source-pcap-file-helper.c index 6e49324648c1..50bbf7cc0046 100644 --- a/src/source-pcap-file-helper.c +++ b/src/source-pcap-file-helper.c @@ -271,10 +271,16 @@ TmEcode InitPcapFile(PcapFileFileVars *pfv) #if defined(HAVE_SETVBUF) && defined(OS_LINUX) if (pcap_g.read_buffer_size > 0) { - errno = 0; - if (setvbuf(pcap_file(pfv->pcap_handle), pfv->buffer, _IOFBF, pcap_g.read_buffer_size) < - 0) { - SCLogWarning("Failed to setvbuf on PCAP file handle: %s", strerror(errno)); + struct stat sb; + int fd = fileno(pcap_file(pfv->pcap_handle)); + if (fd >= 0 && fstat(fd, &sb) == 0 && !S_ISREG(sb.st_mode)) { + SCLogInfo("%s: skipping setvbuf, underlying fd is not a regular file", pfv->filename); + } else { + errno = 0; + if (setvbuf(pcap_file(pfv->pcap_handle), pfv->buffer, _IOFBF, + pcap_g.read_buffer_size) != 0) { + SCLogWarning("Failed to setvbuf on PCAP file handle: %s", strerror(errno)); + } } } #endif diff --git a/src/source-pcap-file.c b/src/source-pcap-file.c index 47f2b2b4b5ec..83ba9edeba77 100644 --- a/src/source-pcap-file.c +++ b/src/source-pcap-file.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2016 Open Information Security Foundation +/* Copyright (C) 2007-2026 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 @@ -158,13 +158,19 @@ void PcapFileGlobalInit(void) if (SCConfGetNonNull("pcap-file.buffer-size", &str) == 1) { uint32_t value = 0; if (ParseSizeStringU32(str, &value) < 0) { - SCLogWarning("failed to parse pcap-file.buffer-size %s", str); - } - if (value >= PCAP_FILE_BUFFER_SIZE_MIN && value <= PCAP_FILE_BUFFER_SIZE_MAX) { - SCLogInfo("Pcap-file will use %u buffer size", value); + SCLogWarning("failed to parse pcap-file.buffer-size %s; keeping default %u", str, + PCAP_FILE_BUFFER_SIZE_DEFAULT); + } else if (value == 0 || + (value >= PCAP_FILE_BUFFER_SIZE_MIN && value <= PCAP_FILE_BUFFER_SIZE_MAX)) { + if (value == 0) { + SCLogInfo("Pcap-file buffering disabled"); + } else { + SCLogInfo("Pcap-file will use %u buffer size", value); + } pcap_g.read_buffer_size = value; } else { - SCLogWarning("pcap-file.buffer-size value of %u is invalid. Valid range is %u-%u", + SCLogWarning("pcap-file.buffer-size value of %u is invalid. Valid values are 0 to " + "disable buffering, or %u-%u", value, PCAP_FILE_BUFFER_SIZE_MIN, PCAP_FILE_BUFFER_SIZE_MAX); } } From c6014a77a276319efce93e903ccca076329bf6d9 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Fri, 12 Jun 2026 15:03:18 +0200 Subject: [PATCH 06/23] websocket: accepts config value with units Ticket: 8552 As was the commented out example --- rust/src/websocket/websocket.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/rust/src/websocket/websocket.rs b/rust/src/websocket/websocket.rs index f3e3a9bf2255..6dd990c2fa99 100644 --- a/rust/src/websocket/websocket.rs +++ b/rust/src/websocket/websocket.rs @@ -17,7 +17,7 @@ use super::parser; use crate::applayer::{self, *}; -use crate::conf::conf_get; +use crate::conf::{conf_get, get_memval}; use crate::core::{ sc_app_layer_parser_trigger_raw_stream_inspection, ALPROTO_FAILED, ALPROTO_UNKNOWN, IPPROTO_TCP, }; @@ -437,8 +437,15 @@ pub unsafe extern "C" fn SCRegisterWebSocketParser() { } SCLogDebug!("Rust websocket parser registered."); if let Some(val) = conf_get("app-layer.protocols.websocket.max-payload-size") { - if let Ok(v) = val.parse::() { - WEBSOCKET_MAX_PAYLOAD_SIZE = v; + if let Ok(v) = get_memval(val) { + if let Ok(vu) = u32::try_from(v) { + WEBSOCKET_MAX_PAYLOAD_SIZE = vu; + } else { + SCLogError!( + "Too big value for websocket.max-payload-size: max is {}.", + u32::MAX + ); + } } else { SCLogError!("Invalid value for websocket.max-payload-size"); } From 188376b577a6928fe3a92797694dd693b2105b3f Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Tue, 2 Jun 2026 21:25:15 +0200 Subject: [PATCH 07/23] output/filestore: refactor file descriptor handling To assist code analyzers. Gcc -fanalyzer got confused about it. Also test data pointer and length before calling fwrite and check the result better. Use a single atomic for the max open files check. --- src/output-filestore.c | 52 +++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/src/output-filestore.c b/src/output-filestore.c index 84a68aa4e8fc..a5e2a3fa3052 100644 --- a/src/output-filestore.c +++ b/src/output-filestore.c @@ -192,6 +192,9 @@ static void OutputFilestoreFinalizeFiles(ThreadVars *tv, const OutputFilestoreLo } } +/** + * \note `filestore_open_file_cnt` should only be used when FileGetMaxOpenFiles() is non-zero + */ static int OutputFilestoreLogger(ThreadVars *tv, void *thread_data, const Packet *p, File *ff, void *tx, const uint64_t tx_id, const uint8_t *data, uint32_t data_len, uint8_t flags, uint8_t dir) @@ -201,6 +204,7 @@ static int OutputFilestoreLogger(ThreadVars *tv, void *thread_data, const Packet OutputFilestoreCtx *ctx = aft->ctx; char filename[PATH_MAX] = ""; int file_fd = -1; + bool close_file = false; SCLogDebug("ff %p, data %p, data_len %u", ff, data, data_len); @@ -218,18 +222,22 @@ static int OutputFilestoreLogger(ThreadVars *tv, void *thread_data, const Packet return -1; } - if (SC_ATOMIC_GET(filestore_open_file_cnt) < FileGetMaxOpenFiles()) { - SC_ATOMIC_ADD(filestore_open_file_cnt, 1); - ff->fd = file_fd; - } else { - if (FileGetMaxOpenFiles() > 0) { - StatsCounterIncr(&tv->stats, aft->counter_max_hits); - } + /* SC_ATOMIC_ADD returns value before the addition. */ + if (FileGetMaxOpenFiles() > 0 && + SC_ATOMIC_ADD(filestore_open_file_cnt, 1) >= FileGetMaxOpenFiles()) { + (void)SC_ATOMIC_SUB(filestore_open_file_cnt, 1); + StatsCounterIncr(&tv->stats, aft->counter_max_hits); ff->fd = -1; + close_file = true; /* not storing it, so need to close */ + } else if (FileGetMaxOpenFiles() == 0) { + close_file = true; /* max open files 0 means we immediately close */ + } else { + ff->fd = file_fd; } /* we can get called with NULL data when we need to close */ } else if (data != NULL) { - if (ff->fd == -1) { + file_fd = ff->fd; + if (file_fd == -1) { /* construct tmp file path */ char tmp_filename[PATH_MAX] = ""; snprintf(tmp_filename, sizeof(tmp_filename), "file.%u", ff->file_store_id); @@ -242,14 +250,15 @@ static int OutputFilestoreLogger(ThreadVars *tv, void *thread_data, const Packet strerror(errno)); return -1; } - } else { - file_fd = ff->fd; + close_file = true; /* close temporary open */ } + } else if (flags & OUTPUT_FILEDATA_FLAG_CLOSE) { + file_fd = ff->fd; } - if (file_fd != -1) { + if (file_fd != -1 && data != NULL && data_len > 0) { ssize_t r = write(file_fd, (const void *)data, (size_t)data_len); - if (r == -1) { + if (r == -1 || (ssize_t)data_len != r) { /* construct tmp file path */ char tmp_filename[PATH_MAX] = ""; snprintf(tmp_filename, sizeof(tmp_filename), "file.%u", ff->file_store_id); @@ -257,22 +266,23 @@ static int OutputFilestoreLogger(ThreadVars *tv, void *thread_data, const Packet StatsCounterIncr(&tv->stats, aft->fs_error_counter); WARN_ONCE(WOT_WRITE, "Filestore (v2) failed to write to %s: %s", filename, strerror(errno)); - if (ff->fd != -1) { + close_file = true; /* close in the error case */ + } + } + + /* close the open file if needed */ + if (file_fd != -1 && ((flags & OUTPUT_FILEDATA_FLAG_CLOSE) != 0 || close_file)) { + /* if it was stored in the ff, disconnect */ + if (ff->fd != -1) { + if (FileGetMaxOpenFiles()) { SC_ATOMIC_SUB(filestore_open_file_cnt, 1); } ff->fd = -1; } - if (ff->fd == -1) { - close(file_fd); - } + close(file_fd); } if (flags & OUTPUT_FILEDATA_FLAG_CLOSE) { - if (ff->fd != -1) { - close(ff->fd); - ff->fd = -1; - SC_ATOMIC_SUB(filestore_open_file_cnt, 1); - } OutputFilestoreFinalizeFiles(tv, aft, ctx, p, ff, tx, tx_id, dir); } From a5effad9362cc1dfa9b33fea867ac586ca14ca20 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Wed, 3 Jun 2026 10:13:39 +0200 Subject: [PATCH 08/23] frames: avoid possible undefined behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code analyzer flagged FrameCopy as a possible source of UB due to both pointers passed to memcpy being the same. app-layer-frames.c: In function ‘FrameCopy’: app-layer-frames.c:236:5: warning: overlapping buffers passed as arguments to ‘memcpy’ [-Wanalyzer-overlapping-buffers] 236 | memcpy(dst, src, sizeof(*dst)); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ‘FramePrune’: events 1-8 │ │ 750 | static void FramePrune(Frames *frames, const TcpStream *stream, const bool eof) │ | ^~~~~~~~~~ │ | | │ | (1) entry to ‘FramePrune’ │...... │ 766 | for (uint16_t i = 0; i < frames->cnt; i++) { │ | ~~~~~~~~~~~~~~~ │ | | │ | (2) following ‘true’ branch... ─>─┐ │ | │ │ | │ │ |┌─────────────────────────────────────────────────────────────┘ │ 767 |│ if (i < FRAMES_STATIC_CNT) { │ |│ ~ │ |│ | │ |└──────────>(3) ...to here │ | (4) following ‘true’ branch (when ‘i <= 2’)... ─>─┐ │ | │ │ | │ │ |┌─────────────────────────────────────────────────────────────┘ │ 768 |│ Frame *frame = &frames->sframes[i]; │ |│ ~~~~~~~~~~~~~~~~~~ │ |│ | │ |└──────────────────────────────────────────>(5) ...to here │ 769 | FrameDebug("prune(s)", frames, frame); │ 770 | if (eof || FrameIsDone(frame, acked)) { │ | ~ │ | | │ | (6) following ‘false’ branch... ─>─┐ │ | │ │...... │ | │ │ |┌──────────────────────────────────────────────────┘ │ 779 |│ const uint64_t fle = FrameLeftEdge(stream, frame); │ |│ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ │ |│ | │ |└────────────────────────────────────>(7) ...to here │ | (8) calling ‘FrameLeftEdge’ from ‘FramePrune’ │ └──> ‘FrameLeftEdge’: event 9 │ │ 257 | static inline uint64_t FrameLeftEdge(const TcpStream *stream, const Frame *frame) │ | ^~~~~~~~~~~~~ │ | | │ | (9) entry to ‘FrameLeftEdge’ │ ‘FrameLeftEdge’: event 10 │ │suricata-common.h:323:27: │ 323 | #define BUG_ON(x) assert(!(x)) │ | ^~~~~~ │ | | │ | (10) following ‘false’ branch (when ‘frame_offset <= app_progress’)... ─>─┐ │ | │ util-validate.h:95:36: note: in expansion of macro ‘BUG_ON’ │ 95 | #define DEBUG_VALIDATE_BUG_ON(exp) BUG_ON((exp)) │ | ^~~~~~ app-layer-frames.c:266:5: note: in expansion of macro ‘DEBUG_VALIDATE_BUG_ON’ │ 266 | DEBUG_VALIDATE_BUG_ON(frame_offset > app_progress); │ | ^~~~~~~~~~~~~~~~~~~~~ │ ‘FrameLeftEdge’: event 11 │ │ | │ │ |┌────────────────────────────────────────────────────────────────────────────────────────────────────┘ │ 269 |│ if (frame->len < 0) { │ |│ ~~~~~^~~~~ │ |│ | │ |└────────────>(11) ...to here │ <──────┘ │ ‘FramePrune’: events 12-13 │ │ 779 | const uint64_t fle = FrameLeftEdge(stream, frame); │ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ │ | | │ | (12) returning to ‘FramePrune’ from ‘FrameLeftEdge’ │...... │ 783 | FrameCopy(nframe, frame); │ | ~~~~~~~~~~~~~~~~~~~~~~~~ │ | | │ | (13) calling ‘FrameCopy’ from ‘FramePrune’ │ └──> ‘FrameCopy’: events 14-15 │ │ 234 | static void FrameCopy(Frame *dst, Frame *src) │ | ^~~~~~~~~ │ | | │ | (14) entry to ‘FrameCopy’ │ 235 | { │ 236 | memcpy(dst, src, sizeof(*dst)); │ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ │ | | │ | (15) ⚠️ overlapping buffers passed as arguments to ‘memcpy’ │ In file included from suricata-common.h:129, from app-layer-frames.c:25: /usr/include/string.h:47:14: note: the behavior of ‘memcpy’ is undefined for overlapping buffers 47 | extern void *memcpy (void *__restrict __dest, const void *__restrict __src, | ^~~~~~ --- src/app-layer-frames.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/app-layer-frames.c b/src/app-layer-frames.c index 2d79fb463ab6..3962110414fa 100644 --- a/src/app-layer-frames.c +++ b/src/app-layer-frames.c @@ -233,6 +233,7 @@ static void FrameClean(Frame *frame) static void FrameCopy(Frame *dst, Frame *src) { + DEBUG_VALIDATE_BUG_ON(dst == src); memcpy(dst, src, sizeof(*dst)); } @@ -353,8 +354,8 @@ static int FrameSlide(const char *ds, Frames *frames, const TcpStream *stream, c #endif } else { Frame *nframe = &frames->sframes[x]; - FrameCopy(nframe, frame); if (frame != nframe) { + FrameCopy(nframe, frame); FrameClean(frame); } le = MIN(le, FrameLeftEdge(stream, nframe)); @@ -378,8 +379,8 @@ static int FrameSlide(const char *ds, Frames *frames, const TcpStream *stream, c } else { nframe = &frames->sframes[x]; } - FrameCopy(nframe, frame); if (frame != nframe) { + FrameCopy(nframe, frame); FrameClean(frame); } le = MIN(le, FrameLeftEdge(stream, nframe)); @@ -780,8 +781,8 @@ static void FramePrune(Frames *frames, const TcpStream *stream, const bool eof) le = MIN(le, fle); SCLogDebug("le %" PRIu64 ", frame fle %" PRIu64, le, fle); Frame *nframe = &frames->sframes[x]; - FrameCopy(nframe, frame); if (frame != nframe) { + FrameCopy(nframe, frame); FrameClean(frame); } x++; @@ -808,8 +809,8 @@ static void FramePrune(Frames *frames, const TcpStream *stream, const bool eof) } else { nframe = &frames->sframes[x]; } - FrameCopy(nframe, frame); if (frame != nframe) { + FrameCopy(nframe, frame); FrameClean(frame); } x++; From 6e1f96f91a8b25b717c16e7fba05fc14f6cd18c0 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Wed, 3 Jun 2026 12:19:27 +0200 Subject: [PATCH 09/23] tm/queues: assist gcc -fanalyzer Work around TAILQ false positive. --- src/tm-queues.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tm-queues.c b/src/tm-queues.c index 81bbc3042b83..c36f97de4d4c 100644 --- a/src/tm-queues.c +++ b/src/tm-queues.c @@ -27,6 +27,7 @@ #include "threads.h" #include "tm-queues.h" #include "util-debug.h" +#include "util-validate.h" static TAILQ_HEAD(TmqList_, Tmq_) tmq_list = TAILQ_HEAD_INITIALIZER(tmq_list); @@ -83,6 +84,9 @@ void TmqResetQueues(void) while ((tmq = TAILQ_FIRST(&tmq_list))) { TAILQ_REMOVE(&tmq_list, tmq, next); + /* help code checkers to understand what TAILQ_REMOVE does */ + DEBUG_VALIDATE_BUG_ON(TAILQ_FIRST(&tmq_list) == tmq); + if (tmq->name) { SCFree(tmq->name); } From 32414573a7180c9cf6b74a05e04efadc26f35f0a Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Wed, 3 Jun 2026 12:40:51 +0200 Subject: [PATCH 10/23] conf: assist gcc -fanalyzer Work around TAILQ false positive. --- src/conf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/conf.c b/src/conf.c index ff40575d1226..4dc511808136 100644 --- a/src/conf.c +++ b/src/conf.c @@ -161,6 +161,8 @@ void SCConfNodeFree(SCConfNode *node) while ((tmp = TAILQ_FIRST(&node->head))) { TAILQ_REMOVE(&node->head, tmp, next); + /* help code checkers to understand what TAILQ_REMOVE does */ + DEBUG_VALIDATE_BUG_ON(TAILQ_FIRST(&node->head) == tmp); SCConfNodeFree(tmp); } From caa9ddaa26661ee1fd4f1f08a0f9c057c33ffdb3 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Wed, 3 Jun 2026 12:48:04 +0200 Subject: [PATCH 11/23] decode/tcp: only set data ptr for valid option lengths --- src/decode-tcp.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/decode-tcp.c b/src/decode-tcp.c index a7d5ee4b1686..f2cad13d06bf 100644 --- a/src/decode-tcp.c +++ b/src/decode-tcp.c @@ -79,8 +79,7 @@ static void DecodeTCPOptions(Packet *p, const uint8_t *pkt, uint16_t pktlen) } tcp_opts[tcp_opt_cnt].type = type; - tcp_opts[tcp_opt_cnt].len = olen; - tcp_opts[tcp_opt_cnt].data = (olen > 2) ? (pkt+2) : NULL; + tcp_opts[tcp_opt_cnt].len = olen; /* we are parsing the most commonly used opts to prevent * us from having to walk the opts list for these all the @@ -90,6 +89,7 @@ static void DecodeTCPOptions(Packet *p, const uint8_t *pkt, uint16_t pktlen) if (olen != TCP_OPT_WS_LEN) { ENGINE_SET_EVENT(p,TCP_OPT_INVALID_LEN); } else { + tcp_opts[tcp_opt_cnt].data = (pkt + 2); if (p->l4.vars.tcp.wscale_set != 0) { ENGINE_SET_EVENT(p,TCP_OPT_DUPLICATE); } else { @@ -107,6 +107,7 @@ static void DecodeTCPOptions(Packet *p, const uint8_t *pkt, uint16_t pktlen) if (olen != TCP_OPT_MSS_LEN) { ENGINE_SET_EVENT(p,TCP_OPT_INVALID_LEN); } else { + tcp_opts[tcp_opt_cnt].data = (pkt + 2); if (p->l4.vars.tcp.mss_set) { ENGINE_SET_EVENT(p,TCP_OPT_DUPLICATE); } else { @@ -119,6 +120,7 @@ static void DecodeTCPOptions(Packet *p, const uint8_t *pkt, uint16_t pktlen) if (olen != TCP_OPT_SACKOK_LEN) { ENGINE_SET_EVENT(p,TCP_OPT_INVALID_LEN); } else { + tcp_opts[tcp_opt_cnt].data = (pkt + 2); if (TCP_GET_SACKOK(p)) { ENGINE_SET_EVENT(p,TCP_OPT_DUPLICATE); } else { @@ -130,6 +132,7 @@ static void DecodeTCPOptions(Packet *p, const uint8_t *pkt, uint16_t pktlen) if (olen != TCP_OPT_TS_LEN) { ENGINE_SET_EVENT(p,TCP_OPT_INVALID_LEN); } else { + tcp_opts[tcp_opt_cnt].data = (pkt + 2); if (p->l4.vars.tcp.ts_set) { ENGINE_SET_EVENT(p,TCP_OPT_DUPLICATE); } else { @@ -149,6 +152,7 @@ static void DecodeTCPOptions(Packet *p, const uint8_t *pkt, uint16_t pktlen) !((olen - 2) % 8 == 0)) { ENGINE_SET_EVENT(p, TCP_OPT_INVALID_LEN); } else { + tcp_opts[tcp_opt_cnt].data = (pkt + 2); if (p->l4.vars.tcp.sack_set) { ENGINE_SET_EVENT(p,TCP_OPT_DUPLICATE); } else { @@ -166,6 +170,7 @@ static void DecodeTCPOptions(Packet *p, const uint8_t *pkt, uint16_t pktlen) !(((olen - 2) & 0x1) == 0))) { ENGINE_SET_EVENT(p,TCP_OPT_INVALID_LEN); } else { + tcp_opts[tcp_opt_cnt].data = (pkt + 2); if (p->l4.vars.tcp.tfo_set) { ENGINE_SET_EVENT(p,TCP_OPT_DUPLICATE); } else { @@ -178,6 +183,7 @@ static void DecodeTCPOptions(Packet *p, const uint8_t *pkt, uint16_t pktlen) case TCP_OPT_EXP2: SCLogDebug("TCP EXP option, len %u", olen); if (olen == 4 || olen == 12) { + tcp_opts[tcp_opt_cnt].data = (pkt + 2); uint16_t magic = DecodeTCPGetU16(tcp_opts[tcp_opt_cnt].data); if (magic == 0xf989) { if (p->l4.vars.tcp.tfo_set) { @@ -196,6 +202,7 @@ static void DecodeTCPOptions(Packet *p, const uint8_t *pkt, uint16_t pktlen) if (olen != 18) { ENGINE_SET_INVALID_EVENT(p,TCP_OPT_INVALID_LEN); } else { + tcp_opts[tcp_opt_cnt].data = (pkt + 2); /* we can't validate the option as the key is out of band */ p->l4.vars.tcp.md5_option_present = true; } @@ -206,10 +213,18 @@ static void DecodeTCPOptions(Packet *p, const uint8_t *pkt, uint16_t pktlen) if (olen < 4) { ENGINE_SET_INVALID_EVENT(p,TCP_OPT_INVALID_LEN); } else { + tcp_opts[tcp_opt_cnt].data = (pkt + 2); /* we can't validate the option as the key is out of band */ p->l4.vars.tcp.ao_option_present = true; } break; + default: + if (olen > 2) { + tcp_opts[tcp_opt_cnt].data = (pkt + 2); + } else { + + tcp_opts[tcp_opt_cnt].data = NULL; + } } pkt += olen; From 05eac977f07e856f9f7fc9dce94ebcc2672b604d Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Wed, 3 Jun 2026 12:57:18 +0200 Subject: [PATCH 12/23] detect/sigorder: handle allocation failure Addresses a gcc -fanalyzer warning. --- src/detect-engine-loader.c | 5 ++++- src/detect-engine-sigorder.c | 35 ++++++++++++++++++++++------------- src/detect-engine-sigorder.h | 2 +- src/detect-flowint.c | 6 +++--- src/util-unittest-helper.c | 4 +++- 5 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/detect-engine-loader.c b/src/detect-engine-loader.c index ca9cda0ce0e7..038146a946b6 100644 --- a/src/detect-engine-loader.c +++ b/src/detect-engine-loader.c @@ -500,7 +500,10 @@ int SigLoadSignatures(DetectEngineCtx *de_ctx, char *sig_file, bool sig_file_exc } SCSigRegisterSignatureOrderingFuncs(de_ctx); - SCSigOrderSignatures(de_ctx); + if (SCSigOrderSignatures(de_ctx) != 0) { + ret = -1; + goto end; + } SCSigSignatureOrderingModuleCleanup(de_ctx); if (SCThresholdConfInitContext(de_ctx) < 0) { diff --git a/src/detect-engine-sigorder.c b/src/detect-engine-sigorder.c index 9447b527393c..98012cf9d3f2 100644 --- a/src/detect-engine-sigorder.c +++ b/src/detect-engine-sigorder.c @@ -799,13 +799,14 @@ static inline SCSigSignatureWrapper *SCSigAllocSignatureWrapper(Signature *sig) * \param de_ctx Pointer to the Detection Engine Context that holds the * signatures to be ordered */ -void SCSigOrderSignatures(DetectEngineCtx *de_ctx) +int SCSigOrderSignatures(DetectEngineCtx *de_ctx) { if (de_ctx->sig_list == NULL) { SCLogDebug("no signatures to order"); - return; + return 0; } + int retval = 0; SCLogDebug("ordering signatures in memory"); SCSigSignatureWrapper *sigw = NULL; SCSigSignatureWrapper *td_sigw_list = NULL; /* unified td list */ @@ -816,6 +817,12 @@ void SCSigOrderSignatures(DetectEngineCtx *de_ctx) Signature *sig = de_ctx->sig_list; while (sig != NULL) { sigw = SCSigAllocSignatureWrapper(sig); + if (sigw == NULL) { + SCLogError("failed to alloc signature wrapper for rule ordering"); + retval = -1; + goto cleanup; + } + /* Push signature wrapper onto a list, order doesn't matter here. */ if (sig->init_data->firewall_rule) { if (sig->type == SIG_TYPE_PKT) { @@ -851,6 +858,7 @@ void SCSigOrderSignatures(DetectEngineCtx *de_ctx) /* Recreate the sig list in order */ de_ctx->sig_list = NULL; +cleanup: /* firewall list for hook packet_filter */ for (sigw = fw_pf_sigw_list; sigw != NULL;) { SCLogDebug("post-sort packet_filter: sid %u", sigw->sig->id); @@ -901,6 +909,7 @@ void SCSigOrderSignatures(DetectEngineCtx *de_ctx) sigw = sigw->next; SCFree(sigw_to_free); } + return retval; } /** @@ -1059,7 +1068,7 @@ static int SCSigOrderingTest02(void) SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare); - SCSigOrderSignatures(de_ctx); + FAIL_IF(SCSigOrderSignatures(de_ctx) != 0); sig = de_ctx->sig_list; @@ -1198,7 +1207,7 @@ static int SCSigOrderingTest03(void) SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare); - SCSigOrderSignatures(de_ctx); + FAIL_IF(SCSigOrderSignatures(de_ctx) != 0); sig = de_ctx->sig_list; @@ -1313,7 +1322,7 @@ static int SCSigOrderingTest04(void) SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare); - SCSigOrderSignatures(de_ctx); + FAIL_IF(SCSigOrderSignatures(de_ctx) != 0); sig = de_ctx->sig_list; @@ -1411,7 +1420,7 @@ static int SCSigOrderingTest05(void) SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare); - SCSigOrderSignatures(de_ctx); + FAIL_IF(SCSigOrderSignatures(de_ctx) != 0); sig = de_ctx->sig_list; @@ -1500,7 +1509,7 @@ static int SCSigOrderingTest06(void) SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare); - SCSigOrderSignatures(de_ctx); + FAIL_IF(SCSigOrderSignatures(de_ctx) != 0); sig = de_ctx->sig_list; @@ -1587,7 +1596,7 @@ static int SCSigOrderingTest07(void) SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare); - SCSigOrderSignatures(de_ctx); + FAIL_IF(SCSigOrderSignatures(de_ctx) != 0); sig = de_ctx->sig_list; @@ -1687,7 +1696,7 @@ static int SCSigOrderingTest08(void) SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare); - SCSigOrderSignatures(de_ctx); + FAIL_IF(SCSigOrderSignatures(de_ctx) != 0); sig = de_ctx->sig_list; @@ -1793,7 +1802,7 @@ static int SCSigOrderingTest09(void) SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare); - SCSigOrderSignatures(de_ctx); + FAIL_IF(SCSigOrderSignatures(de_ctx) != 0); sig = de_ctx->sig_list; @@ -1897,7 +1906,7 @@ static int SCSigOrderingTest10(void) SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare); - SCSigOrderSignatures(de_ctx); + FAIL_IF(SCSigOrderSignatures(de_ctx) != 0); sig = de_ctx->sig_list; @@ -1965,7 +1974,7 @@ static int SCSigOrderingTest11(void) SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowvarCompare); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPktvarCompare); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByPriorityCompare); - SCSigOrderSignatures(de_ctx); + FAIL_IF(SCSigOrderSignatures(de_ctx) != 0); sig = de_ctx->sig_list; @@ -2056,7 +2065,7 @@ static int SCSigOrderingTest13(void) FAIL_IF_NULL(sig); SCSigRegisterSignatureOrderingFunc(de_ctx, SCSigOrderByFlowbitsCompare); - SCSigOrderSignatures(de_ctx); + FAIL_IF(SCSigOrderSignatures(de_ctx) != 0); #ifdef DEBUG sig = de_ctx->sig_list; diff --git a/src/detect-engine-sigorder.h b/src/detect-engine-sigorder.h index d859846c629e..358aa275c1a2 100644 --- a/src/detect-engine-sigorder.h +++ b/src/detect-engine-sigorder.h @@ -24,7 +24,7 @@ #ifndef SURICATA_DETECT_ENGINE_SIGORDER_H #define SURICATA_DETECT_ENGINE_SIGORDER_H -void SCSigOrderSignatures(DetectEngineCtx *); +int WARN_UNUSED SCSigOrderSignatures(DetectEngineCtx *); void SCSigRegisterSignatureOrderingFuncs(DetectEngineCtx *); void SCSigRegisterSignatureOrderingTests(void); void SCSigSignatureOrderingModuleCleanup(DetectEngineCtx *); diff --git a/src/detect-flowint.c b/src/detect-flowint.c index d83a9529f570..efc105468349 100644 --- a/src/detect-flowint.c +++ b/src/detect-flowint.c @@ -1131,7 +1131,7 @@ static int DetectFlowintTestPacket01Real(void) FAIL_IF(UTHAppendSigs(de_ctx, sigs, 5) == 0); SCSigRegisterSignatureOrderingFuncs(de_ctx); - SCSigOrderSignatures(de_ctx); + FAIL_IF(SCSigOrderSignatures(de_ctx) != 0); SCSigSignatureOrderingModuleCleanup(de_ctx); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&th_v,(void *) de_ctx,(void *) &det_ctx); @@ -1207,7 +1207,7 @@ static int DetectFlowintTestPacket02Real(void) FAIL_IF(UTHAppendSigs(de_ctx, sigs, 5) == 0); SCSigRegisterSignatureOrderingFuncs(de_ctx); - SCSigOrderSignatures(de_ctx); + FAIL_IF(SCSigOrderSignatures(de_ctx) != 0); SCSigSignatureOrderingModuleCleanup(de_ctx); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&th_v,(void *) de_ctx,(void *) &det_ctx); @@ -1280,7 +1280,7 @@ static int DetectFlowintTestPacket03Real(void) FAIL_IF(UTHAppendSigs(de_ctx, sigs, 3) == 0); SCSigRegisterSignatureOrderingFuncs(de_ctx); - SCSigOrderSignatures(de_ctx); + FAIL_IF(SCSigOrderSignatures(de_ctx) != 0); SCSigSignatureOrderingModuleCleanup(de_ctx); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&th_v,(void *) de_ctx,(void *) &det_ctx); diff --git a/src/util-unittest-helper.c b/src/util-unittest-helper.c index 8aa92b226f65..0d26588f19a9 100644 --- a/src/util-unittest-helper.c +++ b/src/util-unittest-helper.c @@ -738,7 +738,9 @@ int UTHMatchPackets(DetectEngineCtx *de_ctx, Packet **p, int num_packets) memset(&th_v, 0, sizeof(th_v)); StatsThreadInit(&th_v.stats); SCSigRegisterSignatureOrderingFuncs(de_ctx); - SCSigOrderSignatures(de_ctx); + if (SCSigOrderSignatures(de_ctx) != 0) { + result = 0; + } SCSigSignatureOrderingModuleCleanup(de_ctx); SigGroupBuild(de_ctx); DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); From ff7fae54d2637e9ea99e154e3114124a5060ccf1 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Wed, 3 Jun 2026 13:54:55 +0200 Subject: [PATCH 13/23] detect/flowvar: help gcc -fanalyzer Add debug validation statement to assert prev pointer is not NULL. --- src/detect-flowvar.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/detect-flowvar.c b/src/detect-flowvar.c index d5396742456f..e1e2397afa02 100644 --- a/src/detect-flowvar.c +++ b/src/detect-flowvar.c @@ -287,17 +287,16 @@ static int DetectFlowvarPostMatch( DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) { - DetectVarList *fs, *prev; - const DetectFlowvarData *fd; - if (det_ctx->varlist == NULL) return 1; - fd = (const DetectFlowvarData *)ctx; + const DetectFlowvarData *fd = (const DetectFlowvarData *)ctx; + DetectVarList *prev = NULL; - prev = NULL; - fs = det_ctx->varlist; + DetectVarList *fs = det_ctx->varlist; while (fs != NULL) { + DetectVarList *next_fs = fs->next; + if (fd->idx == 0 || fd->idx == fs->idx) { SCLogDebug("adding to the flow %u:", fs->idx); //PrintRawDataFp(stdout, fs->buffer, fs->len); @@ -323,17 +322,16 @@ static int DetectFlowvarPostMatch( } if (fs == det_ctx->varlist) { - det_ctx->varlist = fs->next; SCFree(fs); - fs = det_ctx->varlist; + det_ctx->varlist = fs = next_fs; } else { - prev->next = fs->next; + DEBUG_VALIDATE_BUG_ON(prev == NULL); SCFree(fs); - fs = prev->next; + fs = prev->next = next_fs; } } else { prev = fs; - fs = fs->next; + fs = next_fs; } } return 1; From 65f1a022f0e63bd1fb5484da90047a18176d1f1c Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Wed, 3 Jun 2026 14:51:54 +0200 Subject: [PATCH 14/23] detect/ip_proto: clean up parsing function Helps address a gcc -fanalyzer warning. --- src/detect-ipproto.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/detect-ipproto.c b/src/detect-ipproto.c index 7fc05b15e826..22b5624f346b 100644 --- a/src/detect-ipproto.c +++ b/src/detect-ipproto.c @@ -83,13 +83,6 @@ void DetectIPProtoRegister(void) */ static DetectIPProtoData *DetectIPProtoParse(const char *optstr) { - DetectIPProtoData *data = NULL; - char *args[2] = { NULL, NULL }; - int res = 0; - size_t pcre2_len; - int i; - const char *str_ptr; - /* Execute the regex and populate args with captures. */ pcre2_match_data *match = NULL; int ret = DetectParsePcreExec(&parse_regex, &match, optstr, 0, 0); @@ -97,11 +90,19 @@ static DetectIPProtoData *DetectIPProtoParse(const char *optstr) SCLogError("pcre_exec parse error, ret" "%" PRId32 ", string %s", ret, optstr); - goto error; + if (match) { + pcre2_match_data_free(match); + } + return NULL; } - for (i = 0; i < (ret - 1); i++) { - res = pcre2_substring_get_bynumber(match, i + 1, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len); + char *args[2] = { NULL, NULL }; + DetectIPProtoData *data = NULL; + + for (int i = 0; i < 2; i++) { + const char *str_ptr = NULL; + size_t pcre2_len = 0; + int res = pcre2_substring_get_bynumber(match, i + 1, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len); if (res < 0) { SCLogError("pcre2_substring_get_bynumber failed"); goto error; @@ -110,7 +111,7 @@ static DetectIPProtoData *DetectIPProtoParse(const char *optstr) } /* Initialize the data */ - data = SCMalloc(sizeof(DetectIPProtoData)); + data = SCCalloc(1, sizeof(DetectIPProtoData)); if (unlikely(data == NULL)) goto error; data->op = DETECT_IPPROTO_OP_EQ; @@ -125,19 +126,19 @@ static DetectIPProtoData *DetectIPProtoParse(const char *optstr) if (!isdigit((unsigned char)*(args[1]))) { uint8_t proto; if (!SCGetProtoByName(args[1], &proto)) { - SCLogError("Unknown protocol name: \"%s\"", str_ptr); + SCLogError("Unknown protocol name: \"%s\"", args[1]); goto error; } data->proto = proto; } else { if (StringParseUint8(&data->proto, 10, 0, args[1]) <= 0) { - SCLogError("Malformed protocol number: %s", str_ptr); + SCLogError("Malformed protocol number: %s", args[1]); goto error; } } - for (i = 0; i < (ret - 1); i++){ + for (int i = 0; i < 2; i++) { if (args[i] != NULL) pcre2_substring_free((PCRE2_UCHAR8 *)args[i]); } @@ -149,7 +150,7 @@ static DetectIPProtoData *DetectIPProtoParse(const char *optstr) if (match) { pcre2_match_data_free(match); } - for (i = 0; i < (ret - 1) && i < 2; i++){ + for (int i = 0; i < 2; i++) { if (args[i] != NULL) pcre2_substring_free((PCRE2_UCHAR8 *)args[i]); } From e2f92117279f4252e95978209d3d456a56a69d90 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Thu, 4 Jun 2026 10:05:46 +0200 Subject: [PATCH 15/23] log-pcap: address gcc analyzer warnings Reopen file descriptor for lz4 with the init function. This helps code analyzers understand the handle it's leaked. Improve flow of profiling dumps to avoid analyzer confusion around the file descriptor. Suppress TAILQ related warnings. --- src/log-pcap.c | 68 +++++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/src/log-pcap.c b/src/log-pcap.c index 077fd9f6a481..1468cd144780 100644 --- a/src/log-pcap.c +++ b/src/log-pcap.c @@ -129,11 +129,11 @@ typedef struct PcapLogCompressionData_ { #ifdef HAVE_LIBLZ4 LZ4F_compressionContext_t lz4f_context; LZ4F_preferences_t lz4f_prefs; + FILE *pcap_buf_wrapper; #endif /* HAVE_LIBLZ4 */ FILE *file; uint8_t *pcap_buf; uint64_t pcap_buf_size; - FILE *pcap_buf_wrapper; uint64_t bytes_in_block; } PcapLogCompressionData; @@ -277,15 +277,7 @@ static int PcapLogCloseFile(ThreadVars *t, PcapLogData *pl) #ifdef HAVE_LIBLZ4 PcapLogCompressionData *comp = &pl->compression; if (comp->format == PCAP_LOG_COMPRESSION_FORMAT_LZ4) { - /* pcap_dump_close() has closed its output ``file'', - * so we need to call fmemopen again. */ - - comp->pcap_buf_wrapper = SCFmemopen(comp->pcap_buf, - comp->pcap_buf_size, "w"); - if (comp->pcap_buf_wrapper == NULL) { - SCLogError("SCFmemopen failed: %s", strerror(errno)); - return TM_ECODE_FAILED; - } + comp->pcap_buf_wrapper = NULL; } #endif /* HAVE_LIBLZ4 */ } @@ -367,6 +359,7 @@ static int PcapLogRotateFile(ThreadVars *t, PcapLogData *pl) } TAILQ_REMOVE(&pl->pcap_file_list, pf, next); + DEBUG_VALIDATE_BUG_ON(TAILQ_FIRST(&pl->pcap_file_list) == pf); PcapFileNameFree(pf); pl->file_cnt--; } @@ -442,6 +435,12 @@ static int PcapLogOpenHandles(PcapLogData *pl, const Packet *p) pl->fopen_err = 0; } + comp->pcap_buf_wrapper = SCFmemopen(comp->pcap_buf, comp->pcap_buf_size, "w"); + if (comp->pcap_buf_wrapper == NULL) { + fclose(comp->file); + comp->file = NULL; + return TM_ECODE_FAILED; + } if ((pl->pcap_dumper = pcap_dump_fopen(pl->pcap_dead_handle, comp->pcap_buf_wrapper)) == NULL) { if (!pl->pcap_open_err) { @@ -450,6 +449,8 @@ static int PcapLogOpenHandles(PcapLogData *pl, const Packet *p) } fclose(comp->file); comp->file = NULL; + fclose(comp->pcap_buf_wrapper); + comp->pcap_buf_wrapper = NULL; return TM_ECODE_FAILED; } else { pl->pcap_open_err = false; @@ -1015,6 +1016,7 @@ static TmEcode PcapLogInitRingBuffer(PcapLogData *pl) PcapFileName *pf = TAILQ_FIRST(&pl->pcap_file_list); while (pf != NULL && pl->file_cnt > pl->max_files) { TAILQ_REMOVE(&pl->pcap_file_list, pf, next); + DEBUG_VALIDATE_BUG_ON(TAILQ_FIRST(&pl->pcap_file_list) == pf); SCLogDebug("Removing PCAP file %s", pf->filename); if (remove(pf->filename) != 0) { @@ -1166,6 +1168,7 @@ static void PcapLogDataFree(PcapLogData *pl) PcapFileName *pf; while ((pf = TAILQ_FIRST(&pl->pcap_file_list)) != NULL) { TAILQ_REMOVE(&pl->pcap_file_list, pf, next); + DEBUG_VALIDATE_BUG_ON(TAILQ_FIRST(&pl->pcap_file_list) == pf); PcapFileNameFree(pf); } if (pl == g_pcap_data) { @@ -1191,7 +1194,8 @@ static void PcapLogDataFree(PcapLogData *pl) #ifdef HAVE_LIBLZ4 if (pl->compression.format == PCAP_LOG_COMPRESSION_FORMAT_LZ4) { SCFree(pl->compression.buffer); - fclose(pl->compression.pcap_buf_wrapper); + if (pl->compression.pcap_buf_wrapper) + fclose(pl->compression.pcap_buf_wrapper); SCFree(pl->compression.pcap_buf); LZ4F_errorCode_t errcode = LZ4F_freeCompressionContext(pl->compression.lz4f_context); @@ -1217,7 +1221,7 @@ static TmEcode PcapLogDataDeinit(ThreadVars *t, void *thread_data) PcapLogData *pl = td->pcap_log; if (pl->pcap_dumper != NULL) { - if (PcapLogCloseFile(t,pl) < 0) { + if (PcapLogCloseFile(t, pl) != TM_ECODE_OK) { SCLogDebug("PcapLogCloseFile failed"); } } @@ -1482,7 +1486,9 @@ static OutputInitResult PcapLogInitCtx(SCConfNode *conf) comp->file = NULL; comp->pcap_buf = NULL; comp->pcap_buf_size = 0; +#ifdef HAVE_LIBLZ4 comp->pcap_buf_wrapper = NULL; +#endif } else if (strcmp(compression_str, "lz4") == 0) { #ifdef HAVE_LIBLZ4 pl->compression.format = PCAP_LOG_COMPRESSION_FORMAT_LZ4; @@ -1849,7 +1855,7 @@ static void FormatNumber(uint64_t num, char *str, size_t size) snprintf(str, size, "%3.1fb", (float)num/1000000000UL); } -static void ProfileReportPair(FILE *fp, const char *name, PcapLogProfileData *p) +static void ProfileReportPair(FILE *fp, const char *name, const PcapLogProfileData *p) { char ticks_str[32] = "n/a"; char cnt_str[32] = "n/a"; @@ -1863,7 +1869,7 @@ static void ProfileReportPair(FILE *fp, const char *name, PcapLogProfileData *p) fprintf(fp, "%-28s %-10s %-10s %-10s\n", name, cnt_str, avg_str, ticks_str); } -static void ProfileReport(FILE *fp, PcapLogData *pl) +static void ProfileReport(FILE *fp, const PcapLogData *pl) { ProfileReportPair(fp, "open", &pl->profile_open); ProfileReportPair(fp, "close", &pl->profile_close); @@ -1886,23 +1892,8 @@ static void FormatBytes(uint64_t num, char *str, size_t size) snprintf(str, size, "%3.1fGiB", (float)num/1000000000UL); } -static void PcapLogProfilingDump(PcapLogData *pl) +static void DoDump(const PcapLogData *pl, FILE *fp) { - FILE *fp = NULL; - - if (profiling_pcaplog_enabled == 0) - return; - - if (profiling_pcaplog_output_to_file == 1) { - fp = fopen(profiling_pcaplog_file_name, profiling_pcaplog_file_mode); - if (fp == NULL) { - SCLogError("failed to open %s: %s", profiling_pcaplog_file_name, strerror(errno)); - return; - } - } else { - fp = stdout; - } - /* counters */ fprintf(fp, "\n\nOperation Cnt Avg ticks Total ticks\n"); fprintf(fp, "---------------------------- ---------- ---------- -----------\n"); @@ -1942,9 +1933,24 @@ static void PcapLogProfilingDump(PcapLogData *pl) if (ticks_per_gib > 0) FormatNumber(ticks_per_gib, ticks_per_gib_str, sizeof(ticks_per_gib_str)); fprintf(fp, " Ticks per GiB: %s\n", ticks_per_gib_str); +} - if (fp != stdout) +static void PcapLogProfilingDump(PcapLogData *pl) +{ + if (profiling_pcaplog_enabled == 0) + return; + + if (profiling_pcaplog_output_to_file == 1) { + FILE *fp = fopen(profiling_pcaplog_file_name, profiling_pcaplog_file_mode); + if (fp == NULL) { + SCLogError("failed to open %s: %s", profiling_pcaplog_file_name, strerror(errno)); + return; + } + DoDump(pl, fp); fclose(fp); + } else { + DoDump(pl, stdout); + } } void PcapLogProfileSetup(void) From f9cf9c1de72d790bdea97dda9ba3c207d793a410 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Thu, 4 Jun 2026 10:06:03 +0200 Subject: [PATCH 16/23] output: suppress gcc analyzer warnings By teaching about TAILQ. --- src/output.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/output.c b/src/output.c index e9f76f4e79d9..cdecfe519fdb 100644 --- a/src/output.c +++ b/src/output.c @@ -656,6 +656,7 @@ void OutputDeregisterAll(void) while ((module = TAILQ_FIRST(&output_modules))) { TAILQ_REMOVE(&output_modules, module, entries); + DEBUG_VALIDATE_BUG_ON(TAILQ_FIRST(&output_modules) == module); SCFree(module); } SCFree(simple_json_applayer_loggers); @@ -898,6 +899,7 @@ void OutputClearActiveLoggers(void) RootLogger *logger; while ((logger = TAILQ_FIRST(&active_loggers)) != NULL) { TAILQ_REMOVE(&active_loggers, logger, entries); + DEBUG_VALIDATE_BUG_ON(TAILQ_FIRST(&active_loggers) == logger); SCFree(logger); } } From 5179af55574d6acce7c1fa17b74221cc839cbd40 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Thu, 4 Jun 2026 10:06:23 +0200 Subject: [PATCH 17/23] affinity: gcc analyzer warnings The double strchr confused gcc -fanalyzer. --- src/util-affinity.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util-affinity.c b/src/util-affinity.c index cba5aec1d60a..9edd601c4f27 100644 --- a/src/util-affinity.c +++ b/src/util-affinity.c @@ -229,6 +229,7 @@ int BuildCpusetWithCallback( { SCConfNode *lnode; TAILQ_FOREACH(lnode, &node->head, next) { + char *sep = NULL; uint32_t i; uint32_t a, b; uint32_t stop = 0; @@ -240,8 +241,7 @@ int BuildCpusetWithCallback( a = 0; b = max; stop = 1; - } else if (strchr(lnode->val, '-') != NULL) { - char *sep = strchr(lnode->val, '-'); + } else if ((sep = strchr(lnode->val, '-')) != NULL) { if (StringParseUint32(&a, 10, sep - lnode->val, lnode->val) <= 0) { SCLogError("%s: invalid cpu range (start invalid): \"%s\"", name, lnode->val); return -1; From 32fc437568ed2d38c8bd57aa9438dc32773d9845 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Thu, 4 Jun 2026 10:33:26 +0200 Subject: [PATCH 18/23] spm/bm: match suff array to pattern size Avoids gcc -fanalyzer warning about out of bounds write to the array. --- src/util-spm-bm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util-spm-bm.c b/src/util-spm-bm.c index bfdb28600aa3..850662e42fa0 100644 --- a/src/util-spm-bm.c +++ b/src/util-spm-bm.c @@ -184,7 +184,7 @@ static void BoyerMooreSuffixes(const uint8_t *x, uint16_t m, uint16_t *suff) static int PreBmGs(const uint8_t *x, uint16_t m, uint16_t *bmGs) { int32_t i, j; - uint16_t suff[m + 1]; + uint16_t suff[m]; BoyerMooreSuffixes(x, m, suff); @@ -260,7 +260,7 @@ static void BoyerMooreSuffixesNocase(const uint8_t *x, uint16_t m, static void PreBmGsNocase(const uint8_t *x, uint16_t m, uint16_t *bmGs) { uint16_t i, j; - uint16_t suff[m + 1]; + uint16_t suff[m]; BoyerMooreSuffixesNocase(x, m, suff); From f434732c73d785a9e8a68f440f11437fad6e1e5c Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Thu, 4 Jun 2026 10:34:51 +0200 Subject: [PATCH 19/23] util/var-name: help gcc analyzer Help it understand TAILQ. --- src/util-var-name.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util-var-name.c b/src/util-var-name.c index a24c9f4874de..f62675950f09 100644 --- a/src/util-var-name.c +++ b/src/util-var-name.c @@ -127,6 +127,7 @@ void VarNameStoreDestroy(void) while ((s = TAILQ_FIRST(&free_list))) { TAILQ_REMOVE(&free_list, s, next); + DEBUG_VALIDATE_BUG_ON(TAILQ_FIRST(&free_list) == s); HashListTableFree(s->names); HashListTableFree(s->ids); SCFree(s); From 79ef58be12a3be48ab6312f4468d8597ec2fa578 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Thu, 4 Jun 2026 13:03:36 +0200 Subject: [PATCH 20/23] mpm/hs: remove useless pointer check Pointer can't be NULL, so don't check it. Helps gcc analyzer as well. --- src/util-mpm-hs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/util-mpm-hs.c b/src/util-mpm-hs.c index 3b77a4de44c5..a8b1e2d0ac22 100644 --- a/src/util-mpm-hs.c +++ b/src/util-mpm-hs.c @@ -775,7 +775,8 @@ int SCHSPreparePatterns(MpmConfig *mpm_conf, MpmCtx *mpm_ctx) } const char *cache_path = pd->no_cache || !mpm_conf ? NULL : mpm_conf->cache_dir_path; - if (PatternDatabaseGetCached(&pd, cd, cache_path) == 0 && pd != NULL) { + if (PatternDatabaseGetCached(&pd, cd, cache_path) == 0) { + DEBUG_VALIDATE_BUG_ON(pd == NULL); cd = NULL; ctx->pattern_db = pd; if (PatternDatabaseGetSize(pd, &ctx->hs_db_size) != 0) { From 7cb64cc20f0002be7c21e53528aa0a02abdbea5d Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Thu, 4 Jun 2026 15:15:16 +0200 Subject: [PATCH 21/23] nfq: suppress gcc analyzer warnings --- src/source-nfq.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/source-nfq.c b/src/source-nfq.c index 456bbd54e44c..c5dbe8629156 100644 --- a/src/source-nfq.c +++ b/src/source-nfq.c @@ -567,6 +567,7 @@ static int NFQCallBack(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, if (ret == -1) { #ifdef COUNTERS NFQQueueVars *q = NFQGetQueue(ntv->nfq_index); + DEBUG_VALIDATE_BUG_ON(q == NULL); q->errs++; q->pkts++; q->bytes += GET_PKT_LEN(p); @@ -976,6 +977,7 @@ static void NFQRecvPkt(NFQQueueVars *t, NFQThreadVars *tv) { int ret; int flag = NFQVerdictCacheLen(t) ? MSG_DONTWAIT : 0; + DEBUG_VALIDATE_BUG_ON(t == NULL); int rv = recv(t->fd, tv->data, tv->datalen, flag); if (rv < 0) { @@ -1049,6 +1051,7 @@ void ReceiveNFQThreadExitStats(ThreadVars *tv, void *data) { NFQThreadVars *ntv = (NFQThreadVars *)data; NFQQueueVars *nq = NFQGetQueue(ntv->nfq_index); + DEBUG_VALIDATE_BUG_ON(nq == NULL); #ifdef COUNTERS SCLogNotice("(%s) Treated: Pkts %" PRIu32 ", Bytes %" PRIu64 ", Errors %" PRIu32 "", tv->name, nq->pkts, nq->bytes, nq->errs); From 1eff94f2b2294166e7744880fb09b49eeb3e8c74 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Thu, 4 Jun 2026 15:16:02 +0200 Subject: [PATCH 22/23] github-ci: add gcc analyzer build Make sure to not run against lua rust crate build, as it's not clean. --- .github/workflows/scan-build.yml | 60 ++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/.github/workflows/scan-build.yml b/.github/workflows/scan-build.yml index 49691d2d1934..990f0fa97a9c 100644 --- a/.github/workflows/scan-build.yml +++ b/.github/workflows/scan-build.yml @@ -165,3 +165,63 @@ jobs: name: scan-build-results path: scan-build-report/ retention-days: 5 + gcc-analyzer: + name: GCC analyzer + runs-on: ubuntu-latest + container: ubuntu:26.04 + steps: + - name: Cache scan-build + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb + with: + path: ~/.cargo + key: scan-build + + - name: Install system packages + run: | + apt update + apt -y install \ + libpcre2-dev \ + build-essential \ + autoconf \ + automake \ + cargo \ + cbindgen \ + dpdk-dev \ + gcc-16 \ + git \ + libtool \ + libpcap-dev \ + libnet1-dev \ + libyaml-0-2 \ + libyaml-dev \ + libcap-ng-dev \ + libcap-ng0 \ + libmagic-dev \ + libnetfilter-log-dev \ + libnetfilter-queue-dev \ + libnetfilter-queue1 \ + libnfnetlink-dev \ + libnfnetlink0 \ + libnuma-dev \ + libhiredis-dev \ + libhyperscan-dev \ + libjansson-dev \ + libevent-dev \ + libevent-pthreads-2.1-7 \ + liblz4-dev \ + make \ + python3-yaml \ + rustc \ + software-properties-common \ + zlib1g \ + zlib1g-dev + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + - run: git config --global --add safe.directory /__w/suricata/suricata + - run: ./scripts/bundle.sh + - run: ./autogen.sh + - run: ./configure --enable-warnings --enable-dpdk --enable-nfqueue --enable-nflog --enable-debug-validation + env: + CC: gcc-16 + CFLAGS: "-fanalyzer -Werror" + SURICATA_LUA_SYS_CFLAGS: "" + - run: make From 28b10fb0429f42b1f1cfdb3bcb4ea634e3cbaab3 Mon Sep 17 00:00:00 2001 From: Abhijeet Singh Date: Thu, 2 Apr 2026 15:16:05 -0700 Subject: [PATCH 23/23] util/log: rotate log file periodically Fix log file not rotating during zero traffic periods by triggering rotation logic every second Ticket: https://redmine.openinfosecfoundation.org/issues/8115 --- src/Makefile.am | 4 +- src/{log-flush.c => log-maintenance.c} | 57 +++++++----- src/{log-flush.h => log-maintenance.h} | 10 +-- src/runmodes.c | 6 +- src/util-logopenfile.c | 115 ++++++++++++++----------- src/util-logopenfile.h | 14 +-- 6 files changed, 118 insertions(+), 88 deletions(-) rename src/{log-flush.c => log-maintenance.c} (59%) rename src/{log-flush.h => log-maintenance.h} (77%) diff --git a/src/Makefile.am b/src/Makefile.am index 7d6d0ebf0c02..121df60ffd1f 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -343,7 +343,7 @@ noinst_HEADERS = \ ippair-storage.h \ ippair-timeout.h \ ippair.h \ - log-flush.h \ + log-maintenance.h \ log-pcap.h \ log-stats.h \ log-tcp-data.h \ @@ -921,7 +921,7 @@ libsuricata_c_a_SOURCES = \ ippair-storage.c \ ippair-timeout.c \ ippair.c \ - log-flush.c \ + log-maintenance.c \ log-pcap.c \ log-stats.c \ log-tcp-data.c \ diff --git a/src/log-flush.c b/src/log-maintenance.c similarity index 59% rename from src/log-flush.c rename to src/log-maintenance.c index 7022e1359bf9..170103754b48 100644 --- a/src/log-flush.c +++ b/src/log-maintenance.c @@ -23,13 +23,12 @@ #include "suricata-common.h" #include "suricata.h" -#include "log-flush.h" +#include "log-maintenance.h" #include "util-logopenfile.h" #include "tm-threads.h" #include "conf.h" #include "conf-yaml-loader.h" #include "util-privs.h" -#include "util-logopenfile.h" int OutputFlushInterval(void) { @@ -45,20 +44,26 @@ int OutputFlushInterval(void) return (int)output_flush_interval; } -static void *LogFlusherWakeupThread(void *arg) +static void *LogMaintenanceThread(void *arg) { int output_flush_interval = OutputFlushInterval(); - /* This was checked by the logic creating this thread */ - BUG_ON(output_flush_interval == 0); - SCLogConfig("Using output-flush-interval of %d seconds", output_flush_interval); + if (output_flush_interval > 0) { + SCLogConfig("Log maintenance thread started: rotation check every 1s, flush interval %ds", + output_flush_interval); + } else { + SCLogConfig("Log maintenance thread started: rotation check every 1s, flush disabled"); + } + /* * Calculate the number of sleep intervals based on the output flush interval. This is necessary * because this thread pauses a fixed amount of time to react to shutdown situations more * quickly. */ - const int log_flush_sleep_time = 500; /* milliseconds */ - const int flush_wait_count = (1000 * output_flush_interval) / log_flush_sleep_time; + const int maintenance_sleep_time = 500; /* milliseconds */ + const int rotation_wait_count = 1000 / maintenance_sleep_time; /* = 2, check every 1 second */ + const int flush_wait_count = + output_flush_interval > 0 ? (1000 * output_flush_interval) / maintenance_sleep_time : 0; ThreadVars *tv_local = (ThreadVars *)arg; SCSetThreadName(tv_local->name); @@ -72,16 +77,26 @@ static void *LogFlusherWakeupThread(void *arg) TmThreadsSetFlag(tv_local, THV_INIT_DONE | THV_RUNNING); - int wait_count = 0; + int rotation_counter = 0; + int flush_counter = 0; + uint64_t rotation_check_count = 0; uint64_t worker_flush_count = 0; bool run = TmThreadsWaitForUnpause(tv_local); while (run) { - SleepMsec(log_flush_sleep_time); + SleepMsec(maintenance_sleep_time); - if (++wait_count == flush_wait_count) { + /* Check rotation every 1 second */ + if (++rotation_counter >= rotation_wait_count) { + rotation_check_count++; + LogFileRotateAll(); + rotation_counter = 0; + } + + /* Flush at configured interval (if enabled) */ + if (flush_wait_count > 0 && ++flush_counter >= flush_wait_count) { worker_flush_count++; LogFileFlushAll(); - wait_count = 0; + flush_counter = 0; } if (TmThreadsCheckFlag(tv_local, THV_KILL)) { @@ -92,20 +107,16 @@ static void *LogFlusherWakeupThread(void *arg) TmThreadsSetFlag(tv_local, THV_RUNNING_DONE); TmThreadWaitForFlag(tv_local, THV_DEINIT); TmThreadsSetFlag(tv_local, THV_CLOSED); - SCLogInfo("%s: initiated %" PRIu64 " flushes", tv_local->name, worker_flush_count); + SCLogInfo("%s: performed %" PRIu64 " rotation checks, %" PRIu64 " flushes", tv_local->name, + rotation_check_count, worker_flush_count); return NULL; } -void LogFlushThreads(void) +void LogMaintenanceThreadSpawn(void) { - if (0 == OutputFlushInterval()) { - SCLogConfig("log flusher thread not used with heartbeat.output-flush-interval of 0"); - return; - } - - ThreadVars *tv_log_flush = - TmThreadCreateMgmtThread(thread_name_heartbeat, LogFlusherWakeupThread, 1); - if (!tv_log_flush || (TmThreadSpawn(tv_log_flush) != 0)) { - FatalError("Unable to create and start log flush thread"); + ThreadVars *tv_maintenance = + TmThreadCreateMgmtThread(thread_name_heartbeat, LogMaintenanceThread, 1); + if (!tv_maintenance || (TmThreadSpawn(tv_maintenance) != 0)) { + FatalError("Unable to create and start log maintenance thread"); } } diff --git a/src/log-flush.h b/src/log-maintenance.h similarity index 77% rename from src/log-flush.h rename to src/log-maintenance.h index 8676e75745ba..61728d8d542e 100644 --- a/src/log-flush.h +++ b/src/log-maintenance.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 Open Information Security Foundation +/* Copyright (C) 2026 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 @@ -20,8 +20,8 @@ * * \author Jeff Lucovsky */ -#ifndef SURICATA_LOG_FLUSH_H__ -#define SURICATA_LOG_FLUSH_H__ -void LogFlushThreads(void); +#ifndef SURICATA_LOG_MAINTENANCE_H__ +#define SURICATA_LOG_MAINTENANCE_H__ +void LogMaintenanceThreadSpawn(void); int OutputFlushInterval(void); -#endif /* SURICATA_LOG_FLUSH_H__ */ +#endif /* SURICATA_LOG_MAINTENANCE_H__ */ diff --git a/src/runmodes.c b/src/runmodes.c index ddf12557ea22..c935b914ca46 100644 --- a/src/runmodes.c +++ b/src/runmodes.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2024 Open Information Security Foundation +/* Copyright (C) 2007-2026 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 @@ -28,7 +28,7 @@ #include "util-debug.h" #include "util-affinity.h" #include "conf.h" -#include "log-flush.h" +#include "log-maintenance.h" #include "runmodes.h" #include "runmode-af-packet.h" #include "runmode-af-xdp.h" @@ -451,7 +451,7 @@ void RunModeDispatch(int runmode, const char *custom_mode, const char *capture_p BypassedFlowManagerThreadSpawn(); } StatsSpawnThreads(); - LogFlushThreads(); + LogMaintenanceThreadSpawn(); TmThreadsSealThreads(); } } diff --git a/src/util-logopenfile.c b/src/util-logopenfile.c index 90b42c3b7b95..8386d455e9ba 100644 --- a/src/util-logopenfile.c +++ b/src/util-logopenfile.c @@ -33,7 +33,7 @@ #include "util-path.h" #include "util-misc.h" #include "util-time.h" -#include "log-flush.h" +#include "log-maintenance.h" #if defined(HAVE_SYS_UN_H) && defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SYS_TYPES_H) #define BUILD_WITH_UNIXSOCKET @@ -54,10 +54,9 @@ static bool LogFileNewThreadedCtx(LogFileCtx *parent_ctx, const char *log_path, // Threaded eve.json identifier static SC_ATOMIC_DECL_AND_INIT_WITH_VAL(uint16_t, eve_file_id, 1); -/* Flush list for heartbeat-triggered flushing */ -static SCMutex log_file_flush_mutex = SCMUTEX_INITIALIZER; -static TAILQ_HEAD(, LogFileFlushEntry_) log_file_flush_list = TAILQ_HEAD_INITIALIZER( - log_file_flush_list); +/* Log file list for heartbeat-triggered flushing and rotation */ +static SCMutex log_file_list_mutex = SCMUTEX_INITIALIZER; +static TAILQ_HEAD(, LogFileEntry_) log_file_list = TAILQ_HEAD_INITIALIZER(log_file_list); #ifdef BUILD_WITH_UNIXSOCKET /** \brief connect to the indicated local stream socket, logging any errors @@ -275,6 +274,20 @@ static int SCLogFileWriteNoLock(const char *buffer, int buffer_len, LogFileCtx * return ret; } +/** + * \brief Check and perform log file rotation if needed. + * + * Called by the maintenance thread to drive rotation independently of + * traffic-driven writes. Takes the file lock so it is safe to run + * concurrently with worker threads writing to the same context. + */ +static void SCLogFileRotate(LogFileCtx *log_ctx) +{ + OutputWriteLock(&log_ctx->fp_mutex); + HandleLogRotation(log_ctx); + SCMutexUnlock(&log_ctx->fp_mutex); +} + /** * \brief Write buffer to log file. * \retval 0 on failure; otherwise, the return value of fwrite (number of @@ -652,9 +665,9 @@ int SCConfLogOpenGeneric( if (rotate) { OutputRegisterFileRotationFlag(&log_ctx->rotation_flag); } - /* Register non-threaded regular files for direct heartbeat flushing */ + /* Register non-threaded regular files for heartbeat maintenance */ if (!log_ctx->threaded && log_ctx->is_regular) { - LogFileRegisterForFlush(log_ctx); + LogFileRegister(log_ctx); } } else { SCLogError("Invalid entry for " @@ -730,6 +743,7 @@ LogFileCtx *LogFileNewCtx(void) lf_ctx->Write = SCLogFileWrite; lf_ctx->Close = SCLogFileClose; lf_ctx->Flush = SCLogFileFlush; + lf_ctx->Rotate = SCLogFileRotate; return lf_ctx; } @@ -898,15 +912,11 @@ static bool LogFileNewThreadedCtx(LogFileCtx *parent_ctx, const char *log_path, goto error; } thread->is_regular = true; - if (OutputFlushInterval() > 0) { - thread->Write = SCLogFileWrite; - thread->Close = SCLogFileClose; - } else { - thread->Write = SCLogFileWriteNoLock; - thread->Close = SCLogFileCloseNoLock; - } + thread->Write = SCLogFileWrite; + thread->Close = SCLogFileClose; + thread->Rotate = SCLogFileRotate; OutputRegisterFileRotationFlag(&thread->rotation_flag); - LogFileRegisterForFlush(thread); + LogFileRegister(thread); } else if (parent_ctx->type == LOGFILE_TYPE_FILETYPE) { entry->slot_number = SC_ATOMIC_ADD(eve_file_id, 1); SCLogDebug("%s - thread %d [slot %d]", log_path, entry->internal_thread_id, @@ -948,7 +958,7 @@ int LogFileFreeCtx(LogFileCtx *lf_ctx) /* Unregister from flush list first, before closing files. * This ensures the heartbeat thread won't try to flush a context * that's being destroyed. */ - LogFileUnregisterForFlush(lf_ctx); + LogFileUnregister(lf_ctx); if (lf_ctx->type == LOGFILE_TYPE_FILETYPE && lf_ctx->filetype.filetype->ThreadDeinit) { lf_ctx->filetype.filetype->ThreadDeinit( @@ -1016,83 +1026,90 @@ void LogFileFlush(LogFileCtx *file_ctx) } /** - * \brief Register a LogFileCtx for flush operations + * \brief Register a LogFileCtx for maintenance operations * - * Adds a LogFileCtx to the global flush list so the heartbeat thread - * can flush it directly without using pseudo packets. + * Adds a LogFileCtx to the global log file list so the heartbeat thread + * can perform flush and rotation on it. * * \param ctx The LogFileCtx to register (must be LOGFILE_TYPE_FILE) */ -void LogFileRegisterForFlush(LogFileCtx *ctx) +void LogFileRegister(LogFileCtx *ctx) { - if (!OutputFlushInterval()) { - SCLogDebug("heartbeat disabled; skipping flush registration"); - return; - } - if (ctx == NULL || ctx->type != LOGFILE_TYPE_FILE) { return; } - LogFileFlushEntry *entry = SCMalloc(sizeof(LogFileFlushEntry)); + LogFileEntry *entry = SCMalloc(sizeof(LogFileEntry)); if (entry == NULL) { - SCLogError("Unable to allocate memory for flush entry"); + SCLogError("Unable to allocate memory for log file entry"); return; } entry->ctx = ctx; - SCMutexLock(&log_file_flush_mutex); - TAILQ_INSERT_TAIL(&log_file_flush_list, entry, entries); - SCMutexUnlock(&log_file_flush_mutex); + SCMutexLock(&log_file_list_mutex); + TAILQ_INSERT_TAIL(&log_file_list, entry, entries); + SCMutexUnlock(&log_file_list_mutex); } /** - * \brief Unregister a LogFileCtx from flush operations + * \brief Unregister a LogFileCtx from maintenance operations * - * Removes a LogFileCtx from the global flush list. + * Removes a LogFileCtx from the global log file list. * * \param ctx The LogFileCtx to unregister */ -void LogFileUnregisterForFlush(LogFileCtx *ctx) +void LogFileUnregister(LogFileCtx *ctx) { - if (!OutputFlushInterval()) { - SCLogDebug("heartbeat disabled; skipping flush deregistration"); - return; - } - if (ctx == NULL) { return; } - SCMutexLock(&log_file_flush_mutex); - LogFileFlushEntry *entry, *safe; - TAILQ_FOREACH_SAFE (entry, &log_file_flush_list, entries, safe) { + SCMutexLock(&log_file_list_mutex); + LogFileEntry *entry, *safe; + TAILQ_FOREACH_SAFE (entry, &log_file_list, entries, safe) { if (entry->ctx == ctx) { - TAILQ_REMOVE(&log_file_flush_list, entry, entries); + TAILQ_REMOVE(&log_file_list, entry, entries); SCFree(entry); break; } } - SCMutexUnlock(&log_file_flush_mutex); + SCMutexUnlock(&log_file_list_mutex); } /** * \brief Flush all registered LogFileCtx instances * - * Called by the heartbeat thread to flush all active file-based loggers. - * Iterates through the flush list and calls LogFileFlush on each context. + * Called by the maintenance thread to flush all active file-based loggers. */ void LogFileFlushAll(void) { - SCMutexLock(&log_file_flush_mutex); - LogFileFlushEntry *entry; - TAILQ_FOREACH (entry, &log_file_flush_list, entries) { + SCMutexLock(&log_file_list_mutex); + LogFileEntry *entry; + TAILQ_FOREACH (entry, &log_file_list, entries) { if (entry->ctx != NULL) { LogFileFlush(entry->ctx); } } - SCMutexUnlock(&log_file_flush_mutex); + SCMutexUnlock(&log_file_list_mutex); +} + +/** + * \brief Check rotation for all registered LogFileCtx instances + * + * Called by the maintenance thread to trigger rotation checks on all + * registered log contexts during zero-traffic periods. + */ +void LogFileRotateAll(void) +{ + SCMutexLock(&log_file_list_mutex); + LogFileEntry *entry; + TAILQ_FOREACH (entry, &log_file_list, entries) { + if (entry->ctx != NULL && entry->ctx->Rotate != NULL) { + entry->ctx->Rotate(entry->ctx); + } + } + SCMutexUnlock(&log_file_list_mutex); } int LogFileWrite(LogFileCtx *file_ctx, MemBuffer *buffer) diff --git a/src/util-logopenfile.h b/src/util-logopenfile.h index d8ca383e1077..314673ff3741 100644 --- a/src/util-logopenfile.h +++ b/src/util-logopenfile.h @@ -68,10 +68,10 @@ typedef struct LogFileTypeCtx_ { void *thread_data; } LogFileTypeCtx; -typedef struct LogFileFlushEntry_ { +typedef struct LogFileEntry_ { struct LogFileCtx_ *ctx; - TAILQ_ENTRY(LogFileFlushEntry_) entries; -} LogFileFlushEntry; + TAILQ_ENTRY(LogFileEntry_) entries; +} LogFileEntry; /** Global structure for Output Context */ typedef struct LogFileCtx_ { @@ -92,6 +92,7 @@ typedef struct LogFileCtx_ { int (*Write)(const char *buffer, int buffer_len, struct LogFileCtx_ *fp); void (*Close)(struct LogFileCtx_ *fp); void (*Flush)(struct LogFileCtx_ *fp); + void (*Rotate)(struct LogFileCtx_ *fp); LogFileTypeCtx filetype; @@ -192,9 +193,10 @@ int SCConfLogOpenGeneric(SCConfNode *conf, LogFileCtx *, const char *, int); int SCConfLogReopen(LogFileCtx *); bool SCLogOpenThreadedFile(const char *log_path, const char *append, LogFileCtx *parent_ctx); -/* Flush list management functions */ -void LogFileRegisterForFlush(LogFileCtx *ctx); -void LogFileUnregisterForFlush(LogFileCtx *ctx); +/* Log file list management functions */ +void LogFileRegister(LogFileCtx *ctx); +void LogFileUnregister(LogFileCtx *ctx); void LogFileFlushAll(void); +void LogFileRotateAll(void); #endif /* SURICATA_UTIL_LOGOPENFILE_H */