From ad8a3246fa2c3efa5f8112ad5235eab2424d39ff Mon Sep 17 00:00:00 2001 From: Weijun Huang Date: Sat, 18 Mar 2023 22:14:17 +0100 Subject: [PATCH] making use of checked multiplication and addition to avoid silent overflow --- arrow-cast/src/cast.rs | 22 ++++++++++++++++++++++ arrow-cast/src/parse.rs | 27 ++++++++++++++++----------- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/arrow-cast/src/cast.rs b/arrow-cast/src/cast.rs index 1bd5027406b9..72d1bc1cb254 100644 --- a/arrow-cast/src/cast.rs +++ b/arrow-cast/src/cast.rs @@ -5273,6 +5273,28 @@ mod tests { IntervalUnit::DayTime, r#"Cast error: Cannot cast 1 day 1.5 milliseconds to IntervalDayTime because the nanos part isn't multiple of milliseconds"# ); + + // overflow + test_unsafe_string_to_interval_err!( + vec![Some(format!( + "{} century {} year {} month", + i64::MAX - 2, + i64::MAX - 2, + i64::MAX - 2 + ))], + IntervalUnit::DayTime, + r#"Parser error: Parsed interval field value out of range: 11068046444225730000000 months 331764692165666300000000 days 28663672503769583000000000000000000000 nanos"# + ); + test_unsafe_string_to_interval_err!( + vec![Some(format!( + "{} year {} month {} day", + i64::MAX - 2, + i64::MAX - 2, + i64::MAX - 2 + ))], + IntervalUnit::MonthDayNano, + r#"Parser error: Parsed interval field value out of range: 110680464442257310000 months 3043712772162076000000 days 262179884170819100000000000000000000 nanos"# + ); } #[test] diff --git a/arrow-cast/src/parse.rs b/arrow-cast/src/parse.rs index 30cebb4bf3d0..ced951ca8f65 100644 --- a/arrow-cast/src/parse.rs +++ b/arrow-cast/src/parse.rs @@ -838,13 +838,13 @@ fn parse_interval(leading_field: &str, value: &str) -> Result { - align_interval_parts(interval_period * 1200_f64, 0.0, 0.0) + align_interval_parts(interval_period.mul_checked(1200_f64)?, 0.0, 0.0) } IntervalType::Decade => { - align_interval_parts(interval_period * 120_f64, 0.0, 0.0) + align_interval_parts(interval_period.mul_checked(120_f64)?, 0.0, 0.0) } IntervalType::Year => { - align_interval_parts(interval_period * 12_f64, 0.0, 0.0) + align_interval_parts(interval_period.mul_checked(12_f64)?, 0.0, 0.0) } IntervalType::Month => align_interval_parts(interval_period, 0.0, 0.0), IntervalType::Week => align_interval_parts(0.0, interval_period * 7_f64, 0.0), @@ -852,16 +852,21 @@ fn parse_interval(leading_field: &str, value: &str) -> Result Ok(( 0, 0, - (interval_period * SECONDS_PER_HOUR * NANOS_PER_SECOND) as i64, + (interval_period.mul_checked(SECONDS_PER_HOUR * NANOS_PER_SECOND))? + as i64, + )), + IntervalType::Minute => Ok(( + 0, + 0, + (interval_period.mul_checked(60_f64 * NANOS_PER_SECOND))? as i64, + )), + IntervalType::Second => Ok(( + 0, + 0, + (interval_period.mul_checked(NANOS_PER_SECOND))? as i64, )), - IntervalType::Minute => { - Ok((0, 0, (interval_period * 60_f64 * NANOS_PER_SECOND) as i64)) - } - IntervalType::Second => { - Ok((0, 0, (interval_period * NANOS_PER_SECOND) as i64)) - } IntervalType::Millisecond => { - Ok((0, 0, (interval_period * 1_000_000f64) as i64)) + Ok((0, 0, (interval_period.mul_checked(1_000_000f64))? as i64)) } } };