Skip to content

Commit 7135d6f

Browse files
nissaoftheseatobz
andauthored
fix: lossy truncation in checked Instant arithmetic (#110)
Signed-off-by: Nissa <[email protected]> Co-authored-by: Toby Lawrence <[email protected]>
1 parent 67e9f61 commit 7135d6f

File tree

1 file changed

+31
-2
lines changed

1 file changed

+31
-2
lines changed

src/instant.rs

+31-2
Original file line numberDiff line numberDiff line change
@@ -171,14 +171,16 @@ impl Instant {
171171
/// `Instant` (which means it's inside the bounds of the underlying data structure), `None`
172172
/// otherwise.
173173
pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
174-
self.0.checked_add(duration.as_nanos() as u64).map(Instant)
174+
let total_nanos = u64::try_from(duration.as_nanos()).ok()?;
175+
self.0.checked_add(total_nanos).map(Instant)
175176
}
176177

177178
/// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as
178179
/// `Instant` (which means it's inside the bounds of the underlying data structure), `None`
179180
/// otherwise.
180181
pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
181-
self.0.checked_sub(duration.as_nanos() as u64).map(Instant)
182+
let total_nanos = u64::try_from(duration.as_nanos()).ok()?;
183+
self.0.checked_sub(total_nanos).map(Instant)
182184
}
183185
}
184186

@@ -371,4 +373,31 @@ mod tests {
371373
assert_eq!(t4.0, 1440);
372374
})
373375
}
376+
377+
// Test fix for issue #109
378+
#[test]
379+
#[cfg_attr(
380+
all(target_arch = "wasm32", target_os = "unknown"),
381+
wasm_bindgen_test::wasm_bindgen_test
382+
)]
383+
fn checked_arithmetic_u64_overflow() {
384+
fn nanos_to_dur(total_nanos: u128) -> Duration {
385+
let nanos_per_sec = Duration::from_secs(1).as_nanos();
386+
let secs = total_nanos / nanos_per_sec;
387+
let nanos = total_nanos % nanos_per_sec;
388+
let dur = Duration::new(secs as _, nanos as _);
389+
assert_eq!(dur.as_nanos(), total_nanos);
390+
dur
391+
}
392+
393+
let dur = nanos_to_dur(1 << 64);
394+
let now = Instant::now();
395+
396+
let behind = now.checked_sub(dur);
397+
let ahead = now.checked_add(dur);
398+
399+
assert_ne!(Duration::ZERO, dur);
400+
assert_ne!(Some(now), behind);
401+
assert_ne!(Some(now), ahead);
402+
}
374403
}

0 commit comments

Comments
 (0)