Coverage Report

Created: 2026-11-12 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata8/rust/src/detect/byte_math.rs
Line
Count
Source
1
/* Copyright (C) 2022-2026 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
// Author: Jeff Lucovsky <jlucovsky@oisf.net>
19
20
use crate::detect::error::RuleParseError;
21
use crate::detect::parser::{parse_var, take_until_whitespace, ResultValue};
22
use crate::detect::{get_endian_value, get_string_value, ByteBase, ByteEndian};
23
use std::ffi::{CStr, CString};
24
use std::os::raw::c_char;
25
26
use nom7::bytes::complete::tag;
27
use nom7::character::complete::multispace0;
28
use nom7::sequence::preceded;
29
use nom7::{Err, IResult};
30
use std::str;
31
32
pub const DETECT_BYTEMATH_FLAG_RELATIVE: u8 = 0x01;
33
pub const DETECT_BYTEMATH_FLAG_STRING: u8 = 0x02;
34
pub const DETECT_BYTEMATH_FLAG_BITMASK: u8 = 0x04;
35
pub const DETECT_BYTEMATH_FLAG_ENDIAN: u8 = 0x08;
36
pub const DETECT_BYTEMATH_FLAG_RVALUE_VAR: u8 = 0x10;
37
pub const DETECT_BYTEMATH_FLAG_NBYTES_VAR: u8 = 0x20;
38
39
// Ensure required values are provided
40
const DETECT_BYTEMATH_FLAG_NBYTES: u8 = 0x1;
41
const DETECT_BYTEMATH_FLAG_OFFSET: u8 = 0x2;
42
const DETECT_BYTEMATH_FLAG_OPER: u8 = 0x4;
43
const DETECT_BYTEMATH_FLAG_RVALUE: u8 = 0x8;
44
const DETECT_BYTEMATH_FLAG_RESULT: u8 = 0x10;
45
const DETECT_BYTEMATH_FLAG_REQUIRED: u8 = DETECT_BYTEMATH_FLAG_RESULT
46
    | DETECT_BYTEMATH_FLAG_RVALUE
47
    | DETECT_BYTEMATH_FLAG_NBYTES
48
    | DETECT_BYTEMATH_FLAG_OFFSET
49
    | DETECT_BYTEMATH_FLAG_OPER;
50
51
#[repr(u8)]
52
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
53
// operators: +, -, /, *, <<, >>
54
pub enum ByteMathOperator {
55
    OperatorNone = 1,
56
    Addition = 2,
57
    Subtraction = 3,
58
    Division = 4,
59
    Multiplication = 5,
60
    LeftShift = 6,
61
    RightShift = 7,
62
}
63
64
pub const DETECT_BYTEMATH_ENDIAN_DEFAULT: ByteEndian = ByteEndian::BigEndian;
65
66
const BASE_DEFAULT: ByteBase = ByteBase::BaseDec;
67
68
// Fixed position parameter count: bytes, offset, oper, rvalue, result
69
// result is not parsed with the fixed position parameters as it's
70
// often swapped with optional parameters
71
pub const DETECT_BYTEMATH_FIXED_PARAM_COUNT: usize = 5;
72
// Optional parameters: endian, relative, string, dce, bitmask
73
pub const DETECT_BYTEMATH_MAX_PARAM_COUNT: usize = 10;
74
75
#[repr(C)]
76
#[derive(Debug)]
77
pub struct DetectByteMathData {
78
    rvalue_str: *const c_char,
79
    result: *const c_char,
80
    nbytes_str: *const c_char,
81
    rvalue: u32,
82
    offset: i32,
83
    bitmask_val: u32,
84
    bitmask_shift_count: u16,
85
    id: u16,
86
    flags: u8,
87
    local_id: u8,
88
    nbytes: u8,
89
    oper: ByteMathOperator,
90
    endian: ByteEndian, // big, little, dce
91
    base: ByteBase,     // From string or dce
92
}
93
94
impl Drop for DetectByteMathData {
95
6.59k
    fn drop(&mut self) {
96
        unsafe {
97
6.59k
            if !self.result.is_null() {
98
2.88k
                let _ = CString::from_raw(self.result as *mut c_char);
99
3.70k
            }
100
6.59k
            if !self.rvalue_str.is_null() {
101
2.59k
                let _ = CString::from_raw(self.rvalue_str as *mut c_char);
102
3.99k
            }
103
6.59k
            if !self.nbytes_str.is_null() {
104
275
                let _ = CString::from_raw(self.nbytes_str as *mut c_char);
105
6.31k
            }
106
        }
107
6.59k
    }
108
}
109
110
impl Default for DetectByteMathData {
111
3.29k
    fn default() -> Self {
112
3.29k
        DetectByteMathData {
113
3.29k
            local_id: 0,
114
3.29k
            flags: 0,
115
3.29k
            nbytes: 0,
116
3.29k
            offset: 0,
117
3.29k
            oper: ByteMathOperator::OperatorNone,
118
3.29k
            rvalue_str: std::ptr::null_mut(),
119
3.29k
            nbytes_str: std::ptr::null_mut(),
120
3.29k
            rvalue: 0,
121
3.29k
            result: std::ptr::null_mut(),
122
3.29k
            endian: DETECT_BYTEMATH_ENDIAN_DEFAULT,
123
3.29k
            base: BASE_DEFAULT,
124
3.29k
            bitmask_val: 0,
125
3.29k
            bitmask_shift_count: 0,
126
3.29k
            id: 0,
127
3.29k
        }
128
3.29k
    }
129
}
130
131
impl DetectByteMathData {
132
3.29k
    pub fn new() -> Self {
133
3.29k
        Self {
134
3.29k
            ..Default::default()
135
3.29k
        }
136
3.29k
    }
137
}
138
139
3.14k
fn get_oper_value(value: &str) -> Result<ByteMathOperator, ()> {
140
3.14k
    let res = match value {
141
3.14k
        "+" => ByteMathOperator::Addition,
142
3.09k
        "-" => ByteMathOperator::Subtraction,
143
2.46k
        "/" => ByteMathOperator::Division,
144
475
        "*" => ByteMathOperator::Multiplication,
145
475
        "<<" => ByteMathOperator::LeftShift,
146
61
        ">>" => ByteMathOperator::RightShift,
147
23
        _ => return Err(()),
148
    };
149
150
3.12k
    Ok(res)
151
3.14k
}
152
153
3.54k
fn parse_bytemath(input: &str) -> IResult<&str, DetectByteMathData, RuleParseError<&str>> {
154
    // Inner utility function for easy error creation.
155
696
    fn make_error(reason: String) -> nom7::Err<RuleParseError<&'static str>> {
156
696
        Err::Error(RuleParseError::InvalidByteMath(reason))
157
696
    }
158
3.54k
    let (_, values) = nom7::multi::separated_list1(
159
3.54k
        tag(","),
160
3.54k
        preceded(multispace0, nom7::bytes::complete::is_not(",")),
161
3.54k
    )(input)?;
162
163
3.54k
    if values.len() < DETECT_BYTEMATH_FIXED_PARAM_COUNT
164
3.30k
        || values.len() > DETECT_BYTEMATH_MAX_PARAM_COUNT
165
    {
166
245
        return Err(make_error(format!("Incorrect argument string; at least {} values must be specified but no more than {}: {:?}",
167
245
            DETECT_BYTEMATH_FIXED_PARAM_COUNT, DETECT_BYTEMATH_MAX_PARAM_COUNT, input)));
168
3.29k
    }
169
170
3.29k
    let mut required_flags: u8 = 0;
171
3.29k
    let mut byte_math = DetectByteMathData::new();
172
    //for value in &values[0..] {
173
19.3k
    for value in values {
174
16.4k
        let (mut val, mut name) = take_until_whitespace(value)?;
175
16.4k
        val = val.trim();
176
16.4k
        name = name.trim();
177
16.4k
        match name {
178
16.4k
            "oper" => {
179
3.15k
                if 0 != (required_flags & DETECT_BYTEMATH_FLAG_OPER) {
180
7
                    return Err(make_error("operator already set".to_string()));
181
3.14k
                }
182
3.14k
                byte_math.oper = match get_oper_value(val) {
183
3.12k
                    Ok(val) => val,
184
                    Err(_) => {
185
23
                        return Err(make_error(format!("unknown oper value {}", val)));
186
                    }
187
                };
188
3.12k
                required_flags |= DETECT_BYTEMATH_FLAG_OPER;
189
            }
190
13.3k
            "result" => {
191
2.88k
                if 0 != (required_flags & DETECT_BYTEMATH_FLAG_RESULT) {
192
1
                    return Err(make_error("result already set".to_string()));
193
2.88k
                }
194
2.88k
                let tmp: String = val
195
2.88k
                    .parse()
196
2.88k
                    .map_err(|_| make_error(format!("invalid result: {}", val)))?;
197
2.88k
                match CString::new(tmp) {
198
2.88k
                    Ok(strval) => {
199
2.88k
                        byte_math.result = strval.into_raw();
200
2.88k
                        required_flags |= DETECT_BYTEMATH_FLAG_RESULT;
201
2.88k
                    }
202
                    _ => {
203
0
                        return Err(make_error(
204
0
                            "parse string not safely convertible to C".to_string(),
205
0
                        ));
206
                    }
207
                }
208
            }
209
10.4k
            "rvalue" => {
210
3.10k
                if 0 != (required_flags & DETECT_BYTEMATH_FLAG_RVALUE) {
211
1
                    return Err(make_error("rvalue already set".to_string()));
212
3.10k
                }
213
3.10k
                let (_, res) = parse_var(val)?;
214
3.08k
                match res {
215
492
                    ResultValue::Numeric(val) => {
216
492
                        if val >= u64::from(u32::MIN) && val <= u64::from(u32::MAX) {
217
489
                            byte_math.rvalue = val as u32
218
                        } else {
219
3
                            return Err(make_error(format!(
220
3
                                "invalid rvalue value: must be between {} and {}: {}",
221
3
                                1,
222
3
                                u32::MAX,
223
3
                                val
224
3
                            )));
225
                        }
226
                    }
227
2.59k
                    ResultValue::String(val) => match CString::new(val) {
228
2.59k
                        Ok(newval) => {
229
2.59k
                            byte_math.rvalue_str = newval.into_raw();
230
2.59k
                            byte_math.flags |= DETECT_BYTEMATH_FLAG_RVALUE_VAR;
231
2.59k
                        }
232
                        _ => {
233
0
                            return Err(make_error(
234
0
                                "parse string not safely convertible to C".to_string(),
235
0
                            ))
236
                        }
237
                    },
238
                }
239
3.08k
                required_flags |= DETECT_BYTEMATH_FLAG_RVALUE;
240
            }
241
7.33k
            "endian" => {
242
5
                if 0 != (byte_math.flags & DETECT_BYTEMATH_FLAG_ENDIAN) {
243
0
                    return Err(make_error("endianess already set".to_string()));
244
5
                }
245
5
                if let Some(endian) = get_endian_value(val) {
246
1
                    byte_math.endian = endian;
247
1
                } else {
248
4
                    return Err(make_error(format!("invalid endian value: {}", val)));
249
                };
250
1
                byte_math.flags |= DETECT_BYTEMATH_FLAG_ENDIAN;
251
            }
252
7.33k
            "dce" => {
253
14
                if 0 != (byte_math.flags & DETECT_BYTEMATH_FLAG_ENDIAN) {
254
1
                    return Err(make_error("endianess already set".to_string()));
255
13
                }
256
13
                byte_math.flags |= DETECT_BYTEMATH_FLAG_ENDIAN;
257
13
                byte_math.endian = ByteEndian::EndianDCE;
258
            }
259
7.32k
            "string" => {
260
5
                if 0 != (byte_math.flags & DETECT_BYTEMATH_FLAG_STRING) {
261
0
                    return Err(make_error("string already set".to_string()));
262
5
                }
263
5
                if let Some(base) = get_string_value(val) {
264
0
                    byte_math.base = base;
265
0
                } else {
266
5
                    return Err(make_error(format!("invalid string value: {}", val)));
267
                };
268
0
                byte_math.flags |= DETECT_BYTEMATH_FLAG_STRING;
269
            }
270
7.31k
            "relative" => {
271
554
                if 0 != (byte_math.flags & DETECT_BYTEMATH_FLAG_RELATIVE) {
272
1
                    return Err(make_error("relative already set".to_string()));
273
553
                }
274
553
                byte_math.flags |= DETECT_BYTEMATH_FLAG_RELATIVE;
275
            }
276
6.76k
            "bitmask" => {
277
22
                if 0 != (byte_math.flags & DETECT_BYTEMATH_FLAG_BITMASK) {
278
0
                    return Err(make_error("bitmask already set".to_string()));
279
22
                }
280
22
                let trimmed = if val.starts_with("0x") || val.starts_with("0X") {
281
2
                    &val[2..]
282
                } else {
283
20
                    val
284
                };
285
286
22
                let val = u32::from_str_radix(trimmed, 16)
287
22
                    .map_err(|_| make_error(format!("invalid bitmask value: {}", value)))?;
288
7
                byte_math.bitmask_val = val;
289
7
                byte_math.flags |= DETECT_BYTEMATH_FLAG_BITMASK;
290
            }
291
6.73k
            "offset" => {
292
3.27k
                if 0 != (required_flags & DETECT_BYTEMATH_FLAG_OFFSET) {
293
96
                    return Err(make_error("offset already set".to_string()));
294
3.17k
                }
295
3.17k
                byte_math.offset = val
296
3.17k
                    .parse::<i32>()
297
3.17k
                    .map_err(|_| make_error(format!("invalid offset value: {}", val)))?;
298
3.16k
                if byte_math.offset > 65535 || byte_math.offset < -65535 {
299
15
                    return Err(make_error(format!(
300
15
                        "invalid offset value: must be between -65535 and 65535: {}",
301
15
                        val
302
15
                    )));
303
3.15k
                }
304
3.15k
                required_flags |= DETECT_BYTEMATH_FLAG_OFFSET;
305
            }
306
3.46k
            "bytes" => {
307
3.29k
                if 0 != (required_flags & DETECT_BYTEMATH_FLAG_NBYTES) {
308
1
                    return Err(make_error("nbytes already set".to_string()));
309
3.28k
                }
310
3.28k
                let (_, res) = parse_var(val)?;
311
3.28k
                match res {
312
3.01k
                    ResultValue::Numeric(val) => {
313
3.01k
                        if (1..=10).contains(&val) {
314
2.93k
                            byte_math.nbytes = val as u8
315
                        } else {
316
75
                            return Err(make_error(format!(
317
75
                                "invalid nbytes value: must be between 1 and 10: {}",
318
75
                                val
319
75
                            )));
320
                        }
321
                    }
322
275
                    ResultValue::String(val) => match CString::new(val) {
323
275
                        Ok(newval) => {
324
275
                            byte_math.nbytes_str = newval.into_raw();
325
275
                            byte_math.flags |= DETECT_BYTEMATH_FLAG_NBYTES_VAR;
326
275
                        }
327
                        _ => {
328
0
                            return Err(make_error(
329
0
                                "parse string not safely convertible to C".to_string(),
330
0
                            ))
331
                        }
332
                    },
333
                }
334
3.21k
                required_flags |= DETECT_BYTEMATH_FLAG_NBYTES;
335
            }
336
            _ => {
337
174
                return Err(make_error(format!("unknown byte_math keyword: {}", name)));
338
            }
339
        };
340
    }
341
342
    // Ensure required values are present
343
2.83k
    if (required_flags & DETECT_BYTEMATH_FLAG_REQUIRED) != DETECT_BYTEMATH_FLAG_REQUIRED {
344
8
        return Err(make_error(format!(
345
8
            "required byte_math parameters missing: \"{:?}\"",
346
8
            input
347
8
        )));
348
2.82k
    }
349
350
    // Using left/right shift further restricts the value of nbytes. Note that
351
    // validation has already ensured nbytes is in [1..10]
352
322
    match byte_math.oper {
353
322
        ByteMathOperator::LeftShift | ByteMathOperator::RightShift if byte_math.nbytes > 4 => {
354
7
            return Err(make_error(format!("nbytes must be 1 through 4 (inclusive) when used with \"<<\" or \">>\"; {} is not valid", byte_math.nbytes)));
355
        }
356
2.82k
        _ => {}
357
    };
358
359
2.82k
    Ok((input, byte_math))
360
3.54k
}
361
362
/// Intermediary function between the C code and the parsing functions.
363
#[no_mangle]
364
3.54k
pub unsafe extern "C" fn SCByteMathParse(c_arg: *const c_char) -> *mut DetectByteMathData {
365
3.54k
    if c_arg.is_null() {
366
0
        return std::ptr::null_mut();
367
3.54k
    }
368
369
3.54k
    let arg = match CStr::from_ptr(c_arg).to_str() {
370
3.54k
        Ok(arg) => arg,
371
        Err(_) => {
372
0
            return std::ptr::null_mut();
373
        }
374
    };
375
3.54k
    match parse_bytemath(arg) {
376
2.82k
        Ok((_, detect)) => return Box::into_raw(Box::new(detect)),
377
720
        Err(_) => return std::ptr::null_mut(),
378
    }
379
3.54k
}
380
381
#[no_mangle]
382
3.54k
pub unsafe extern "C" fn SCByteMathFree(ptr: *mut DetectByteMathData) {
383
3.54k
    if !ptr.is_null() {
384
2.82k
        let _ = Box::from_raw(ptr);
385
2.82k
    }
386
3.54k
}
387
388
#[cfg(test)]
389
mod tests {
390
    use super::*;
391
    // structure equality only used by test cases
392
    impl PartialEq for DetectByteMathData {
393
        fn eq(&self, other: &Self) -> bool {
394
            let mut res: bool = false;
395
396
            if !self.rvalue_str.is_null() && !other.rvalue_str.is_null() {
397
                let s_val = unsafe { CStr::from_ptr(self.rvalue_str) };
398
                let o_val = unsafe { CStr::from_ptr(other.rvalue_str) };
399
                res = s_val == o_val;
400
            } else if !self.rvalue_str.is_null() || !other.rvalue_str.is_null() {
401
                return false;
402
            }
403
404
            if !self.nbytes_str.is_null() && !other.nbytes_str.is_null() {
405
                let s_val = unsafe { CStr::from_ptr(self.nbytes_str) };
406
                let o_val = unsafe { CStr::from_ptr(other.nbytes_str) };
407
                res = s_val == o_val;
408
            } else if !self.nbytes_str.is_null() || !other.nbytes_str.is_null() {
409
                return false;
410
            }
411
412
            if !self.result.is_null() && !self.result.is_null() {
413
                let s_val = unsafe { CStr::from_ptr(self.result) };
414
                let o_val = unsafe { CStr::from_ptr(other.result) };
415
                res = s_val == o_val;
416
            } else if !self.result.is_null() || !self.result.is_null() {
417
                return false;
418
            }
419
420
            res && self.local_id == other.local_id
421
                && self.flags == other.flags
422
                && self.nbytes == other.nbytes
423
                && self.offset == other.offset
424
                && self.oper == other.oper
425
                && self.rvalue == other.rvalue
426
                && self.endian == other.endian
427
                && self.base == other.base
428
                && self.bitmask_val == other.bitmask_val
429
                && self.bitmask_shift_count == other.bitmask_shift_count
430
                && self.id == other.id
431
        }
432
    }
433
434
    fn valid_test(
435
        args: &str, nbytes: u8, offset: i32, oper: ByteMathOperator, rvalue_str: &str,
436
        nbytes_str: &str, rvalue: u32, result: &str, base: ByteBase, endian: ByteEndian,
437
        bitmask_val: u32, flags: u8,
438
    ) {
439
        let bmd = DetectByteMathData {
440
            nbytes,
441
            offset,
442
            oper,
443
            rvalue_str: if !rvalue_str.is_empty() {
444
                CString::new(rvalue_str).unwrap().into_raw()
445
            } else {
446
                std::ptr::null_mut()
447
            },
448
            nbytes_str: if !nbytes_str.is_empty() {
449
                CString::new(nbytes_str).unwrap().into_raw()
450
            } else {
451
                std::ptr::null_mut()
452
            },
453
            rvalue,
454
            result: CString::new(result).unwrap().into_raw(),
455
            base,
456
            endian,
457
            bitmask_val,
458
            flags,
459
            ..Default::default()
460
        };
461
462
        let (_, val) = parse_bytemath(args).unwrap();
463
        assert_eq!(val, bmd);
464
    }
465
466
    #[test]
467
    fn test_parser_valid() {
468
        valid_test(
469
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result myresult, dce, string dec",
470
            4,
471
            3933,
472
            ByteMathOperator::Addition,
473
            "myrvalue",
474
            "",
475
            0,
476
            "myresult",
477
            ByteBase::BaseDec,
478
            ByteEndian::EndianDCE,
479
            0,
480
            DETECT_BYTEMATH_FLAG_RVALUE_VAR
481
                | DETECT_BYTEMATH_FLAG_STRING
482
                | DETECT_BYTEMATH_FLAG_ENDIAN,
483
        );
484
485
        valid_test(
486
            "bytes 4, offset 3933, oper +, rvalue 99, result other, dce, string dec",
487
            4,
488
            3933,
489
            ByteMathOperator::Addition,
490
            "",
491
            "",
492
            99,
493
            "other",
494
            ByteBase::BaseDec,
495
            ByteEndian::EndianDCE,
496
            0,
497
            DETECT_BYTEMATH_FLAG_STRING | DETECT_BYTEMATH_FLAG_ENDIAN,
498
        );
499
500
        valid_test(
501
            "bytes 4, offset -3933, oper +, rvalue myrvalue, result foo",
502
            4,
503
            -3933,
504
            ByteMathOperator::Addition,
505
            "rvalue",
506
            "",
507
            0,
508
            "foo",
509
            BASE_DEFAULT,
510
            ByteEndian::BigEndian,
511
            0,
512
            DETECT_BYTEMATH_FLAG_RVALUE_VAR,
513
        );
514
515
        valid_test(
516
            "bytes nbytes_var, offset -3933, oper +, rvalue myrvalue, result foo",
517
            0,
518
            -3933,
519
            ByteMathOperator::Addition,
520
            "rvalue",
521
            "nbytes_var",
522
            0,
523
            "foo",
524
            BASE_DEFAULT,
525
            ByteEndian::BigEndian,
526
            0,
527
            DETECT_BYTEMATH_FLAG_RVALUE_VAR | DETECT_BYTEMATH_FLAG_NBYTES_VAR,
528
        );
529
530
        // Out of order
531
        valid_test(
532
            "string dec, endian big, result other, rvalue 99, oper +, offset 3933, bytes 4",
533
            4,
534
            3933,
535
            ByteMathOperator::Addition,
536
            "",
537
            "",
538
            99,
539
            "other",
540
            ByteBase::BaseDec,
541
            ByteEndian::BigEndian,
542
            0,
543
            DETECT_BYTEMATH_FLAG_STRING | DETECT_BYTEMATH_FLAG_ENDIAN,
544
        );
545
    }
546
547
    #[test]
548
    fn test_parser_string_valid() {
549
        let mut bmd = DetectByteMathData {
550
            nbytes: 4,
551
            offset: 3933,
552
            oper: ByteMathOperator::Addition,
553
            rvalue_str: CString::new("myrvalue").unwrap().into_raw(),
554
            rvalue: 0,
555
            result: CString::new("foo").unwrap().into_raw(),
556
            endian: DETECT_BYTEMATH_ENDIAN_DEFAULT,
557
            base: ByteBase::BaseDec,
558
            flags: DETECT_BYTEMATH_FLAG_RVALUE_VAR | DETECT_BYTEMATH_FLAG_STRING,
559
            ..Default::default()
560
        };
561
562
        let (_, val) =
563
            parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string dec")
564
                .unwrap();
565
        assert_eq!(val, bmd);
566
567
        bmd.flags = DETECT_BYTEMATH_FLAG_RVALUE_VAR;
568
        bmd.base = BASE_DEFAULT;
569
        let (_, val) =
570
            parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo").unwrap();
571
        assert_eq!(val, bmd);
572
573
        bmd.flags = DETECT_BYTEMATH_FLAG_RVALUE_VAR | DETECT_BYTEMATH_FLAG_STRING;
574
        bmd.base = ByteBase::BaseHex;
575
        let (_, val) =
576
            parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string hex")
577
                .unwrap();
578
        assert_eq!(val, bmd);
579
580
        bmd.base = ByteBase::BaseOct;
581
        let (_, val) =
582
            parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string oct")
583
                .unwrap();
584
        assert_eq!(val, bmd);
585
    }
586
587
    #[test]
588
    fn test_parser_string_invalid() {
589
        assert!(parse_bytemath(
590
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string decimal"
591
        )
592
        .is_err());
593
        assert!(parse_bytemath(
594
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string hexadecimal"
595
        )
596
        .is_err());
597
        assert!(parse_bytemath(
598
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, string octal"
599
        )
600
        .is_err());
601
    }
602
603
    #[test]
604
    // bytes must be between 1 and 10; when combined with rshift/lshift, must be 4 or less
605
    fn test_parser_bytes_invalid() {
606
        assert!(
607
            parse_bytemath("bytes 0, offset 3933, oper +, rvalue myrvalue, result foo").is_err()
608
        );
609
        assert!(
610
            parse_bytemath("bytes 11, offset 3933, oper +, rvalue myrvalue, result foo").is_err()
611
        );
612
        assert!(
613
            parse_bytemath("bytes 5, offset 3933, oper >>, rvalue myrvalue, result foo").is_err()
614
        );
615
        assert!(
616
            parse_bytemath("bytes 5, offset 3933, oper <<, rvalue myrvalue, result foo").is_err()
617
        );
618
    }
619
620
    #[test]
621
    // a shift count of 64 or more parses; the rule loads with a warning from
622
    // DetectByteMathSetup() and the shift yields 0 at match time
623
    fn test_parser_shift_rvalue() {
624
        assert!(parse_bytemath("bytes 4, offset 3933, oper >>, rvalue 63, result foo").is_ok());
625
        assert!(parse_bytemath("bytes 4, offset 3933, oper <<, rvalue 63, result foo").is_ok());
626
        assert!(parse_bytemath("bytes 4, offset 3933, oper >>, rvalue 64, result foo").is_ok());
627
        assert!(parse_bytemath("bytes 4, offset 3933, oper <<, rvalue 64, result foo").is_ok());
628
        assert!(parse_bytemath("bytes 4, offset 3933, oper >>, rvalue 100, result foo").is_ok());
629
        assert!(parse_bytemath("bytes 4, offset 3933, oper +, rvalue 100, result foo").is_ok());
630
        assert!(
631
            parse_bytemath("bytes 4, offset 3933, oper >>, rvalue myrvalue, result foo").is_ok()
632
        );
633
    }
634
635
    #[test]
636
    fn test_parser_bitmask_invalid() {
637
        assert!(parse_bytemath(
638
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0x"
639
        )
640
        .is_err());
641
        assert!(parse_bytemath(
642
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask x12345678"
643
        )
644
        .is_err());
645
        assert!(parse_bytemath(
646
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask X12345678"
647
        )
648
        .is_err());
649
        assert!(parse_bytemath(
650
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0x123456789012"
651
        )
652
        .is_err());
653
        assert!(parse_bytemath(
654
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0q"
655
        )
656
        .is_err());
657
        assert!(parse_bytemath(
658
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask maple"
659
        )
660
        .is_err());
661
        assert!(parse_bytemath(
662
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0xGHIJKLMN"
663
        )
664
        .is_err());
665
        assert!(parse_bytemath(
666
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask #*#*@-"
667
        )
668
        .is_err());
669
    }
670
671
    #[test]
672
    fn test_parser_bitmask_valid() {
673
        let mut bmd = DetectByteMathData {
674
            nbytes: 4,
675
            offset: 3933,
676
            oper: ByteMathOperator::Addition,
677
            rvalue_str: CString::new("myrvalue").unwrap().into_raw(),
678
            rvalue: 0,
679
            result: CString::new("foo").unwrap().into_raw(),
680
            endian: ByteEndian::BigEndian,
681
            base: BASE_DEFAULT,
682
            flags: DETECT_BYTEMATH_FLAG_RVALUE_VAR | DETECT_BYTEMATH_FLAG_BITMASK,
683
            ..Default::default()
684
        };
685
686
        bmd.bitmask_val = 0x12345678;
687
        let (_, val) = parse_bytemath(
688
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0x12345678",
689
        )
690
        .unwrap();
691
        assert_eq!(val, bmd);
692
693
        bmd.bitmask_val = 0xffff1234;
694
        let (_, val) = parse_bytemath(
695
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask ffff1234",
696
        )
697
        .unwrap();
698
        assert_eq!(val, bmd);
699
700
        bmd.bitmask_val = 0xffff1234;
701
        let (_, val) = parse_bytemath(
702
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, bitmask 0Xffff1234",
703
        )
704
        .unwrap();
705
        assert_eq!(val, bmd);
706
    }
707
    #[test]
708
    fn test_parser_endian_valid() {
709
        let mut bmd = DetectByteMathData {
710
            nbytes: 4,
711
            offset: 3933,
712
            oper: ByteMathOperator::Addition,
713
            rvalue_str: CString::new("myrvalue").unwrap().into_raw(),
714
            rvalue: 0,
715
            result: CString::new("foo").unwrap().into_raw(),
716
            endian: ByteEndian::BigEndian,
717
            base: BASE_DEFAULT,
718
            flags: DETECT_BYTEMATH_FLAG_RVALUE_VAR | DETECT_BYTEMATH_FLAG_ENDIAN,
719
            ..Default::default()
720
        };
721
722
        let (_, val) =
723
            parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian big")
724
                .unwrap();
725
        assert_eq!(val, bmd);
726
727
        bmd.endian = ByteEndian::LittleEndian;
728
        let (_, val) = parse_bytemath(
729
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian little",
730
        )
731
        .unwrap();
732
        assert_eq!(val, bmd);
733
734
        bmd.endian = ByteEndian::EndianDCE;
735
        let (_, val) =
736
            parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, dce")
737
                .unwrap();
738
        assert_eq!(val, bmd);
739
740
        bmd.endian = DETECT_BYTEMATH_ENDIAN_DEFAULT;
741
        bmd.flags = DETECT_BYTEMATH_FLAG_RVALUE_VAR;
742
        let (_, val) =
743
            parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo").unwrap();
744
        assert_eq!(val, bmd);
745
    }
746
747
    #[test]
748
    fn test_parser_endian_invalid() {
749
        assert!(parse_bytemath(
750
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian bigger"
751
        )
752
        .is_err());
753
        assert!(parse_bytemath(
754
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian smaller"
755
        )
756
        .is_err());
757
758
        // endianess can only be specified once
759
        assert!(parse_bytemath(
760
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian big, dce"
761
        )
762
        .is_err());
763
        assert!(parse_bytemath(
764
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian small, endian big"
765
        )
766
        .is_err());
767
        assert!(parse_bytemath(
768
            "bytes 4, offset 3933, oper +, rvalue myrvalue, result foo, endian small, dce"
769
        )
770
        .is_err());
771
    }
772
773
    #[test]
774
    fn test_parser_oper_valid() {
775
        let mut bmd = DetectByteMathData {
776
            nbytes: 4,
777
            offset: 3933,
778
            oper: ByteMathOperator::Addition,
779
            rvalue_str: CString::new("myrvalue").unwrap().into_raw(),
780
            rvalue: 0,
781
            result: CString::new("foo").unwrap().into_raw(),
782
            endian: ByteEndian::BigEndian,
783
            base: BASE_DEFAULT,
784
            flags: DETECT_BYTEMATH_FLAG_RVALUE_VAR,
785
            ..Default::default()
786
        };
787
788
        let (_, val) =
789
            parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, result foo").unwrap();
790
        assert_eq!(val, bmd);
791
792
        bmd.oper = ByteMathOperator::Subtraction;
793
        let (_, val) =
794
            parse_bytemath("bytes 4, offset 3933, oper -, rvalue myrvalue, result foo").unwrap();
795
        assert_eq!(val, bmd);
796
797
        bmd.oper = ByteMathOperator::Multiplication;
798
        let (_, val) =
799
            parse_bytemath("bytes 4, offset 3933, oper *, rvalue myrvalue, result foo").unwrap();
800
        assert_eq!(val, bmd);
801
802
        bmd.oper = ByteMathOperator::Division;
803
        let (_, val) =
804
            parse_bytemath("bytes 4, offset 3933, oper /, rvalue myrvalue, result foo").unwrap();
805
        assert_eq!(val, bmd);
806
807
        bmd.oper = ByteMathOperator::RightShift;
808
        let (_, val) =
809
            parse_bytemath("bytes 4, offset 3933, oper >>, rvalue myrvalue, result foo").unwrap();
810
        assert_eq!(val, bmd);
811
812
        bmd.oper = ByteMathOperator::LeftShift;
813
        let (_, val) =
814
            parse_bytemath("bytes 4, offset 3933, oper <<, rvalue myrvalue, result foo").unwrap();
815
        assert_eq!(val, bmd);
816
    }
817
818
    #[test]
819
    fn test_parser_oper_invalid() {
820
        assert!(parse_bytemath("bytes 4, offset 0, oper !, rvalue myvalue, result foo").is_err());
821
        assert!(parse_bytemath("bytes 4, offset 0, oper ^, rvalue myvalue, result foo").is_err());
822
        assert!(parse_bytemath("bytes 4, offset 0, oper <>, rvalue myvalue, result foo").is_err());
823
        assert!(parse_bytemath("bytes 4, offset 0, oper ><, rvalue myvalue, result foo").is_err());
824
        assert!(parse_bytemath("bytes 4, offset 0, oper <, rvalue myvalue, result foo").is_err());
825
        assert!(parse_bytemath("bytes 4, offset 0, oper >, rvalue myvalue, result foo").is_err());
826
    }
827
828
    #[test]
829
    fn test_parser_rvalue_valid() {
830
        let mut bmd = DetectByteMathData {
831
            nbytes: 4,
832
            offset: 47303,
833
            oper: ByteMathOperator::Multiplication,
834
            rvalue_str: std::ptr::null_mut(),
835
            rvalue: 4294967295,
836
            result: CString::new("foo").unwrap().into_raw(),
837
            endian: DETECT_BYTEMATH_ENDIAN_DEFAULT,
838
            base: BASE_DEFAULT,
839
            ..Default::default()
840
        };
841
842
        let (_, val) =
843
            parse_bytemath("bytes 4, offset 47303, oper *, rvalue 4294967295      , result foo")
844
                .unwrap();
845
        assert_eq!(val, bmd);
846
847
        bmd.rvalue = 1;
848
        let (_, val) =
849
            parse_bytemath("bytes 4, offset 47303, oper *, rvalue 1, result foo").unwrap();
850
        assert_eq!(val, bmd);
851
852
        bmd.rvalue = 0;
853
        let (_, val) =
854
            parse_bytemath("bytes 4, offset 47303, oper *, rvalue 0, result foo").unwrap();
855
        assert_eq!(val, bmd);
856
    }
857
858
    #[test]
859
    fn test_parser_rvalue_invalid() {
860
        assert!(
861
            parse_bytemath("bytes 4, offset 47303, oper *, rvalue 4294967296, result foo").is_err()
862
        );
863
    }
864
865
    #[test]
866
    fn test_parser_offset_valid() {
867
        let mut bmd = DetectByteMathData {
868
            nbytes: 4,
869
            offset: -65535,
870
            oper: ByteMathOperator::Multiplication,
871
            rvalue_str: CString::new("myrvalue").unwrap().into_raw(),
872
            rvalue: 0,
873
            result: CString::new("foo").unwrap().into_raw(),
874
            endian: DETECT_BYTEMATH_ENDIAN_DEFAULT,
875
            base: BASE_DEFAULT,
876
            flags: DETECT_BYTEMATH_FLAG_RVALUE_VAR,
877
            ..Default::default()
878
        };
879
880
        let (_, val) =
881
            parse_bytemath("bytes 4, offset -65535, oper *, rvalue myrvalue, result foo").unwrap();
882
        assert_eq!(val, bmd);
883
884
        bmd.offset = 65535;
885
        let (_, val) =
886
            parse_bytemath("bytes 4, offset 65535, oper *, rvalue myrvalue, result foo").unwrap();
887
        assert_eq!(val, bmd);
888
    }
889
890
    #[test]
891
    // offset: numeric values must be between -65535 and 65535
892
    fn test_parser_offset_invalid() {
893
        assert!(
894
            parse_bytemath("bytes 4, offset -70000, oper *, rvalue myvalue, result foo").is_err()
895
        );
896
        assert!(
897
            parse_bytemath("bytes 4, offset 70000, oper +, rvalue myvalue, result foo").is_err()
898
        );
899
    }
900
901
    #[test]
902
    fn test_parser_incomplete_args() {
903
        assert!(parse_bytemath("").is_err());
904
        assert!(parse_bytemath("bytes 4").is_err());
905
        assert!(parse_bytemath("bytes 4, offset 0").is_err());
906
        assert!(parse_bytemath("bytes 4, offset 0, oper <<").is_err());
907
    }
908
909
    #[test]
910
    fn test_parser_missing_required() {
911
        assert!(
912
            parse_bytemath("endian big, offset 3933, oper +, rvalue myrvalue, result foo").is_err()
913
        );
914
        assert!(
915
            parse_bytemath("bytes 4, endian big, oper +, rvalue myrvalue, result foo,").is_err()
916
        );
917
        assert!(
918
            parse_bytemath("bytes 4, offset 3933, endian big, rvalue myrvalue, result foo")
919
                .is_err()
920
        );
921
        assert!(parse_bytemath("bytes 4, offset 3933, oper +, endian big, result foo").is_err());
922
        assert!(
923
            parse_bytemath("bytes 4, offset 3933, oper +, rvalue myrvalue, endian big").is_err()
924
        );
925
    }
926
927
    #[test]
928
    fn test_parser_invalid_args() {
929
        assert!(parse_bytemath("monkey banana").is_err());
930
        assert!(parse_bytemath("bytes nan").is_err());
931
        assert!(parse_bytemath("bytes 4, offset nan").is_err());
932
        assert!(parse_bytemath("bytes 4, offset 0, three 3, four 4, five 5, six 6, seven 7, eight 8, nine 9, ten 10, eleven 11").is_err());
933
        assert!(parse_bytemath("bytes 4, offset 0, oper ><, rvalue myrvalue").is_err());
934
        assert!(
935
            parse_bytemath("bytes 4, offset 0, oper +, rvalue myrvalue, endian endian").is_err()
936
        );
937
    }
938
    #[test]
939
    fn test_parser_multiple() {
940
        assert!(parse_bytemath(
941
            "bytes 4, bytes 4, offset 0, oper +, rvalue myrvalue, result myresult, endian big"
942
        )
943
        .is_err());
944
        assert!(parse_bytemath(
945
            "bytes 4, offset 0, offset 0, oper +, rvalue myrvalue, result myresult, endian big"
946
        )
947
        .is_err());
948
        assert!(parse_bytemath(
949
            "bytes 4, offset 0, oper +, oper +, rvalue myrvalue, result myresult, endian big"
950
        )
951
        .is_err());
952
        assert!(parse_bytemath("bytes 4, offset 0, oper +, rvalue myrvalue, rvalue myrvalue, result myresult, endian big").is_err());
953
        assert!(parse_bytemath("bytes 4, offset 0, oper +, rvalue myrvalue, result myresult, result myresult, endian big").is_err());
954
        assert!(parse_bytemath(
955
            "bytes 4, offset 0, oper +, rvalue myrvalue, result myresult, endian big, endian big"
956
        )
957
        .is_err());
958
    }
959
}