From b38ccb969e09ae0856c235fcb496b3f3faf41c87 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Thu, 29 Aug 2024 11:39:53 -0400 Subject: [PATCH] Fix panic in DateTime::from_secs_f64 (#3806) ## Motivation and Context - #3805 ## Description Recover from floating point values extremely close to whole numbers by incrementing the epoch seconds value. ## Testing - proptest ## Checklist - [x] For changes to the smithy-rs codegen or runtime crates, I have created a changelog entry Markdown file in the `.changelog` directory, specifying "client," "server," or both in the `applies_to` key. - [x] For changes to the AWS SDK, generated SDK code, or SDK runtime crates, I have created a changelog entry Markdown file in the `.changelog` directory, specifying "aws-sdk-rust" in the `applies_to` key. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .changelog/1724779868.md | 9 ++++++ aws/rust-runtime/Cargo.lock | 2 +- rust-runtime/aws-smithy-types/Cargo.toml | 2 +- .../aws-smithy-types/src/date_time/mod.rs | 31 +++++++++++++++++-- 4 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 .changelog/1724779868.md diff --git a/.changelog/1724779868.md b/.changelog/1724779868.md new file mode 100644 index 0000000000..09e2238756 --- /dev/null +++ b/.changelog/1724779868.md @@ -0,0 +1,9 @@ +--- +applies_to: ["client", "server"] +authors: ["rcoh"] +references: ["smithy-rs#3805"] +breaking: false +new_feature: false +bug_fix: true +--- +Fix bug in `DateTime::from_secs_f64` where certain floating point values could lead to a panic. diff --git a/aws/rust-runtime/Cargo.lock b/aws/rust-runtime/Cargo.lock index 8193108229..2853b9b5fc 100644 --- a/aws/rust-runtime/Cargo.lock +++ b/aws/rust-runtime/Cargo.lock @@ -149,7 +149,7 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.4.1" +version = "1.4.2" dependencies = [ "arbitrary", "aws-credential-types", diff --git a/rust-runtime/aws-smithy-types/Cargo.toml b/rust-runtime/aws-smithy-types/Cargo.toml index 017ae99d65..134abbfded 100644 --- a/rust-runtime/aws-smithy-types/Cargo.toml +++ b/rust-runtime/aws-smithy-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aws-smithy-types" -version = "1.2.4" +version = "1.2.5" authors = [ "AWS Rust SDK Team ", "Russell Cohen ", diff --git a/rust-runtime/aws-smithy-types/src/date_time/mod.rs b/rust-runtime/aws-smithy-types/src/date_time/mod.rs index 970c7223c4..7b4a4dc58d 100644 --- a/rust-runtime/aws-smithy-types/src/date_time/mod.rs +++ b/rust-runtime/aws-smithy-types/src/date_time/mod.rs @@ -110,13 +110,22 @@ impl DateTime { /// DateTime::from_fractional_secs(1, 0.5), /// ); /// ``` - pub fn from_fractional_secs(epoch_seconds: i64, fraction: f64) -> Self { - let subsecond_nanos = (fraction * 1_000_000_000_f64) as u32; + pub fn from_fractional_secs(mut epoch_seconds: i64, fraction: f64) -> Self { + // Because of floating point issues, `fraction` can end up being 1.0 leading to + // a full second of subsecond nanos. In that case, rollover the subsecond into the second. + let mut subsecond_nanos = (fraction * 1_000_000_000_f64) as u32; + if subsecond_nanos == 1_000_000_000 { + epoch_seconds += 1; + subsecond_nanos = 0; + } DateTime::from_secs_and_nanos(epoch_seconds, subsecond_nanos) } /// Creates a `DateTime` from a number of seconds and sub-second nanos since the Unix epoch. /// + /// # Panics + /// This function will panic if `subsecond_nanos` is >= 1_000_000_000 + /// /// # Example /// ``` /// # use aws_smithy_types::DateTime; @@ -680,6 +689,17 @@ mod test { assert!(fifth == fifth); } + /// https://github.com/smithy-lang/smithy-rs/issues/3805 + #[test] + fn panic_in_fromsecs_f64() { + assert_eq!(DateTime::from_secs_f64(-1.0), DateTime::from_secs(-1)); + + assert_eq!( + DateTime::from_secs_f64(-1.95877825437922e-309), + DateTime::from_secs(0) + ); + } + const MIN_RFC_3339_MILLIS: i64 = -62135596800000; const MAX_RFC_3339_MILLIS: i64 = 253402300799999; @@ -699,4 +719,11 @@ mod test { assert_eq!(left.cmp(&right), left_str.cmp(&right_str)); } } + + proptest! { + #[test] + fn from_secs_f64_proptest(secs: f64) { + let _date = DateTime::from_secs_f64(secs); + } + } }