Skip to content

Commit

Permalink
fix potential race in AtomicU64 time monotonizer
Browse files Browse the repository at this point in the history
  • Loading branch information
the8472 committed Sep 16, 2021
1 parent 2b5ddf3 commit 2b512cc
Showing 1 changed file with 34 additions and 28 deletions.
62 changes: 34 additions & 28 deletions library/std/src/time/monotonic.rs
Expand Up @@ -37,35 +37,41 @@ pub mod inner {
// This could be a problem for programs that call instants at intervals greater
// than 68 years. Interstellar probes may want to ensure that actually_monotonic() is true.
let packed = (secs << 32) | nanos;
let old = mono.load(Relaxed);

if old == UNINITIALIZED || packed.wrapping_sub(old) < u64::MAX / 2 {
mono.store(packed, Relaxed);
raw
} else {
// Backslide occurred. We reconstruct monotonized time from the upper 32 bit of the
// passed in value and the 64bits loaded from the atomic
let seconds_lower = old >> 32;
let mut seconds_upper = secs & 0xffff_ffff_0000_0000;
if secs & 0xffff_ffff > seconds_lower {
// Backslide caused the lower 32bit of the seconds part to wrap.
// This must be the case because the seconds part is larger even though
// we are in the backslide branch, i.e. the seconds count should be smaller or equal.
//
// We assume that backslides are smaller than 2^32 seconds
// which means we need to add 1 to the upper half to restore it.
//
// Example:
// most recent observed time: 0xA1_0000_0000_0000_0000u128
// bits stored in AtomicU64: 0x0000_0000_0000_0000u64
// backslide by 1s
// caller time is 0xA0_ffff_ffff_0000_0000u128
// -> we can fix up the upper half time by adding 1 << 32
seconds_upper = seconds_upper.wrapping_add(0x1_0000_0000);
let mut old = mono.load(Relaxed);
loop {
if old == UNINITIALIZED || packed.wrapping_sub(old) < u64::MAX / 2 {
match mono.compare_exchange_weak(old, packed, Relaxed, Relaxed) {
Ok(_) => return raw,
Err(x) => {
old = x;
continue;
}
}
} else {
// Backslide occurred. We reconstruct monotonized time from the upper 32 bit of the
// passed in value and the 64bits loaded from the atomic
let seconds_lower = old >> 32;
let mut seconds_upper = secs & 0xffff_ffff_0000_0000;
if secs & 0xffff_ffff > seconds_lower {
// Backslide caused the lower 32bit of the seconds part to wrap.
// This must be the case because the seconds part is larger even though
// we are in the backslide branch, i.e. the seconds count should be smaller or equal.
//
// We assume that backslides are smaller than 2^32 seconds
// which means we need to add 1 to the upper half to restore it.
//
// Example:
// most recent observed time: 0xA1_0000_0000_0000_0000u128
// bits stored in AtomicU64: 0x0000_0000_0000_0000u64
// backslide by 1s
// caller time is 0xA0_ffff_ffff_0000_0000u128
// -> we can fix up the upper half time by adding 1 << 32
seconds_upper = seconds_upper.wrapping_add(0x1_0000_0000);
}
let secs = seconds_upper | seconds_lower;
let nanos = old as u32;
return ZERO.checked_add_duration(&Duration::new(secs, nanos)).unwrap();
}
let secs = seconds_upper | seconds_lower;
let nanos = old as u32;
ZERO.checked_add_duration(&Duration::new(secs, nanos)).unwrap()
}
}
}
Expand Down

0 comments on commit 2b512cc

Please sign in to comment.