]> git.proxmox.com Git - rustc.git/blame - library/std/src/sys/sgx/abi/usercalls/mod.rs
New upstream version 1.62.1+dfsg1
[rustc.git] / library / std / src / sys / sgx / abi / usercalls / mod.rs
CommitLineData
48663c56 1use crate::cmp;
3dfed10e
XL
2use crate::io::{Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult};
3use crate::sys::rand::rdrand64;
4use crate::time::{Duration, Instant};
0731742a
XL
5
6pub(crate) mod alloc;
7#[macro_use]
8pub(crate) mod raw;
04454e1e
FG
9#[cfg(test)]
10mod tests;
0731742a
XL
11
12use 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 19pub 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")]
41pub 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 55pub 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")]
75pub 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")]
81pub fn close(fd: Fd) {
82 unsafe { raw::close(fd) }
83}
84
85fn 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")]
92pub 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")]
105pub 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")]
121pub 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")]
143pub 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")]
150pub 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
156pub 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")]
182pub fn wait_timeout<F>(event_mask: u64, duration: Duration, should_wake_up: F)
183where
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")]
246pub 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")]
252pub 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")]
259pub 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)]
265pub use self::raw::free;
266
267fn 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
295trait FromSgxResult {
296 type Return;
297
298 fn from_sgx_result(self) -> IoResult<Self::Return>;
299}
300
301impl<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
313impl 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}