|
1 | 1 | use crate::cmp;
|
2 |
| -use crate::io::{Error as IoError, IoSlice, IoSliceMut, Result as IoResult}; |
3 |
| -use crate::time::Duration; |
| 2 | +use crate::convert::TryFrom; |
| 3 | +use crate::io::{Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult}; |
| 4 | +use crate::sys::rand::rdrand64; |
| 5 | +use crate::time::{Duration, Instant}; |
4 | 6 |
|
5 | 7 | pub(crate) mod alloc;
|
6 | 8 | #[macro_use]
|
@@ -149,10 +151,94 @@ pub fn exit(panic: bool) -> ! {
|
149 | 151 |
|
150 | 152 | /// Usercall `wait`. See the ABI documentation for more information.
|
151 | 153 | #[unstable(feature = "sgx_platform", issue = "56975")]
|
152 |
| -pub fn wait(event_mask: u64, timeout: u64) -> IoResult<u64> { |
| 154 | +pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult<u64> { |
| 155 | + if timeout != WAIT_NO && timeout != WAIT_INDEFINITE { |
| 156 | + // We don't want people to rely on accuracy of timeouts to make |
| 157 | + // security decisions in an SGX enclave. That's why we add a random |
| 158 | + // amount not exceeding +/- 10% to the timeout value to discourage |
| 159 | + // people from relying on accuracy of timeouts while providing a way |
| 160 | + // to make things work in other cases. Note that in the SGX threat |
| 161 | + // model the enclave runner which is serving the wait usercall is not |
| 162 | + // trusted to ensure accurate timeouts. |
| 163 | + if let Ok(timeout_signed) = i64::try_from(timeout) { |
| 164 | + let tenth = timeout_signed / 10; |
| 165 | + let deviation = (rdrand64() as i64).checked_rem(tenth).unwrap_or(0); |
| 166 | + timeout = timeout_signed.saturating_add(deviation) as _; |
| 167 | + } |
| 168 | + } |
153 | 169 | unsafe { raw::wait(event_mask, timeout).from_sgx_result() }
|
154 | 170 | }
|
155 | 171 |
|
| 172 | +/// This function makes an effort to wait for a non-spurious event at least as |
| 173 | +/// long as `duration`. Note that in general there is no guarantee about accuracy |
| 174 | +/// of time and timeouts in SGX model. The enclave runner serving usercalls may |
| 175 | +/// lie about current time and/or ignore timeout values. |
| 176 | +/// |
| 177 | +/// Once the event is observed, `should_wake_up` will be used to determine |
| 178 | +/// whether or not the event was spurious. |
| 179 | +#[unstable(feature = "sgx_platform", issue = "56975")] |
| 180 | +pub fn wait_timeout<F>(event_mask: u64, duration: Duration, should_wake_up: F) |
| 181 | +where |
| 182 | + F: Fn() -> bool, |
| 183 | +{ |
| 184 | + // Calls the wait usercall and checks the result. Returns true if event was |
| 185 | + // returned, and false if WouldBlock/TimedOut was returned. |
| 186 | + // If duration is None, it will use WAIT_NO. |
| 187 | + fn wait_checked(event_mask: u64, duration: Option<Duration>) -> bool { |
| 188 | + let timeout = duration.map_or(raw::WAIT_NO, |duration| { |
| 189 | + cmp::min((u64::MAX - 1) as u128, duration.as_nanos()) as u64 |
| 190 | + }); |
| 191 | + match wait(event_mask, timeout) { |
| 192 | + Ok(eventset) => { |
| 193 | + if event_mask == 0 { |
| 194 | + rtabort!("expected wait() to return Err, found Ok."); |
| 195 | + } |
| 196 | + rtassert!(eventset != 0 && eventset & !event_mask == 0); |
| 197 | + true |
| 198 | + } |
| 199 | + Err(e) => { |
| 200 | + rtassert!(e.kind() == ErrorKind::TimedOut || e.kind() == ErrorKind::WouldBlock); |
| 201 | + false |
| 202 | + } |
| 203 | + } |
| 204 | + } |
| 205 | + |
| 206 | + match wait_checked(event_mask, Some(duration)) { |
| 207 | + false => return, // timed out |
| 208 | + true if should_wake_up() => return, // woken up |
| 209 | + true => {} // spurious event |
| 210 | + } |
| 211 | + |
| 212 | + // Drain all cached events. |
| 213 | + // Note that `event_mask != 0` is implied if we get here. |
| 214 | + loop { |
| 215 | + match wait_checked(event_mask, None) { |
| 216 | + false => break, // no more cached events |
| 217 | + true if should_wake_up() => return, // woken up |
| 218 | + true => {} // spurious event |
| 219 | + } |
| 220 | + } |
| 221 | + |
| 222 | + // Continue waiting, but take note of time spent waiting so we don't wait |
| 223 | + // forever. We intentionally don't call `Instant::now()` before this point |
| 224 | + // to avoid the cost of the `insecure_time` usercall in case there are no |
| 225 | + // spurious wakeups. |
| 226 | + |
| 227 | + let start = Instant::now(); |
| 228 | + let mut remaining = duration; |
| 229 | + loop { |
| 230 | + match wait_checked(event_mask, Some(remaining)) { |
| 231 | + false => return, // timed out |
| 232 | + true if should_wake_up() => return, // woken up |
| 233 | + true => {} // spurious event |
| 234 | + } |
| 235 | + remaining = match duration.checked_sub(start.elapsed()) { |
| 236 | + Some(remaining) => remaining, |
| 237 | + None => break, |
| 238 | + } |
| 239 | + } |
| 240 | +} |
| 241 | + |
156 | 242 | /// Usercall `send`. See the ABI documentation for more information.
|
157 | 243 | #[unstable(feature = "sgx_platform", issue = "56975")]
|
158 | 244 | pub fn send(event_set: u64, tcs: Option<Tcs>) -> IoResult<()> {
|
|
0 commit comments