From cb9ab951b975c326da08e42449301f552686c409 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Thu, 11 Sep 2025 22:06:14 +0200 Subject: [PATCH] detect/integers: subslice for multi-integers --- doc/userguide/rules/integer-keywords.rst | 13 +++- rust/src/detect/uint.rs | 77 ++++++++++++++++++++---- 2 files changed, 75 insertions(+), 15 deletions(-) diff --git a/doc/userguide/rules/integer-keywords.rst b/doc/userguide/rules/integer-keywords.rst index 63f4bd121c..13b993c38b 100644 --- a/doc/userguide/rules/integer-keywords.rst +++ b/doc/userguide/rules/integer-keywords.rst @@ -98,7 +98,7 @@ As :ref:`multi-buffers ` and sticky buffers, some integer keywords are also multi-integer. They expand the syntax of a single integer:: - keyword: operation and value[,index]; + keyword: operation and value[,index,subslice]; .. table:: **Index values for multi-integers keyword** @@ -124,4 +124,13 @@ be sure to have the final number of elements. The index ``nb`` accepts all comparison modes as integer keywords. For example ``nb>3`` will match only if more than 3 integers in the -array match the value. \ No newline at end of file +array match the value. + +The subslice may use positive or negative indexing. +For the array [1,2,3,4,5,6], here are some examples: +* 2:4 will have subslice [3,4] +* -4:-1 will have subslice [3,4,5] +* 3:-1 will have subslice [4,5] +* -4:4 will have subslice [3,4] + +If one index is out of bounds, an empty subslice is used. \ No newline at end of file diff --git a/rust/src/detect/uint.rs b/rust/src/detect/uint.rs index 8b4040b50d..e3f847c0bb 100644 --- a/rust/src/detect/uint.rs +++ b/rust/src/detect/uint.rs @@ -64,6 +64,9 @@ pub enum DetectUintIndex { pub struct DetectUintArrayData { pub du: DetectUintData, pub index: DetectUintIndex, + // subslice + pub start: i32, + pub end: i32, } fn parse_uint_index_precise(s: &str) -> IResult<&str, DetectUintIndex> { @@ -85,8 +88,29 @@ fn parse_uint_index_val(s: &str) -> Option { Some(arg1) } +fn parse_uint_subslice_aux(s: &str) -> IResult<&str, (i32, i32)> { + let (s, start) = nom_i32(s)?; + let (s, _) = char(':')(s)?; + let (s, end) = nom_i32(s)?; + return Ok((s, (start, end))); +} + +fn parse_uint_subslice(parts: &[&str]) -> Option<(i32, i32)> { + if parts.len() < 3 { + return Some((0, 0)); + } + let (_, (start, end)) = parse_uint_subslice_aux(parts[2]).ok()?; + if start > 0 && end > 0 && end <= start { + return None; + } + if start < 0 && end < 0 && end <= start { + return None; + } + return Some((start, end)); +} + fn parse_uint_index(parts: &[&str]) -> Option { - let index = if parts.len() == 2 { + let index = if parts.len() >= 2 { match parts[1] { "all" => DetectUintIndex::All, "all1" => DetectUintIndex::All1, @@ -103,36 +127,63 @@ fn parse_uint_index(parts: &[&str]) -> Option { pub(crate) fn detect_parse_array_uint(s: &str) -> Option> { let parts: Vec<&str> = s.split(',').collect(); - if parts.len() > 2 { + if parts.len() > 3 { return None; } let index = parse_uint_index(&parts)?; let (_, du) = detect_parse_uint::(parts[0]).ok()?; + let (start, end) = parse_uint_subslice(&parts)?; - Some(DetectUintArrayData { du, index }) + Some(DetectUintArrayData { + du, + index, + start, + end, + }) } pub(crate) fn detect_parse_array_uint_enum>( s: &str, ) -> Option> { let parts: Vec<&str> = s.split(',').collect(); - if parts.len() > 2 { + if parts.len() > 3 { return None; } let index = parse_uint_index(&parts)?; let du = detect_parse_uint_enum::(parts[0])?; + let (start, end) = parse_uint_subslice(&parts)?; - Some(DetectUintArrayData { du, index }) + Some(DetectUintArrayData { + du, + index, + start, + end, + }) } pub(crate) fn detect_uint_match_at_index( array: &[T], ctx: &DetectUintArrayData, get_value: impl Fn(&T) -> Option, eof: bool, ) -> c_int { + let start = if ctx.start >= 0 { + ctx.start as usize + } else { + ((array.len() as i32) + ctx.start) as usize + }; + let end = if ctx.end > 0 { + ctx.end as usize + } else { + ((array.len() as i32) + ctx.end) as usize + }; + let subslice = if end > array.len() || start >= end { + &array[..0] + } else { + &array[start..end] + }; match &ctx.index { DetectUintIndex::Any => { - for response in array { + for response in subslice { if let Some(code) = get_value(response) { if detect_match_uint::(&ctx.du, code) { return 1; @@ -143,7 +194,7 @@ pub(crate) fn detect_uint_match_at_index( } DetectUintIndex::OrAbsent => { let mut has_elem = false; - for response in array { + for response in subslice { if let Some(code) = get_value(response) { if detect_match_uint::(&ctx.du, code) { return 1; @@ -166,7 +217,7 @@ pub(crate) fn detect_uint_match_at_index( } } let mut nb = 0u32; - for response in array { + for response in subslice { if let Some(code) = get_value(response) { if detect_match_uint::(&ctx.du, code) { nb += 1; @@ -182,7 +233,7 @@ pub(crate) fn detect_uint_match_at_index( if !eof { return 0; } - for response in array { + for response in subslice { if let Some(code) = get_value(response) { if !detect_match_uint::(&ctx.du, code) { return 0; @@ -196,7 +247,7 @@ pub(crate) fn detect_uint_match_at_index( return 0; } let mut has_elem = false; - for response in array { + for response in subslice { if let Some(code) = get_value(response) { if !detect_match_uint::(&ctx.du, code) { return 0; @@ -212,17 +263,17 @@ pub(crate) fn detect_uint_match_at_index( DetectUintIndex::Index((oob, idx)) => { let index = if *idx < 0 { // negative values for backward indexing. - ((array.len() as i32) + idx) as usize + ((subslice.len() as i32) + idx) as usize } else { *idx as usize }; - if array.len() <= index { + if subslice.len() <= index { if *oob && eof { return 1; } return 0; } - if let Some(code) = get_value(&array[index]) { + if let Some(code) = get_value(&subslice[index]) { return detect_match_uint::(&ctx.du, code) as c_int; } return 0;