]>
Commit | Line | Data |
---|---|---|
48663c56 | 1 | use crate::cmp; |
3dfed10e XL |
2 | use crate::io::{Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult}; |
3 | use crate::sys::rand::rdrand64; | |
4 | use crate::time::{Duration, Instant}; | |
0731742a XL |
5 | |
6 | pub(crate) mod alloc; | |
7 | #[macro_use] | |
8 | pub(crate) mod raw; | |
04454e1e FG |
9 | #[cfg(test)] |
10 | mod tests; | |
0731742a XL |
11 | |
12 | use self::raw::*; | |
13 | ||
14 | /// Usercall `read`. See the ABI documentation for more information. | |
48663c56 XL |
15 | /// |
16 | /// This will do a single `read` usercall and scatter the read data among | |
17 | /// `bufs`. To read to a single buffer, just pass a slice of length one. | |
0731742a | 18 | #[unstable(feature = "sgx_platform", issue = "56975")] |
48663c56 | 19 | pub fn read(fd: Fd, bufs: &mut [IoSliceMut<'_>]) -> IoResult<usize> { |
0731742a | 20 | unsafe { |
48663c56 XL |
21 | let total_len = bufs.iter().fold(0usize, |sum, buf| sum.saturating_add(buf.len())); |
22 | let mut userbuf = alloc::User::<[u8]>::uninitialized(total_len); | |
23 | let ret_len = raw::read(fd, userbuf.as_mut_ptr(), userbuf.len()).from_sgx_result()?; | |
24 | let userbuf = &userbuf[..ret_len]; | |
25 | let mut index = 0; | |
26 | for buf in bufs { | |
27 | let end = cmp::min(index + buf.len(), userbuf.len()); | |
28 | if let Some(buflen) = end.checked_sub(index) { | |
29 | userbuf[index..end].copy_to_enclave(&mut buf[..buflen]); | |
30 | index += buf.len(); | |
31 | } else { | |
60c5eb7d | 32 | break; |
48663c56 XL |
33 | } |
34 | } | |
35 | Ok(userbuf.len()) | |
0731742a XL |
36 | } |
37 | } | |
38 | ||
39 | /// Usercall `read_alloc`. See the ABI documentation for more information. | |
40 | #[unstable(feature = "sgx_platform", issue = "56975")] | |
41 | pub fn read_alloc(fd: Fd) -> IoResult<Vec<u8>> { | |
42 | unsafe { | |
532ac7d7 | 43 | let userbuf = ByteBuffer { data: crate::ptr::null_mut(), len: 0 }; |
9fa01778 | 44 | let mut userbuf = alloc::User::new_from_enclave(&userbuf); |
0731742a XL |
45 | raw::read_alloc(fd, userbuf.as_raw_mut_ptr()).from_sgx_result()?; |
46 | Ok(userbuf.copy_user_buffer()) | |
47 | } | |
48 | } | |
49 | ||
50 | /// Usercall `write`. See the ABI documentation for more information. | |
48663c56 XL |
51 | /// |
52 | /// This will do a single `write` usercall and gather the written data from | |
53 | /// `bufs`. To write from a single buffer, just pass a slice of length one. | |
0731742a | 54 | #[unstable(feature = "sgx_platform", issue = "56975")] |
48663c56 | 55 | pub fn write(fd: Fd, bufs: &[IoSlice<'_>]) -> IoResult<usize> { |
0731742a | 56 | unsafe { |
48663c56 XL |
57 | let total_len = bufs.iter().fold(0usize, |sum, buf| sum.saturating_add(buf.len())); |
58 | let mut userbuf = alloc::User::<[u8]>::uninitialized(total_len); | |
59 | let mut index = 0; | |
60 | for buf in bufs { | |
61 | let end = cmp::min(index + buf.len(), userbuf.len()); | |
62 | if let Some(buflen) = end.checked_sub(index) { | |
63 | userbuf[index..end].copy_from_enclave(&buf[..buflen]); | |
64 | index += buf.len(); | |
65 | } else { | |
60c5eb7d | 66 | break; |
48663c56 XL |
67 | } |
68 | } | |
0731742a XL |
69 | raw::write(fd, userbuf.as_ptr(), userbuf.len()).from_sgx_result() |
70 | } | |
71 | } | |
72 | ||
73 | /// Usercall `flush`. See the ABI documentation for more information. | |
74 | #[unstable(feature = "sgx_platform", issue = "56975")] | |
75 | pub fn flush(fd: Fd) -> IoResult<()> { | |
76 | unsafe { raw::flush(fd).from_sgx_result() } | |
77 | } | |
78 | ||
79 | /// Usercall `close`. See the ABI documentation for more information. | |
80 | #[unstable(feature = "sgx_platform", issue = "56975")] | |
81 | pub fn close(fd: Fd) { | |
82 | unsafe { raw::close(fd) } | |
83 | } | |
84 | ||
85 | fn string_from_bytebuffer(buf: &alloc::UserRef<ByteBuffer>, usercall: &str, arg: &str) -> String { | |
86 | String::from_utf8(buf.copy_user_buffer()) | |
5e7ed085 | 87 | .unwrap_or_else(|_| rtabort!("Usercall {usercall}: expected {arg} to be valid UTF-8")) |
0731742a XL |
88 | } |
89 | ||
90 | /// Usercall `bind_stream`. See the ABI documentation for more information. | |
91 | #[unstable(feature = "sgx_platform", issue = "56975")] | |
92 | pub fn bind_stream(addr: &str) -> IoResult<(Fd, String)> { | |
93 | unsafe { | |
94 | let addr_user = alloc::User::new_from_enclave(addr.as_bytes()); | |
95 | let mut local = alloc::User::<ByteBuffer>::uninitialized(); | |
60c5eb7d XL |
96 | let fd = raw::bind_stream(addr_user.as_ptr(), addr_user.len(), local.as_raw_mut_ptr()) |
97 | .from_sgx_result()?; | |
0731742a XL |
98 | let local = string_from_bytebuffer(&local, "bind_stream", "local_addr"); |
99 | Ok((fd, local)) | |
100 | } | |
101 | } | |
102 | ||
103 | /// Usercall `accept_stream`. See the ABI documentation for more information. | |
104 | #[unstable(feature = "sgx_platform", issue = "56975")] | |
105 | pub fn accept_stream(fd: Fd) -> IoResult<(Fd, String, String)> { | |
106 | unsafe { | |
107 | let mut bufs = alloc::User::<[ByteBuffer; 2]>::uninitialized(); | |
108 | let mut buf_it = alloc::UserRef::iter_mut(&mut *bufs); // FIXME: can this be done | |
60c5eb7d | 109 | // without forcing coercion? |
0731742a | 110 | let (local, peer) = (buf_it.next().unwrap(), buf_it.next().unwrap()); |
60c5eb7d XL |
111 | let fd = raw::accept_stream(fd, local.as_raw_mut_ptr(), peer.as_raw_mut_ptr()) |
112 | .from_sgx_result()?; | |
0731742a XL |
113 | let local = string_from_bytebuffer(&local, "accept_stream", "local_addr"); |
114 | let peer = string_from_bytebuffer(&peer, "accept_stream", "peer_addr"); | |
115 | Ok((fd, local, peer)) | |
116 | } | |
117 | } | |
118 | ||
119 | /// Usercall `connect_stream`. See the ABI documentation for more information. | |
120 | #[unstable(feature = "sgx_platform", issue = "56975")] | |
121 | pub fn connect_stream(addr: &str) -> IoResult<(Fd, String, String)> { | |
122 | unsafe { | |
123 | let addr_user = alloc::User::new_from_enclave(addr.as_bytes()); | |
124 | let mut bufs = alloc::User::<[ByteBuffer; 2]>::uninitialized(); | |
125 | let mut buf_it = alloc::UserRef::iter_mut(&mut *bufs); // FIXME: can this be done | |
60c5eb7d | 126 | // without forcing coercion? |
0731742a XL |
127 | let (local, peer) = (buf_it.next().unwrap(), buf_it.next().unwrap()); |
128 | let fd = raw::connect_stream( | |
129 | addr_user.as_ptr(), | |
130 | addr_user.len(), | |
131 | local.as_raw_mut_ptr(), | |
60c5eb7d XL |
132 | peer.as_raw_mut_ptr(), |
133 | ) | |
134 | .from_sgx_result()?; | |
0731742a XL |
135 | let local = string_from_bytebuffer(&local, "connect_stream", "local_addr"); |
136 | let peer = string_from_bytebuffer(&peer, "connect_stream", "peer_addr"); | |
137 | Ok((fd, local, peer)) | |
138 | } | |
139 | } | |
140 | ||
141 | /// Usercall `launch_thread`. See the ABI documentation for more information. | |
142 | #[unstable(feature = "sgx_platform", issue = "56975")] | |
143 | pub unsafe fn launch_thread() -> IoResult<()> { | |
29967ef6 XL |
144 | // SAFETY: The caller must uphold the safety contract for `launch_thread`. |
145 | unsafe { raw::launch_thread().from_sgx_result() } | |
0731742a XL |
146 | } |
147 | ||
148 | /// Usercall `exit`. See the ABI documentation for more information. | |
149 | #[unstable(feature = "sgx_platform", issue = "56975")] | |
150 | pub fn exit(panic: bool) -> ! { | |
151 | unsafe { raw::exit(panic) } | |
152 | } | |
153 | ||
154 | /// Usercall `wait`. See the ABI documentation for more information. | |
155 | #[unstable(feature = "sgx_platform", issue = "56975")] | |
3dfed10e XL |
156 | pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult<u64> { |
157 | if timeout != WAIT_NO && timeout != WAIT_INDEFINITE { | |
158 | // We don't want people to rely on accuracy of timeouts to make | |
159 | // security decisions in an SGX enclave. That's why we add a random | |
160 | // amount not exceeding +/- 10% to the timeout value to discourage | |
161 | // people from relying on accuracy of timeouts while providing a way | |
162 | // to make things work in other cases. Note that in the SGX threat | |
163 | // model the enclave runner which is serving the wait usercall is not | |
164 | // trusted to ensure accurate timeouts. | |
165 | if let Ok(timeout_signed) = i64::try_from(timeout) { | |
166 | let tenth = timeout_signed / 10; | |
167 | let deviation = (rdrand64() as i64).checked_rem(tenth).unwrap_or(0); | |
168 | timeout = timeout_signed.saturating_add(deviation) as _; | |
169 | } | |
170 | } | |
0731742a XL |
171 | unsafe { raw::wait(event_mask, timeout).from_sgx_result() } |
172 | } | |
173 | ||
3dfed10e XL |
174 | /// This function makes an effort to wait for a non-spurious event at least as |
175 | /// long as `duration`. Note that in general there is no guarantee about accuracy | |
176 | /// of time and timeouts in SGX model. The enclave runner serving usercalls may | |
177 | /// lie about current time and/or ignore timeout values. | |
178 | /// | |
179 | /// Once the event is observed, `should_wake_up` will be used to determine | |
180 | /// whether or not the event was spurious. | |
181 | #[unstable(feature = "sgx_platform", issue = "56975")] | |
182 | pub fn wait_timeout<F>(event_mask: u64, duration: Duration, should_wake_up: F) | |
183 | where | |
184 | F: Fn() -> bool, | |
185 | { | |
186 | // Calls the wait usercall and checks the result. Returns true if event was | |
187 | // returned, and false if WouldBlock/TimedOut was returned. | |
188 | // If duration is None, it will use WAIT_NO. | |
189 | fn wait_checked(event_mask: u64, duration: Option<Duration>) -> bool { | |
190 | let timeout = duration.map_or(raw::WAIT_NO, |duration| { | |
191 | cmp::min((u64::MAX - 1) as u128, duration.as_nanos()) as u64 | |
192 | }); | |
193 | match wait(event_mask, timeout) { | |
194 | Ok(eventset) => { | |
195 | if event_mask == 0 { | |
196 | rtabort!("expected wait() to return Err, found Ok."); | |
197 | } | |
198 | rtassert!(eventset != 0 && eventset & !event_mask == 0); | |
199 | true | |
200 | } | |
201 | Err(e) => { | |
202 | rtassert!(e.kind() == ErrorKind::TimedOut || e.kind() == ErrorKind::WouldBlock); | |
203 | false | |
204 | } | |
205 | } | |
206 | } | |
207 | ||
208 | match wait_checked(event_mask, Some(duration)) { | |
209 | false => return, // timed out | |
210 | true if should_wake_up() => return, // woken up | |
211 | true => {} // spurious event | |
212 | } | |
213 | ||
214 | // Drain all cached events. | |
215 | // Note that `event_mask != 0` is implied if we get here. | |
216 | loop { | |
217 | match wait_checked(event_mask, None) { | |
218 | false => break, // no more cached events | |
219 | true if should_wake_up() => return, // woken up | |
220 | true => {} // spurious event | |
221 | } | |
222 | } | |
223 | ||
224 | // Continue waiting, but take note of time spent waiting so we don't wait | |
225 | // forever. We intentionally don't call `Instant::now()` before this point | |
226 | // to avoid the cost of the `insecure_time` usercall in case there are no | |
227 | // spurious wakeups. | |
228 | ||
229 | let start = Instant::now(); | |
230 | let mut remaining = duration; | |
231 | loop { | |
232 | match wait_checked(event_mask, Some(remaining)) { | |
233 | false => return, // timed out | |
234 | true if should_wake_up() => return, // woken up | |
235 | true => {} // spurious event | |
236 | } | |
237 | remaining = match duration.checked_sub(start.elapsed()) { | |
238 | Some(remaining) => remaining, | |
239 | None => break, | |
240 | } | |
241 | } | |
242 | } | |
243 | ||
0731742a XL |
244 | /// Usercall `send`. See the ABI documentation for more information. |
245 | #[unstable(feature = "sgx_platform", issue = "56975")] | |
246 | pub fn send(event_set: u64, tcs: Option<Tcs>) -> IoResult<()> { | |
247 | unsafe { raw::send(event_set, tcs).from_sgx_result() } | |
248 | } | |
249 | ||
250 | /// Usercall `insecure_time`. See the ABI documentation for more information. | |
251 | #[unstable(feature = "sgx_platform", issue = "56975")] | |
252 | pub fn insecure_time() -> Duration { | |
253 | let t = unsafe { raw::insecure_time() }; | |
254 | Duration::new(t / 1_000_000_000, (t % 1_000_000_000) as _) | |
255 | } | |
256 | ||
257 | /// Usercall `alloc`. See the ABI documentation for more information. | |
258 | #[unstable(feature = "sgx_platform", issue = "56975")] | |
259 | pub fn alloc(size: usize, alignment: usize) -> IoResult<*mut u8> { | |
260 | unsafe { raw::alloc(size, alignment).from_sgx_result() } | |
261 | } | |
262 | ||
263 | #[unstable(feature = "sgx_platform", issue = "56975")] | |
264 | #[doc(inline)] | |
265 | pub use self::raw::free; | |
266 | ||
267 | fn check_os_error(err: Result) -> i32 { | |
268 | // FIXME: not sure how to make sure all variants of Error are covered | |
60c5eb7d XL |
269 | if err == Error::NotFound as _ |
270 | || err == Error::PermissionDenied as _ | |
271 | || err == Error::ConnectionRefused as _ | |
272 | || err == Error::ConnectionReset as _ | |
273 | || err == Error::ConnectionAborted as _ | |
274 | || err == Error::NotConnected as _ | |
275 | || err == Error::AddrInUse as _ | |
276 | || err == Error::AddrNotAvailable as _ | |
277 | || err == Error::BrokenPipe as _ | |
278 | || err == Error::AlreadyExists as _ | |
279 | || err == Error::WouldBlock as _ | |
280 | || err == Error::InvalidInput as _ | |
281 | || err == Error::InvalidData as _ | |
282 | || err == Error::TimedOut as _ | |
283 | || err == Error::WriteZero as _ | |
284 | || err == Error::Interrupted as _ | |
285 | || err == Error::Other as _ | |
286 | || err == Error::UnexpectedEof as _ | |
287 | || ((Error::UserRangeStart as _)..=(Error::UserRangeEnd as _)).contains(&err) | |
0731742a XL |
288 | { |
289 | err | |
290 | } else { | |
5e7ed085 | 291 | rtabort!("Usercall: returned invalid error value {err}") |
0731742a XL |
292 | } |
293 | } | |
294 | ||
295 | trait FromSgxResult { | |
296 | type Return; | |
297 | ||
298 | fn from_sgx_result(self) -> IoResult<Self::Return>; | |
299 | } | |
300 | ||
301 | impl<T> FromSgxResult for (Result, T) { | |
302 | type Return = T; | |
303 | ||
304 | fn from_sgx_result(self) -> IoResult<Self::Return> { | |
305 | if self.0 == RESULT_SUCCESS { | |
306 | Ok(self.1) | |
307 | } else { | |
308 | Err(IoError::from_raw_os_error(check_os_error(self.0))) | |
309 | } | |
310 | } | |
311 | } | |
312 | ||
313 | impl FromSgxResult for Result { | |
314 | type Return = (); | |
315 | ||
316 | fn from_sgx_result(self) -> IoResult<Self::Return> { | |
317 | if self == RESULT_SUCCESS { | |
318 | Ok(()) | |
319 | } else { | |
320 | Err(IoError::from_raw_os_error(check_os_error(self))) | |
321 | } | |
322 | } | |
323 | } |