Skip to main content

std/sys/pal/sgx/abi/usercalls/
mod.rs

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