about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-09-10 18:37:27 +0000
committerbors <bors@rust-lang.org>2017-09-10 18:37:27 +0000
commitca94c75c527006be0824c764773380bbeea6030b (patch)
tree947a43561b8045589fdc9f3c13cbadbf4e5ca0bb /src/libstd
parentb413f34087d913b80cf432a66d05ec5be11f3515 (diff)
parent4962f9d72528602f70c7017d95063ef93a3c8967 (diff)
downloadrust-ca94c75c527006be0824c764773380bbeea6030b.tar.gz
rust-ca94c75c527006be0824c764773380bbeea6030b.zip
Auto merge of #44220 - kennytm:fix-44216-instance-plus-max-duration-should-panic, r=alexcrichton
Properly detect overflow in Instance ± Duration.

Fix #44216.
Fix #42622

The computation `Instant::now() + Duration::from_secs(u64::max_value())` now panics. The call `receiver.recv_timeout(Duration::from_secs(u64::max_value()))`, which involves such time addition, will also panic.

The reason #44216 arises is because of an unchecked cast from `u64` to `i64`, making the duration equivalent to -1 second.

Note that the current implementation is over-conservative, since e.g. (-2⁶²) + (2⁶³) is perfectly fine for an `i64`, yet this is rejected because (2⁶³) overflows the `i64`.
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/sys/redox/time.rs22
-rw-r--r--src/libstd/sys/unix/time.rs22
-rw-r--r--src/libstd/sys/windows/time.rs9
-rw-r--r--src/libstd/time/mod.rs11
4 files changed, 43 insertions, 21 deletions
diff --git a/src/libstd/sys/redox/time.rs b/src/libstd/sys/redox/time.rs
index dea406efe6c..6c071afd42d 100644
--- a/src/libstd/sys/redox/time.rs
+++ b/src/libstd/sys/redox/time.rs
@@ -12,6 +12,7 @@ use cmp::Ordering;
 use fmt;
 use sys::{cvt, syscall};
 use time::Duration;
+use convert::TryInto;
 
 const NSEC_PER_SEC: u64 = 1_000_000_000;
 
@@ -40,8 +41,12 @@ impl Timespec {
     }
 
     fn add_duration(&self, other: &Duration) -> Timespec {
-        let secs = (self.t.tv_sec as i64).checked_add(other.as_secs() as i64);
-        let mut secs = secs.expect("overflow when adding duration to time");
+        let mut secs = other
+            .as_secs()
+            .try_into() // <- target type would be `i64`
+            .ok()
+            .and_then(|secs| self.t.tv_sec.checked_add(secs))
+            .expect("overflow when adding duration to time");
 
         // Nano calculations can't overflow because nanos are <1B which fit
         // in a u32.
@@ -53,16 +58,19 @@ impl Timespec {
         }
         Timespec {
             t: syscall::TimeSpec {
-                tv_sec: secs as i64,
+                tv_sec: secs,
                 tv_nsec: nsec as i32,
             },
         }
     }
 
     fn sub_duration(&self, other: &Duration) -> Timespec {
-        let secs = (self.t.tv_sec as i64).checked_sub(other.as_secs() as i64);
-        let mut secs = secs.expect("overflow when subtracting duration \
-                                    from time");
+        let mut secs = other
+            .as_secs()
+            .try_into() // <- target type would be `i64`
+            .ok()
+            .and_then(|secs| self.t.tv_sec.checked_sub(secs))
+            .expect("overflow when subtracting duration from time");
 
         // Similar to above, nanos can't overflow.
         let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32;
@@ -73,7 +81,7 @@ impl Timespec {
         }
         Timespec {
             t: syscall::TimeSpec {
-                tv_sec: secs as i64,
+                tv_sec: secs,
                 tv_nsec: nsec as i32,
             },
         }
diff --git a/src/libstd/sys/unix/time.rs b/src/libstd/sys/unix/time.rs
index a1ad94872de..c1bea95ce91 100644
--- a/src/libstd/sys/unix/time.rs
+++ b/src/libstd/sys/unix/time.rs
@@ -13,6 +13,7 @@ use libc;
 use time::Duration;
 
 pub use self::inner::{Instant, SystemTime, UNIX_EPOCH};
+use convert::TryInto;
 
 const NSEC_PER_SEC: u64 = 1_000_000_000;
 
@@ -41,8 +42,12 @@ impl Timespec {
     }
 
     fn add_duration(&self, other: &Duration) -> Timespec {
-        let secs = (self.t.tv_sec as i64).checked_add(other.as_secs() as i64);
-        let mut secs = secs.expect("overflow when adding duration to time");
+        let mut secs = other
+            .as_secs()
+            .try_into() // <- target type would be `libc::time_t`
+            .ok()
+            .and_then(|secs| self.t.tv_sec.checked_add(secs))
+            .expect("overflow when adding duration to time");
 
         // Nano calculations can't overflow because nanos are <1B which fit
         // in a u32.
@@ -54,16 +59,19 @@ impl Timespec {
         }
         Timespec {
             t: libc::timespec {
-                tv_sec: secs as libc::time_t,
+                tv_sec: secs,
                 tv_nsec: nsec as libc::c_long,
             },
         }
     }
 
     fn sub_duration(&self, other: &Duration) -> Timespec {
-        let secs = (self.t.tv_sec as i64).checked_sub(other.as_secs() as i64);
-        let mut secs = secs.expect("overflow when subtracting duration \
-                                    from time");
+        let mut secs = other
+            .as_secs()
+            .try_into() // <- target type would be `libc::time_t`
+            .ok()
+            .and_then(|secs| self.t.tv_sec.checked_sub(secs))
+            .expect("overflow when subtracting duration from time");
 
         // Similar to above, nanos can't overflow.
         let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32;
@@ -74,7 +82,7 @@ impl Timespec {
         }
         Timespec {
             t: libc::timespec {
-                tv_sec: secs as libc::time_t,
+                tv_sec: secs,
                 tv_nsec: nsec as libc::c_long,
             },
         }
diff --git a/src/libstd/sys/windows/time.rs b/src/libstd/sys/windows/time.rs
index ef8ed606526..1be29b5139a 100644
--- a/src/libstd/sys/windows/time.rs
+++ b/src/libstd/sys/windows/time.rs
@@ -16,6 +16,7 @@ use sys::c;
 use sys::cvt;
 use sys_common::mul_div_u64;
 use time::Duration;
+use convert::TryInto;
 
 const NANOS_PER_SEC: u64 = 1_000_000_000;
 const INTERVALS_PER_SEC: u64 = NANOS_PER_SEC / 100;
@@ -173,9 +174,11 @@ impl From<c::FILETIME> for SystemTime {
 }
 
 fn dur2intervals(d: &Duration) -> i64 {
-    d.as_secs().checked_mul(INTERVALS_PER_SEC).and_then(|i| {
-        i.checked_add(d.subsec_nanos() as u64 / 100)
-    }).expect("overflow when converting duration to intervals") as i64
+    d.as_secs()
+        .checked_mul(INTERVALS_PER_SEC)
+        .and_then(|i| i.checked_add(d.subsec_nanos() as u64 / 100))
+        .and_then(|i| i.try_into().ok())
+        .expect("overflow when converting duration to intervals")
 }
 
 fn intervals2dur(intervals: u64) -> Duration {
diff --git a/src/libstd/time/mod.rs b/src/libstd/time/mod.rs
index e0dd8cfe62e..d4993ded843 100644
--- a/src/libstd/time/mod.rs
+++ b/src/libstd/time/mod.rs
@@ -509,7 +509,7 @@ mod tests {
                 let dur = dur.duration();
                 assert!(a > b);
                 assert_almost_eq!(b + dur, a);
-                assert_almost_eq!(b - dur, a);
+                assert_almost_eq!(a - dur, b);
             }
         }
 
@@ -520,9 +520,12 @@ mod tests {
 
         assert_almost_eq!(a - second + second, a);
 
-        let eighty_years = second * 60 * 60 * 24 * 365 * 80;
-        assert_almost_eq!(a - eighty_years + eighty_years, a);
-        assert_almost_eq!(a - (eighty_years * 10) + (eighty_years * 10), a);
+        // A difference of 80 and 800 years cannot fit inside a 32-bit time_t
+        if !(cfg!(unix) && ::mem::size_of::<::libc::time_t>() <= 4) {
+            let eighty_years = second * 60 * 60 * 24 * 365 * 80;
+            assert_almost_eq!(a - eighty_years + eighty_years, a);
+            assert_almost_eq!(a - (eighty_years * 10) + (eighty_years * 10), a);
+        }
 
         let one_second_from_epoch = UNIX_EPOCH + Duration::new(1, 0);
         let one_second_from_epoch2 = UNIX_EPOCH + Duration::new(0, 500_000_000)