diff --git a/changelog.d/23048_fix_arith_logic_dnstap_parse.fix.md b/changelog.d/23048_fix_arith_logic_dnstap_parse.fix.md new file mode 100644 index 0000000000000..1a213d2bd4e4a --- /dev/null +++ b/changelog.d/23048_fix_arith_logic_dnstap_parse.fix.md @@ -0,0 +1,3 @@ +Added checks for integer operations with timestamps which used in `dnstap-parser::DnstapParser::parse`. + +authors: wooffie diff --git a/lib/dnstap-parser/src/parser.rs b/lib/dnstap-parser/src/parser.rs index 6a950c1a152b7..939f45a0b340d 100644 --- a/lib/dnstap-parser/src/parser.rs +++ b/lib/dnstap-parser/src/parser.rs @@ -236,7 +236,7 @@ impl DnstapParser { dnstap_message_type_id, dnstap_message.query_message.as_ref(), &DNSTAP_MESSAGE_REQUEST_TYPE_IDS, - ); + )?; } if let Some(response_time_sec) = dnstap_message.response_time_sec { @@ -248,7 +248,7 @@ impl DnstapParser { dnstap_message_type_id, dnstap_message.response_message.as_ref(), &DNSTAP_MESSAGE_RESPONSE_TYPE_IDS, - ); + )?; } DnstapParser::parse_dnstap_message_type( @@ -366,10 +366,29 @@ impl DnstapParser { dnstap_message_type_id: i32, message: Option<&Vec>, type_ids: &HashSet, - ) { + ) -> Result<()> { + if time_sec > i64::MAX as u64 { + return Err(Error::from("Cannot parse timestamp")); + } + let (time_in_nanosec, query_time_nsec) = match time_nsec { - Some(nsec) => (time_sec as i64 * 1_000_000_000_i64 + nsec as i64, nsec), - None => (time_sec as i64 * 1_000_000_000_i64, 0), + Some(nsec) => { + if let Some(time_in_ns) = (time_sec as i64) + .checked_mul(1_000_000_000) + .and_then(|v| v.checked_add(nsec as i64)) + { + (time_in_ns, nsec) + } else { + return Err(Error::from("Cannot parse timestamp")); + } + } + None => { + if let Some(time_in_ns) = (time_sec as i64).checked_mul(1_000_000_000) { + (time_in_ns, 0) + } else { + return Err(Error::from("Cannot parse timestamp")); + } + } }; if type_ids.contains(&dnstap_message_type_id) { @@ -392,6 +411,7 @@ impl DnstapParser { "ns", ); } + Ok(()) } fn parse_dnstap_message_socket_family<'a>( @@ -1321,6 +1341,30 @@ mod tests { assert!(e.to_string().contains("Protobuf message")); } + #[test] + fn test_parse_dnstap_data_with_invalid_timestamp() { + fn test_one_timestamp_parse(time_sec: u64, time_nsec: Option) -> Result<()> { + let mut event = LogEvent::default(); + let root = owned_value_path!(); + let type_ids = HashSet::new(); + DnstapParser::parse_dnstap_message_time( + &mut event, &root, time_sec, time_nsec, 0, None, &type_ids, + ) + } + // okay case + assert!(test_one_timestamp_parse(1337, Some(42)).is_ok()); + // overflow in cast (u64 -> i64) + assert!(test_one_timestamp_parse(u64::MAX, Some(42)).is_err()); + assert!(test_one_timestamp_parse(u64::MAX, None).is_err()); + // overflow in multiplication + assert!(test_one_timestamp_parse(i64::MAX as u64, Some(42)).is_err()); + assert!(test_one_timestamp_parse(i64::MAX as u64, None).is_err()); + // overflow in add + assert!( + test_one_timestamp_parse((i64::MAX / 1_000_000_000) as u64, Some(u32::MAX)).is_err() + ) + } + #[test] fn test_get_socket_family_name() { assert_eq!("INET", to_socket_family_name(1).unwrap());