Skip to content

Commit

Permalink
fix(convert): return error when conversion exceeds f64 bounds
Browse files Browse the repository at this point in the history
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 vectordotdev#1107
  • Loading branch information
dhable committed Nov 1, 2024
1 parent c69a52e commit 2ede709
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 1 deletion.
2 changes: 2 additions & 0 deletions changelog.d/1107.fix.md
Original file line number Diff line number Diff line change
@@ -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.
5 changes: 5 additions & 0 deletions src/compiler/conversion/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@ impl Conversion {
let parsed = s
.parse::<f64>()
.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()
}
Expand Down
35 changes: 34 additions & 1 deletion src/compiler/conversion/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<StubValue, Error> {
let input = input.to_string();
let converter = Conversion::parse("float", TimeZone::Local).expect("float conversion");
converter.convert::<StubValue>(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());
}

0 comments on commit 2ede709

Please sign in to comment.