Skip to content

Commit ca94c75

Browse files
committedSep 10, 2017
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`.
2 parents b413f34 + 4962f9d commit ca94c75

8 files changed

+115
-21
lines changed
 

‎src/libstd/sys/redox/time.rs

+15-7
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use cmp::Ordering;
1212
use fmt;
1313
use sys::{cvt, syscall};
1414
use time::Duration;
15+
use convert::TryInto;
1516

1617
const NSEC_PER_SEC: u64 = 1_000_000_000;
1718

@@ -40,8 +41,12 @@ impl Timespec {
4041
}
4142

4243
fn add_duration(&self, other: &Duration) -> Timespec {
43-
let secs = (self.t.tv_sec as i64).checked_add(other.as_secs() as i64);
44-
let mut secs = secs.expect("overflow when adding duration to time");
44+
let mut secs = other
45+
.as_secs()
46+
.try_into() // <- target type would be `i64`
47+
.ok()
48+
.and_then(|secs| self.t.tv_sec.checked_add(secs))
49+
.expect("overflow when adding duration to time");
4550

4651
// Nano calculations can't overflow because nanos are <1B which fit
4752
// in a u32.
@@ -53,16 +58,19 @@ impl Timespec {
5358
}
5459
Timespec {
5560
t: syscall::TimeSpec {
56-
tv_sec: secs as i64,
61+
tv_sec: secs,
5762
tv_nsec: nsec as i32,
5863
},
5964
}
6065
}
6166

6267
fn sub_duration(&self, other: &Duration) -> Timespec {
63-
let secs = (self.t.tv_sec as i64).checked_sub(other.as_secs() as i64);
64-
let mut secs = secs.expect("overflow when subtracting duration \
65-
from time");
68+
let mut secs = other
69+
.as_secs()
70+
.try_into() // <- target type would be `i64`
71+
.ok()
72+
.and_then(|secs| self.t.tv_sec.checked_sub(secs))
73+
.expect("overflow when subtracting duration from time");
6674

6775
// Similar to above, nanos can't overflow.
6876
let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32;
@@ -73,7 +81,7 @@ impl Timespec {
7381
}
7482
Timespec {
7583
t: syscall::TimeSpec {
76-
tv_sec: secs as i64,
84+
tv_sec: secs,
7785
tv_nsec: nsec as i32,
7886
},
7987
}

‎src/libstd/sys/unix/time.rs

+15-7
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use libc;
1313
use time::Duration;
1414

1515
pub use self::inner::{Instant, SystemTime, UNIX_EPOCH};
16+
use convert::TryInto;
1617

1718
const NSEC_PER_SEC: u64 = 1_000_000_000;
1819

@@ -41,8 +42,12 @@ impl Timespec {
4142
}
4243

4344
fn add_duration(&self, other: &Duration) -> Timespec {
44-
let secs = (self.t.tv_sec as i64).checked_add(other.as_secs() as i64);
45-
let mut secs = secs.expect("overflow when adding duration to time");
45+
let mut secs = other
46+
.as_secs()
47+
.try_into() // <- target type would be `libc::time_t`
48+
.ok()
49+
.and_then(|secs| self.t.tv_sec.checked_add(secs))
50+
.expect("overflow when adding duration to time");
4651

4752
// Nano calculations can't overflow because nanos are <1B which fit
4853
// in a u32.
@@ -54,16 +59,19 @@ impl Timespec {
5459
}
5560
Timespec {
5661
t: libc::timespec {
57-
tv_sec: secs as libc::time_t,
62+
tv_sec: secs,
5863
tv_nsec: nsec as libc::c_long,
5964
},
6065
}
6166
}
6267

6368
fn sub_duration(&self, other: &Duration) -> Timespec {
64-
let secs = (self.t.tv_sec as i64).checked_sub(other.as_secs() as i64);
65-
let mut secs = secs.expect("overflow when subtracting duration \
66-
from time");
69+
let mut secs = other
70+
.as_secs()
71+
.try_into() // <- target type would be `libc::time_t`
72+
.ok()
73+
.and_then(|secs| self.t.tv_sec.checked_sub(secs))
74+
.expect("overflow when subtracting duration from time");
6775

6876
// Similar to above, nanos can't overflow.
6977
let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32;
@@ -74,7 +82,7 @@ impl Timespec {
7482
}
7583
Timespec {
7684
t: libc::timespec {
77-
tv_sec: secs as libc::time_t,
85+
tv_sec: secs,
7886
tv_nsec: nsec as libc::c_long,
7987
},
8088
}

‎src/libstd/sys/windows/time.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use sys::c;
1616
use sys::cvt;
1717
use sys_common::mul_div_u64;
1818
use time::Duration;
19+
use convert::TryInto;
1920

2021
const NANOS_PER_SEC: u64 = 1_000_000_000;
2122
const INTERVALS_PER_SEC: u64 = NANOS_PER_SEC / 100;
@@ -173,9 +174,11 @@ impl From<c::FILETIME> for SystemTime {
173174
}
174175

175176
fn dur2intervals(d: &Duration) -> i64 {
176-
d.as_secs().checked_mul(INTERVALS_PER_SEC).and_then(|i| {
177-
i.checked_add(d.subsec_nanos() as u64 / 100)
178-
}).expect("overflow when converting duration to intervals") as i64
177+
d.as_secs()
178+
.checked_mul(INTERVALS_PER_SEC)
179+
.and_then(|i| i.checked_add(d.subsec_nanos() as u64 / 100))
180+
.and_then(|i| i.try_into().ok())
181+
.expect("overflow when converting duration to intervals")
179182
}
180183

181184
fn intervals2dur(intervals: u64) -> Duration {

‎src/libstd/time/mod.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ mod tests {
509509
let dur = dur.duration();
510510
assert!(a > b);
511511
assert_almost_eq!(b + dur, a);
512-
assert_almost_eq!(b - dur, a);
512+
assert_almost_eq!(a - dur, b);
513513
}
514514
}
515515

@@ -520,9 +520,12 @@ mod tests {
520520

521521
assert_almost_eq!(a - second + second, a);
522522

523-
let eighty_years = second * 60 * 60 * 24 * 365 * 80;
524-
assert_almost_eq!(a - eighty_years + eighty_years, a);
525-
assert_almost_eq!(a - (eighty_years * 10) + (eighty_years * 10), a);
523+
// A difference of 80 and 800 years cannot fit inside a 32-bit time_t
524+
if !(cfg!(unix) && ::mem::size_of::<::libc::time_t>() <= 4) {
525+
let eighty_years = second * 60 * 60 * 24 * 365 * 80;
526+
assert_almost_eq!(a - eighty_years + eighty_years, a);
527+
assert_almost_eq!(a - (eighty_years * 10) + (eighty_years * 10), a);
528+
}
526529

527530
let one_second_from_epoch = UNIX_EPOCH + Duration::new(1, 0);
528531
let one_second_from_epoch2 = UNIX_EPOCH + Duration::new(0, 500_000_000)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// error-pattern:overflow
12+
13+
use std::time::{Instant, Duration};
14+
15+
fn main() {
16+
let now = Instant::now();
17+
let _ = now + Duration::from_secs(u64::max_value());
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// error-pattern:overflow
12+
13+
use std::time::{Duration, SystemTime};
14+
15+
fn main() {
16+
let now = SystemTime::now();
17+
let _ = now + Duration::from_secs(u64::max_value());
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// error-pattern:overflow
12+
13+
use std::time::{Instant, Duration};
14+
15+
fn main() {
16+
let now = Instant::now();
17+
let _ = now - Duration::from_secs(u64::max_value());
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// error-pattern:overflow
12+
13+
use std::time::{Duration, SystemTime};
14+
15+
fn main() {
16+
let now = SystemTime::now();
17+
let _ = now - Duration::from_secs(u64::max_value());
18+
}

0 commit comments

Comments
 (0)
Please sign in to comment.