Skip to content

Commit 3442d23

Browse files
committed
Improve wait_timeout_sgx, simplify usercalls::wait
1 parent c5d1fcd commit 3442d23

File tree

2 files changed

+56
-28
lines changed

2 files changed

+56
-28
lines changed

src/libstd/sys/sgx/abi/usercalls/mod.rs

+6-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::cmp;
2+
use crate::convert::TryFrom;
23
use crate::io::{Error as IoError, IoSlice, IoSliceMut, Result as IoResult};
34
use crate::sys::rand::rdrand64;
45
use crate::time::Duration;
@@ -159,17 +160,11 @@ pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult<u64> {
159160
// to make things work in other cases. Note that in the SGX threat
160161
// model the enclave runner which is serving the wait usercall is not
161162
// trusted to ensure accurate timeouts.
162-
let base = cmp::max(1, timeout / 10) * 2 + 1;
163-
let zero = base / 2;
164-
match rdrand64() % base {
165-
jitter if jitter > zero => {
166-
timeout = timeout.checked_add(jitter - zero).unwrap_or(timeout)
167-
}
168-
jitter if jitter < zero => {
169-
timeout = timeout.checked_sub(zero - jitter).unwrap_or(timeout)
170-
}
171-
_ => {}
172-
};
163+
if let Ok(timeout_signed) = i64::try_from(timeout) {
164+
let tenth = 1 + timeout_signed / 10;
165+
let deviation = (rdrand64() as i64).checked_rem(tenth).unwrap_or(0);
166+
timeout = timeout_signed.saturating_add(deviation) as _;
167+
}
173168
timeout = cmp::min(u64::MAX - 1, cmp::max(1, timeout));
174169
}
175170
unsafe { raw::wait(event_mask, timeout).from_sgx_result() }

src/libstd/sys/sgx/mod.rs

+50-17
Original file line numberDiff line numberDiff line change
@@ -110,43 +110,76 @@ pub fn decode_error_kind(code: i32) -> ErrorKind {
110110
}
111111
}
112112

113-
// This function makes an effort to sleep at least as long as `duration`.
114-
// Note that in general there is no guarantee about accuracy of time and
115-
// timeouts in SGX model. The enclave runner serving usercalls may lie about
116-
// current time and/or ignore timeout values.
113+
// This function makes an effort to wait for a non-spurious event at least as
114+
// long as `duration`. Note that in general there is no guarantee about accuracy
115+
// of time and timeouts in SGX model. The enclave runner serving usercalls may
116+
// lie about current time and/or ignore timeout values.
117117
//
118-
// Once the event is observed, `stop` will be used to determine whether or not
119-
// we should continue to wait.
118+
// Once the event is observed, `woken_up` will be used to determine whether or
119+
// not the event was spurious.
120120
//
121121
// FIXME: note these caveats in documentation of all public types that use this
122122
// function in their execution path.
123-
pub fn wait_timeout_sgx<F>(event_mask: u64, duration: crate::time::Duration, stop: F)
123+
pub fn wait_timeout_sgx<F>(event_mask: u64, duration: crate::time::Duration, woken_up: F)
124124
where
125125
F: Fn() -> bool,
126126
{
127127
use self::abi::usercalls;
128128
use crate::cmp;
129129
use crate::io::ErrorKind;
130-
use crate::time::Instant;
131-
132-
let start = Instant::now();
133-
let mut remaining = duration;
134-
loop {
135-
let timeout = cmp::min((u64::MAX - 1) as u128, remaining.as_nanos()) as u64;
130+
use crate::time::{Duration, Instant};
131+
132+
// Calls the wait usercall and checks the result. Returns true if event was
133+
// returned, and false if WouldBlock/TimedOut was returned.
134+
// If duration is None, it will use WAIT_NO.
135+
fn wait_checked(event_mask: u64, duration: Option<Duration>) -> bool {
136+
let timeout = duration.map_or(usercalls::raw::WAIT_NO, |duration| {
137+
cmp::min((u64::MAX - 1) as u128, duration.as_nanos()) as u64
138+
});
136139
match usercalls::wait(event_mask, timeout) {
137140
Ok(eventset) => {
138141
if event_mask == 0 {
139142
rtabort!("expected usercalls::wait() to return Err, found Ok.");
140143
}
141144
rtassert!(eventset & event_mask == event_mask);
142-
if stop() {
143-
return;
144-
}
145+
true
145146
}
146147
Err(e) => {
147-
rtassert!(e.kind() == ErrorKind::TimedOut || e.kind() == ErrorKind::WouldBlock)
148+
rtassert!(e.kind() == ErrorKind::TimedOut || e.kind() == ErrorKind::WouldBlock);
149+
false
148150
}
149151
}
152+
}
153+
154+
match wait_checked(event_mask, Some(duration)) {
155+
false => return, // timed out
156+
true if woken_up() => return, // woken up
157+
true => {} // spurious event
158+
}
159+
160+
// Drain all cached events.
161+
// Note that `event_mask != 0` is implied if we get here.
162+
loop {
163+
match wait_checked(event_mask, None) {
164+
false => break, // no more cached events
165+
true if woken_up() => return, // woken up
166+
true => {} // spurious event
167+
}
168+
}
169+
170+
// Continue waiting, but take note of time spent waiting so we don't wait
171+
// forever. We intentionally don't call `Instant::now()` before this point
172+
// to avoid the cost of the `insecure_time` usercall in case there are no
173+
// spurious wakeups.
174+
175+
let start = Instant::now();
176+
let mut remaining = duration;
177+
loop {
178+
match wait_checked(event_mask, Some(remaining)) {
179+
false => return, // timed out
180+
true if woken_up() => return, // woken up
181+
true => {} // spurious event
182+
}
150183
remaining = match duration.checked_sub(start.elapsed()) {
151184
Some(remaining) => remaining,
152185
None => break,

0 commit comments

Comments
 (0)