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