Skip to content

Commit 665f529

Browse files
committed
Avoid Ticks overflow
1 parent 8c3b7ad commit 665f529

File tree

1 file changed

+33
-7
lines changed
  • intel-sgx/insecure-time/src

1 file changed

+33
-7
lines changed

intel-sgx/insecure-time/src/lib.rs

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ pub enum Error {
6161
UnexpectedTscInfo,
6262
UnknownFrequency,
6363
FrequencyCannotBeDetermined,
64+
TypeOverflow,
6465
}
6566

6667
#[cfg(not(target_env = "sgx"))]
@@ -94,10 +95,14 @@ impl PartialOrd<Ticks> for Ticks {
9495
}
9596

9697
impl Add for Ticks {
97-
type Output = Ticks;
98+
type Output = Result<Ticks, Error>;
9899

99100
fn add(self, other: Self) -> Self::Output {
100-
Ticks(AtomicU64::new(self.0.load(Ordering::Relaxed) + other.0.load(Ordering::Relaxed)))
101+
let t0 = self.0.load(Ordering::Relaxed);
102+
let t1 = other.0.load(Ordering::Relaxed);
103+
t0.checked_add(t1)
104+
.ok_or(Error::TypeOverflow)
105+
.map(|sum| Ticks(AtomicU64::new(sum)))
101106
}
102107
}
103108

@@ -112,7 +117,21 @@ impl Ticks {
112117
Ticks(AtomicU64::new(t))
113118
}
114119

120+
pub const fn max() -> Self {
121+
Ticks(AtomicU64::new(u64::MAX))
122+
}
123+
115124
pub fn now() -> Self {
125+
// The RDTSC instruction reads the time-stamp counter and is guaranteed to
126+
// return a monotonically increasing unique value whenever executed, except
127+
// for a 64-bit counter wraparound. Intel guarantees that the time-stamp
128+
// counter will not wraparound within 10 years after being reset. The period
129+
// for counter wrap is longer for Pentium 4, Intel Xeon, P6 family, and
130+
// Pentium processors.
131+
// Source: Intel x86 manual Volume 3 Chapter 19.17 (Time-stamp counter)
132+
//
133+
// However, note that an attacker may arbitarily set this value on the
134+
// host/hypervisor
116135
Ticks(Rdtscp::read().into())
117136
}
118137

@@ -132,11 +151,12 @@ impl Ticks {
132151
self.0.store(t, Ordering::Relaxed);
133152
}
134153

135-
pub fn from_duration(duration: Duration, freq: &Freq) -> Self {
154+
pub fn from_duration(duration: Duration, freq: &Freq) -> Result<Self, Error> {
136155
let freq = freq.as_u64();
137-
let ticks_secs = duration.as_secs() * freq;
138-
let ticks_nsecs = duration.subsec_nanos() as u64 * freq / NANOS_PER_SEC;
139-
Ticks::new(ticks_secs + ticks_nsecs)
156+
let ticks_secs = duration.as_secs().checked_mul(freq).ok_or(Error::TypeOverflow)?;
157+
let ticks_nsecs = (duration.subsec_nanos() as u64)
158+
.checked_mul(freq).ok_or(Error::TypeOverflow)? / NANOS_PER_SEC;
159+
Ok(Ticks::new(ticks_secs + ticks_nsecs))
140160
}
141161

142162
pub fn as_duration_ex(&self, freq: &Freq) -> Duration {
@@ -613,7 +633,13 @@ impl<T: NativeTime> Tsc<T> {
613633
} else {
614634
// Re-estimate freq when a time threshold is reached
615635
if let Ok(Some((now, next_sync_interval, estimated_freq))) = self.resync_clocks(&f, &frequency_learning_period, &max_acceptable_drift, &max_sync_interval) {
616-
next_sync.set(tsc_now + Ticks::from_duration(next_sync_interval, &estimated_freq));
636+
// When `next_tsc` overflows, the system has been running for over 10
637+
// years, or an attacker manipulated the TSC value. As we can't trust TSC
638+
// anyway, we do the simple thing and continue trying to sync
639+
let duration = Ticks::from_duration(next_sync_interval, &estimated_freq);
640+
if let Ok(next_tsc) = tsc_now + duration.unwrap_or(Ticks::max()) {
641+
next_sync.set(next_tsc);
642+
}
617643
frequency.set(&estimated_freq);
618644
return now;
619645
}

0 commit comments

Comments
 (0)