detect/transforms: in place modifications of buffers

As is the case when chaining multiple transforms.
Avoids using memcpy in these cases.

Add tests for these cases.

Ticket: 7409
pull/12213/head
Philippe Antoine 7 months ago committed by Victor Julien
parent e9173f3b06
commit b58b886db7

@ -35,21 +35,16 @@ unsafe extern "C" fn compress_whitespace_setup(
fn compress_whitespace_transform_do(input: &[u8], output: &mut [u8]) -> u32 { fn compress_whitespace_transform_do(input: &[u8], output: &mut [u8]) -> u32 {
let mut nb = 0; let mut nb = 0;
// seems faster than writing one byte at a time via let mut space = false;
// for (i, o) in input.iter().filter(|c| !matches!(*c, b'\t' | b'\n' | b'\x0B' | b'\x0C' | b'\r' | b' ')).zip(output) for c in input {
for subslice in if !matches!(*c, b'\t' | b'\n' | b'\x0B' | b'\x0C' | b'\r' | b' ') {
input.split_inclusive(|c| matches!(*c, b'\t' | b'\n' | b'\x0B' | b'\x0C' | b'\r' | b' ')) output[nb] = *c;
{ nb += 1;
// a subslice of length 1 not at the beginning is a space following another space space = false;
if nb == 0 } else if !space {
|| subslice.len() > 1 output[nb] = *c;
|| !matches!( nb += 1;
subslice[0], space = true;
b'\t' | b'\n' | b'\x0B' | b'\x0C' | b'\r' | b' '
)
{
output[nb..nb + subslice.len()].copy_from_slice(subslice);
nb += subslice.len();
} }
} }
return nb as u32; return nb as u32;
@ -142,14 +137,22 @@ mod tests {
exp.len() as u32 exp.len() as u32
); );
assert_eq!(&out[..exp.len()], exp); assert_eq!(&out[..exp.len()], exp);
let buf = b"I \t J"; let mut buf = Vec::new();
buf.extend_from_slice(b"I \t J");
let mut out = vec![0; buf.len()]; let mut out = vec![0; buf.len()];
let exp = b"I J"; let exp = b"I J";
assert_eq!( assert_eq!(
compress_whitespace_transform_do(buf, &mut out), compress_whitespace_transform_do(&buf, &mut out),
exp.len() as u32 exp.len() as u32
); );
assert_eq!(&out[..exp.len()], exp); assert_eq!(&out[..exp.len()], exp);
// test in place
let still_buf = unsafe { std::slice::from_raw_parts(buf.as_ptr(), buf.len()) };
assert_eq!(
compress_whitespace_transform_do(still_buf, &mut buf),
exp.len() as u32
);
assert_eq!(&still_buf[..exp.len()], b"I J");
} }
#[test] #[test]

@ -34,24 +34,32 @@ unsafe extern "C" fn dot_prefix_setup(
} }
fn dot_prefix_transform_do(input: &[u8], output: &mut [u8]) { fn dot_prefix_transform_do(input: &[u8], output: &mut [u8]) {
output[0] = b'.'; if std::ptr::eq(output.as_ptr(), input.as_ptr()) {
output.copy_within(0..input.len(), 1);
} else {
output[1..].copy_from_slice(input); output[1..].copy_from_slice(input);
} }
output[0] = b'.';
}
#[no_mangle] #[no_mangle]
unsafe extern "C" fn dot_prefix_transform(buffer: *mut c_void, _ctx: *mut c_void) { unsafe extern "C" fn dot_prefix_transform(buffer: *mut c_void, _ctx: *mut c_void) {
let input = InspectionBufferPtr(buffer);
let input_len = InspectionBufferLength(buffer); let input_len = InspectionBufferLength(buffer);
if input.is_null() || input_len == 0 { if input_len == 0 {
return; return;
} }
let input = build_slice!(input, input_len as usize);
let output = InspectionBufferCheckAndExpand(buffer, input_len + 1); let output = InspectionBufferCheckAndExpand(buffer, input_len + 1);
if output.is_null() { if output.is_null() {
// allocation failure // allocation failure
return; return;
} }
// get input after possible realloc
let input = InspectionBufferPtr(buffer);
if input.is_null() {
// allocation failure
return;
}
let input = build_slice!(input, input_len as usize);
let output = std::slice::from_raw_parts_mut(output, (input_len + 1) as usize); let output = std::slice::from_raw_parts_mut(output, (input_len + 1) as usize);
dot_prefix_transform_do(input, output); dot_prefix_transform_do(input, output);
@ -89,9 +97,15 @@ mod tests {
let mut out = vec![0; b"example.com".len() + 1]; let mut out = vec![0; b"example.com".len() + 1];
dot_prefix_transform_do(buf, &mut out); dot_prefix_transform_do(buf, &mut out);
assert_eq!(out, b".example.com"); assert_eq!(out, b".example.com");
let buf = b"hello.example.com"; let mut buf = Vec::with_capacity(b"hello.example.com".len() + 1);
buf.extend_from_slice(b"hello.example.com");
let mut out = vec![0; b"hello.example.com".len() + 1]; let mut out = vec![0; b"hello.example.com".len() + 1];
dot_prefix_transform_do(buf, &mut out); dot_prefix_transform_do(&buf, &mut out);
assert_eq!(out, b".hello.example.com"); assert_eq!(out, b".hello.example.com");
// test in place
let still_buf = unsafe { std::slice::from_raw_parts(buf.as_ptr(), buf.len()) };
buf.push(b'.');
dot_prefix_transform_do(still_buf, &mut buf);
assert_eq!(&buf, b".hello.example.com");
} }
} }

@ -231,9 +231,15 @@ mod tests {
#[test] #[test]
fn test_sha256_transform() { fn test_sha256_transform() {
let buf = b" A B C D "; let mut buf = Vec::with_capacity(SC_SHA256_LEN);
buf.extend_from_slice(b" A B C D ");
let mut out = vec![0; SC_SHA256_LEN]; let mut out = vec![0; SC_SHA256_LEN];
sha256_transform_do(buf, &mut out); sha256_transform_do(&buf, &mut out);
assert_eq!(out, b"\xd6\xbf\x7d\x8d\x69\x53\x02\x4d\x0d\x84\x5c\x99\x9b\xae\x93\xcc\xac\x68\xea\xab\x9a\xc9\x77\xd0\xfd\x30\x6a\xf5\x9a\x3d\xe4\x3a"); assert_eq!(out, b"\xd6\xbf\x7d\x8d\x69\x53\x02\x4d\x0d\x84\x5c\x99\x9b\xae\x93\xcc\xac\x68\xea\xab\x9a\xc9\x77\xd0\xfd\x30\x6a\xf5\x9a\x3d\xe4\x3a");
// test in place
let still_buf = unsafe { std::slice::from_raw_parts(buf.as_ptr(), buf.len()) };
buf.resize(SC_SHA256_LEN, 0);
sha256_transform_do(still_buf, &mut buf);
assert_eq!(&buf, b"\xd6\xbf\x7d\x8d\x69\x53\x02\x4d\x0d\x84\x5c\x99\x9b\xae\x93\xcc\xac\x68\xea\xab\x9a\xc9\x77\xd0\xfd\x30\x6a\xf5\x9a\x3d\xe4\x3a");
} }
} }

@ -103,11 +103,18 @@ unsafe extern "C" fn strip_pseudo_setup(
fn strip_pseudo_transform_do(input: &[u8], output: &mut [u8]) -> u32 { fn strip_pseudo_transform_do(input: &[u8], output: &mut [u8]) -> u32 {
let mut nb = 0; let mut nb = 0;
let mut inb = 0;
let same = std::ptr::eq(output.as_ptr(), input.as_ptr());
for subslice in input.split_inclusive(|c| *c == b'\n') { for subslice in input.split_inclusive(|c| *c == b'\n') {
if !subslice.is_empty() && subslice[0] != b':' { if !subslice.is_empty() && subslice[0] != b':' {
if same {
output.copy_within(inb..inb + subslice.len(), nb);
} else {
output[nb..nb + subslice.len()].copy_from_slice(subslice); output[nb..nb + subslice.len()].copy_from_slice(subslice);
}
nb += subslice.len(); nb += subslice.len();
} }
inb += subslice.len();
} }
return nb as u32; return nb as u32;
} }
@ -183,5 +190,16 @@ mod tests {
let mut out = vec![0; buf.len()]; let mut out = vec![0; buf.len()];
let nb = strip_pseudo_transform_do(buf, &mut out); let nb = strip_pseudo_transform_do(buf, &mut out);
assert_eq!(&out[..nb as usize], b"header2:Value2"); assert_eq!(&out[..nb as usize], b"header2:Value2");
let mut buf = Vec::new();
buf.extend_from_slice(
b"Header1: Value1\n:method:get\nheader2:Value2\n:scheme:https\nheader3:Value3\n",
);
// test in place
let still_buf = unsafe { std::slice::from_raw_parts(buf.as_ptr(), buf.len()) };
let nb = strip_pseudo_transform_do(still_buf, &mut buf);
assert_eq!(
&still_buf[..nb as usize],
b"Header1: Value1\nheader2:Value2\nheader3:Value3\n"
);
} }
} }

@ -35,13 +35,15 @@ unsafe extern "C" fn strip_whitespace_setup(
fn strip_whitespace_transform_do(input: &[u8], output: &mut [u8]) -> u32 { fn strip_whitespace_transform_do(input: &[u8], output: &mut [u8]) -> u32 {
let mut nb = 0; let mut nb = 0;
// seems faster than writing one byte at a time via for (i, o) in input
// for (i, o) in input.iter().filter(|c| !matches!(*c, b'\t' | b'\n' | b'\x0B' | b'\x0C' | b'\r' | b' ')).zip(output) .iter()
for subslice in input.split(|c| matches!(*c, b'\t' | b'\n' | b'\x0B' | b'\x0C' | b'\r' | b' ')) .filter(|c| !matches!(*c, b'\t' | b'\n' | b'\x0B' | b'\x0C' | b'\r' | b' '))
.zip(output)
{ {
output[nb..nb + subslice.len()].copy_from_slice(subslice); *o = *i;
nb += subslice.len(); nb += 1;
} }
// do not use faster copy_from_slice because input and output may overlap (point to the same data)
return nb as u32; return nb as u32;
} }
@ -124,13 +126,21 @@ mod tests {
); );
assert_eq!(&out[..exp.len()], exp); assert_eq!(&out[..exp.len()], exp);
let buf = b"I \t J"; let mut buf = Vec::new();
buf.extend_from_slice(b"I \t J");
let mut out = vec![0; buf.len()]; let mut out = vec![0; buf.len()];
let exp = b"IJ"; let exp = b"IJ";
assert_eq!( assert_eq!(
strip_whitespace_transform_do(buf, &mut out), strip_whitespace_transform_do(&buf, &mut out),
exp.len() as u32 exp.len() as u32
); );
assert_eq!(&out[..exp.len()], exp); assert_eq!(&out[..exp.len()], exp);
// test in place
let still_buf = unsafe { std::slice::from_raw_parts(buf.as_ptr(), buf.len()) };
assert_eq!(
strip_whitespace_transform_do(still_buf, &mut buf),
exp.len() as u32
);
assert_eq!(&still_buf[..exp.len()], b"IJ");
} }
} }

@ -135,9 +135,14 @@ mod tests {
#[test] #[test]
fn test_url_decode_transform() { fn test_url_decode_transform() {
let buf = b"Suricata%20is+%27%61wesome%21%27%25%30%30%ZZ%4"; let mut buf = Vec::new();
buf.extend_from_slice(b"Suricata%20is+%27%61wesome%21%27%25%30%30%ZZ%4");
let mut out = vec![0; buf.len()]; let mut out = vec![0; buf.len()];
let nb = url_decode_transform_do(buf, &mut out); let nb = url_decode_transform_do(&buf, &mut out);
assert_eq!(&out[..nb as usize], b"Suricata is 'awesome!'%00%ZZ%4"); assert_eq!(&out[..nb as usize], b"Suricata is 'awesome!'%00%ZZ%4");
// test in place
let still_buf = unsafe { std::slice::from_raw_parts(buf.as_ptr(), buf.len()) };
let nb = url_decode_transform_do(still_buf, &mut buf);
assert_eq!(&still_buf[..nb as usize], b"Suricata is 'awesome!'%00%ZZ%4");
} }
} }

@ -143,10 +143,15 @@ mod tests {
#[test] #[test]
fn test_xor_transform() { fn test_xor_transform() {
let buf = b"example.com"; let mut buf = Vec::new();
buf.extend_from_slice(b"example.com");
let mut out = vec![0; buf.len()]; let mut out = vec![0; buf.len()];
let ctx = xor_parse_do("0a0DC8ff").unwrap(); let ctx = xor_parse_do("0a0DC8ff").unwrap();
xor_transform_do(buf, &mut out, &ctx); xor_transform_do(&buf, &mut out, &ctx);
assert_eq!(out, b"ou\xa9\x92za\xad\xd1ib\xa5"); assert_eq!(out, b"ou\xa9\x92za\xad\xd1ib\xa5");
// test in place
let still_buf = unsafe { std::slice::from_raw_parts(buf.as_ptr(), buf.len()) };
xor_transform_do(still_buf, &mut buf, &ctx);
assert_eq!(&still_buf, b"ou\xa9\x92za\xad\xd1ib\xa5");
} }
} }

Loading…
Cancel
Save