Skip to content

Commit

Permalink
Rollup merge of rust-lang#73269 - mzohreva:mz/sgx-wait-timeout, r=jet…
Browse files Browse the repository at this point in the history
…hrogb

Enable some timeouts in SGX platform

This would partially resolve fortanix/rust-sgx#31

cc @jethrogb and @Goirad
  • Loading branch information
Manishearth committed Jul 16, 2020
2 parents dcd0180 + 85c25ae commit 599a77d
Show file tree
Hide file tree
Showing 13 changed files with 215 additions and 48 deletions.
4 changes: 0 additions & 4 deletions src/libstd/sync/condvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,6 @@ mod tests {

#[test]
#[cfg_attr(target_os = "emscripten", ignore)]
#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
fn wait_timeout_wait() {
let m = Arc::new(Mutex::new(()));
let c = Arc::new(Condvar::new());
Expand All @@ -714,7 +713,6 @@ mod tests {

#[test]
#[cfg_attr(target_os = "emscripten", ignore)]
#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
fn wait_timeout_while_wait() {
let m = Arc::new(Mutex::new(()));
let c = Arc::new(Condvar::new());
Expand All @@ -739,7 +737,6 @@ mod tests {

#[test]
#[cfg_attr(target_os = "emscripten", ignore)]
#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
fn wait_timeout_while_wake() {
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pair_copy = pair.clone();
Expand All @@ -763,7 +760,6 @@ mod tests {

#[test]
#[cfg_attr(target_os = "emscripten", ignore)]
#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
fn wait_timeout_wake() {
let m = Arc::new(Mutex::new(()));
let c = Arc::new(Condvar::new());
Expand Down
9 changes: 0 additions & 9 deletions src/libstd/sync/mpsc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2088,7 +2088,6 @@ mod tests {
}

#[test]
#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
fn oneshot_single_thread_recv_timeout() {
let (tx, rx) = channel();
tx.send(()).unwrap();
Expand All @@ -2099,7 +2098,6 @@ mod tests {
}

#[test]
#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
fn stress_recv_timeout_two_threads() {
let (tx, rx) = channel();
let stress = stress_factor() + 100;
Expand Down Expand Up @@ -2130,7 +2128,6 @@ mod tests {
}

#[test]
#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
fn recv_timeout_upgrade() {
let (tx, rx) = channel::<()>();
let timeout = Duration::from_millis(1);
Expand All @@ -2142,7 +2139,6 @@ mod tests {
}

#[test]
#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
fn stress_recv_timeout_shared() {
let (tx, rx) = channel();
let stress = stress_factor() + 100;
Expand Down Expand Up @@ -2173,7 +2169,6 @@ mod tests {
}

#[test]
#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
fn very_long_recv_timeout_wont_panic() {
let (tx, rx) = channel::<()>();
let join_handle = thread::spawn(move || rx.recv_timeout(Duration::from_secs(u64::MAX)));
Expand All @@ -2195,7 +2190,6 @@ mod tests {
}

#[test]
#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
fn shared_recv_timeout() {
let (tx, rx) = channel();
let total = 5;
Expand Down Expand Up @@ -2425,7 +2419,6 @@ mod sync_tests {
}

#[test]
#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
fn recv_timeout() {
let (tx, rx) = sync_channel::<i32>(1);
assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout));
Expand Down Expand Up @@ -2517,7 +2510,6 @@ mod sync_tests {
}

#[test]
#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
fn stress_recv_timeout_two_threads() {
let (tx, rx) = sync_channel::<i32>(0);

Expand All @@ -2543,7 +2535,6 @@ mod sync_tests {
}

#[test]
#[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31
fn stress_recv_timeout_shared() {
const AMT: u32 = 1000;
const NTHREADS: u32 = 8;
Expand Down
92 changes: 89 additions & 3 deletions src/libstd/sys/sgx/abi/usercalls/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::cmp;
use crate::io::{Error as IoError, IoSlice, IoSliceMut, Result as IoResult};
use crate::time::Duration;
use crate::convert::TryFrom;
use crate::io::{Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult};
use crate::sys::rand::rdrand64;
use crate::time::{Duration, Instant};

pub(crate) mod alloc;
#[macro_use]
Expand Down Expand Up @@ -149,10 +151,94 @@ pub fn exit(panic: bool) -> ! {

/// Usercall `wait`. See the ABI documentation for more information.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub fn wait(event_mask: u64, timeout: u64) -> IoResult<u64> {
pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult<u64> {
if timeout != WAIT_NO && timeout != WAIT_INDEFINITE {
// We don't want people to rely on accuracy of timeouts to make
// security decisions in an SGX enclave. That's why we add a random
// amount not exceeding +/- 10% to the timeout value to discourage
// people from relying on accuracy of timeouts while providing a way
// to make things work in other cases. Note that in the SGX threat
// model the enclave runner which is serving the wait usercall is not
// trusted to ensure accurate timeouts.
if let Ok(timeout_signed) = i64::try_from(timeout) {
let tenth = timeout_signed / 10;
let deviation = (rdrand64() as i64).checked_rem(tenth).unwrap_or(0);
timeout = timeout_signed.saturating_add(deviation) as _;
}
}
unsafe { raw::wait(event_mask, timeout).from_sgx_result() }
}

/// This function makes an effort to wait for a non-spurious event at least as
/// long as `duration`. Note that in general there is no guarantee about accuracy
/// of time and timeouts in SGX model. The enclave runner serving usercalls may
/// lie about current time and/or ignore timeout values.
///
/// Once the event is observed, `should_wake_up` will be used to determine
/// whether or not the event was spurious.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub fn wait_timeout<F>(event_mask: u64, duration: Duration, should_wake_up: F)
where
F: Fn() -> bool,
{
// Calls the wait usercall and checks the result. Returns true if event was
// returned, and false if WouldBlock/TimedOut was returned.
// If duration is None, it will use WAIT_NO.
fn wait_checked(event_mask: u64, duration: Option<Duration>) -> bool {
let timeout = duration.map_or(raw::WAIT_NO, |duration| {
cmp::min((u64::MAX - 1) as u128, duration.as_nanos()) as u64
});
match wait(event_mask, timeout) {
Ok(eventset) => {
if event_mask == 0 {
rtabort!("expected wait() to return Err, found Ok.");
}
rtassert!(eventset != 0 && eventset & !event_mask == 0);
true
}
Err(e) => {
rtassert!(e.kind() == ErrorKind::TimedOut || e.kind() == ErrorKind::WouldBlock);
false
}
}
}

match wait_checked(event_mask, Some(duration)) {
false => return, // timed out
true if should_wake_up() => return, // woken up
true => {} // spurious event
}

// Drain all cached events.
// Note that `event_mask != 0` is implied if we get here.
loop {
match wait_checked(event_mask, None) {
false => break, // no more cached events
true if should_wake_up() => return, // woken up
true => {} // spurious event
}
}

// Continue waiting, but take note of time spent waiting so we don't wait
// forever. We intentionally don't call `Instant::now()` before this point
// to avoid the cost of the `insecure_time` usercall in case there are no
// spurious wakeups.

let start = Instant::now();
let mut remaining = duration;
loop {
match wait_checked(event_mask, Some(remaining)) {
false => return, // timed out
true if should_wake_up() => return, // woken up
true => {} // spurious event
}
remaining = match duration.checked_sub(start.elapsed()) {
Some(remaining) => remaining,
None => break,
}
}
}

/// Usercall `send`. See the ABI documentation for more information.
#[unstable(feature = "sgx_platform", issue = "56975")]
pub fn send(event_set: u64, tcs: Option<Tcs>) -> IoResult<()> {
Expand Down
6 changes: 4 additions & 2 deletions src/libstd/sys/sgx/condvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ impl Condvar {
mutex.lock()
}

pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool {
rtabort!("timeout not supported in SGX");
pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
let success = WaitQueue::wait_timeout(&self.inner, dur, || mutex.unlock());
mutex.lock();
success
}

#[inline]
Expand Down
9 changes: 6 additions & 3 deletions src/libstd/sys/sgx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ pub extern "C" fn __rust_abort() {
abort_internal();
}

pub fn hashmap_random_keys() -> (u64, u64) {
fn rdrand64() -> u64 {
pub mod rand {
pub fn rdrand64() -> u64 {
unsafe {
let mut ret: u64 = 0;
for _ in 0..10 {
Expand All @@ -149,7 +149,10 @@ pub fn hashmap_random_keys() -> (u64, u64) {
rtabort!("Failed to obtain random data");
}
}
(rdrand64(), rdrand64())
}

pub fn hashmap_random_keys() -> (u64, u64) {
(self::rand::rdrand64(), self::rand::rdrand64())
}

pub use crate::sys_common::{AsInner, FromInner, IntoInner};
Expand Down
4 changes: 2 additions & 2 deletions src/libstd/sys/sgx/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ impl Thread {
// FIXME: could store this pointer in TLS somewhere
}

pub fn sleep(_dur: Duration) {
rtabort!("can't sleep"); // FIXME
pub fn sleep(dur: Duration) {
usercalls::wait_timeout(0, dur, || true);
}

pub fn join(self) {
Expand Down
Loading

0 comments on commit 599a77d

Please sign in to comment.