Skip to content

Commit

Permalink
Rollup merge of rust-lang#44220 - kennytm:fix-44216-instance-plus-max…
Browse files Browse the repository at this point in the history
…-duration-should-panic, r=alexcrichton

Properly detect overflow in Instance ± Duration.

Fix rust-lang#44216.

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 rust-lang#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`.
  • Loading branch information
Mark-Simulacrum authored Sep 6, 2017
2 parents 124ea4a + ef8c204 commit 4b4f518
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 18 deletions.
22 changes: 15 additions & 7 deletions src/libstd/sys/redox/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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.
Expand All @@ -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;
Expand All @@ -73,7 +81,7 @@ impl Timespec {
}
Timespec {
t: syscall::TimeSpec {
tv_sec: secs as i64,
tv_sec: secs,
tv_nsec: nsec as i32,
},
}
Expand Down
22 changes: 15 additions & 7 deletions src/libstd/sys/unix/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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.
Expand All @@ -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;
Expand All @@ -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,
},
}
Expand Down
9 changes: 6 additions & 3 deletions src/libstd/sys/windows/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion src/libstd/time/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,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);
}
}

Expand Down
18 changes: 18 additions & 0 deletions src/test/run-fail/issue-44216-add.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// error-pattern:overflow when

use std::time::{Instant, Duration};

fn main() {
let now = Instant::now();
let _ = now + Duration::from_secs(u64::max_value());
}
18 changes: 18 additions & 0 deletions src/test/run-fail/issue-44216-sub.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// error-pattern:overflow when

use std::time::{Instant, Duration};

fn main() {
let now = Instant::now();
let _ = now - Duration::from_secs(u64::max_value());
}

0 comments on commit 4b4f518

Please sign in to comment.