From 2ede709e6ba4acd619ff6d78fd3b2b13a5c61c78 Mon Sep 17 00:00:00 2001 From: Dan Hable Date: Wed, 30 Oct 2024 15:02:05 -0500 Subject: [PATCH] fix(convert): return error when conversion exceeds f64 bounds Prior to this commit, string values that would exceed the f64 bounds would return `f64::INFINITY`. This leaks a type into the VRL scripts that could cause a panic without support to check for the value. Fixes #1107 --- changelog.d/1107.fix.md | 2 ++ src/compiler/conversion/mod.rs | 5 ++++ src/compiler/conversion/tests/mod.rs | 35 +++++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 changelog.d/1107.fix.md diff --git a/changelog.d/1107.fix.md b/changelog.d/1107.fix.md new file mode 100644 index 0000000000..343d0f0d5d --- /dev/null +++ b/changelog.d/1107.fix.md @@ -0,0 +1,2 @@ +Fixes the `to_float` function to return an error instead of "infinity" when parsing a string outside +the f64::MAX / f64::MIN bounds. diff --git a/src/compiler/conversion/mod.rs b/src/compiler/conversion/mod.rs index 488b8838e0..eba6e9539f 100644 --- a/src/compiler/conversion/mod.rs +++ b/src/compiler/conversion/mod.rs @@ -159,6 +159,11 @@ impl Conversion { let parsed = s .parse::() .with_context(|_| FloatParseSnafu { s: s.clone() })?; + if parsed.is_infinite() { + return Err(Error::NanFloat { + s: format!("Invalid float \"{s}\": number too large to fit in target type"), + }); + } let f = NotNan::new(parsed).map_err(|_| Error::NanFloat { s: s.to_string() })?; f.into() } diff --git a/src/compiler/conversion/tests/mod.rs b/src/compiler/conversion/tests/mod.rs index 958c9dc77c..7c36feca0c 100644 --- a/src/compiler/conversion/tests/mod.rs +++ b/src/compiler/conversion/tests/mod.rs @@ -2,7 +2,8 @@ use bytes::Bytes; use chrono::{DateTime, Utc}; use ordered_float::NotNan; -use crate::compiler::conversion::parse_bool; +use crate::compiler::conversion::{parse_bool, Conversion, Error}; +use crate::compiler::TimeZone; #[cfg(unix)] // see https://github.com/vectordotdev/vector/issues/1201 mod unix; @@ -91,3 +92,35 @@ fn parse_bool_errors() { assert!(parse_bool("yes or no").is_err()); assert!(parse_bool("123.4").is_err()); } + +fn convert_float(input: impl ToString) -> Result { + let input = input.to_string(); + let converter = Conversion::parse("float", TimeZone::Local).expect("float conversion"); + converter.convert::(input.into()) +} + +#[test] +fn convert_float_ok() { + let max_float = format!("17976931348623157{}", "0".repeat(292)); + let min_float = format!("-{max_float}"); + + assert_eq!(convert_float(max_float), Ok(StubValue::Float(f64::MAX))); + assert_eq!(convert_float("1"), Ok(StubValue::Float(1.0))); + assert_eq!(convert_float("1.23"), Ok(StubValue::Float(1.23))); + assert_eq!(convert_float("0.0"), Ok(StubValue::Float(0.0))); + assert_eq!(convert_float("-0.0"), Ok(StubValue::Float(0.0))); + assert_eq!(convert_float("-1"), Ok(StubValue::Float(-1.0))); + assert_eq!(convert_float("-1.23"), Ok(StubValue::Float(-1.23))); + assert_eq!(convert_float(min_float), Ok(StubValue::Float(f64::MIN))); +} + +#[test] +fn convert_float_errors() { + let exceeds_max_float = format!("17976931348623159{}", "0".repeat(292)); // last number inc by 2 + let exceeds_min_float = format!("-{exceeds_max_float}"); + + assert!(convert_float("abc").is_err()); + assert!(convert_float("1.23.4").is_err()); + assert!(convert_float(exceeds_max_float).is_err()); + assert!(convert_float(exceeds_min_float).is_err()); +}