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:
parent
e8aef7cae1
commit
6d40b7232e
6 changed files with 92 additions and 22 deletions
|
@ -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) }
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue