1
Fork 0

Implement checked_add_duration for SystemTime

Since SystemTime is opaque there is no way to check if the result
of an addition will be in bounds. That makes the Add<Duration>
trait completely unusable with untrusted data. This is a big problem
because adding a Duration to UNIX_EPOCH is the standard way of
constructing a SystemTime from a unix timestamp.

This commit implements checked_add_duration(&self, &Duration) -> Option<SystemTime>
for std::time::SystemTime and as a prerequisite also for all platform
specific time structs. This also led to the refactoring of many
add_duration(&self, &Duration) -> SystemTime functions to avoid
redundancy (they now unwrap the result of checked_add_duration).

Some basic unit tests for the newly introduced function were added
too.
This commit is contained in:
Sebastian Geisler 2018-10-30 22:24:33 -07:00
parent e8aef7cae1
commit 6d40b7232e
6 changed files with 92 additions and 22 deletions

View file

@ -43,27 +43,36 @@ impl Timespec {
}
fn add_duration(&self, other: &Duration) -> Timespec {
let mut secs = other
self.checked_add_duration(other).expect("overflow when adding duration to time")
}
fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
let mut secs = match 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");
{
Some(ts) => ts,
None => return None,
};
// Nano calculations can't overflow because nanos are <1B which fit
// in a u32.
let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32;
if nsec >= NSEC_PER_SEC as u32 {
nsec -= NSEC_PER_SEC as u32;
secs = secs.checked_add(1).expect("overflow when adding \
duration to time");
secs = match secs.checked_add(1) {
Some(ts) => ts,
None => return None,
}
}
Timespec {
Some(Timespec {
t: libc::timespec {
tv_sec: secs,
tv_nsec: nsec as _,
},
}
})
}
fn sub_duration(&self, other: &Duration) -> Timespec {
@ -201,6 +210,10 @@ mod inner {
SystemTime { t: self.t.add_duration(other) }
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
self.t.checked_add_duration(other).map(|t| SystemTime { t })
}
pub fn sub_duration(&self, other: &Duration) -> SystemTime {
SystemTime { t: self.t.sub_duration(other) }
}
@ -325,6 +338,10 @@ mod inner {
SystemTime { t: self.t.add_duration(other) }
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
self.t.checked_add_duration(other).map(|t| SystemTime { t })
}
pub fn sub_duration(&self, other: &Duration) -> SystemTime {
SystemTime { t: self.t.sub_duration(other) }
}