Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1e44e2d

Browse files
author
Jethro Beekman
committedDec 7, 2018
SGX target: implement user memory management
1 parent 39f9751 commit 1e44e2d

File tree

5 files changed

+502
-6
lines changed

5 files changed

+502
-6
lines changed
 

‎src/libstd/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,8 @@
312312
#![feature(non_exhaustive)]
313313
#![feature(alloc_layout_extra)]
314314
#![feature(maybe_uninit)]
315-
#![cfg_attr(target_env = "sgx", feature(global_asm, range_contains))]
315+
#![cfg_attr(target_env = "sgx", feature(global_asm, range_contains, slice_index_methods,
316+
decl_macro, coerce_unsized))]
316317

317318
#![default_lib_allocator]
318319

‎src/libstd/sys/sgx/abi/mem.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,17 @@
1111
// Do not remove inline: will result in relocation failure
1212
#[inline(always)]
1313
pub unsafe fn rel_ptr<T>(offset: u64) -> *const T {
14-
(image_base()+offset) as *const T
14+
(image_base() + offset) as *const T
1515
}
1616

1717
// Do not remove inline: will result in relocation failure
1818
#[inline(always)]
1919
pub unsafe fn rel_ptr_mut<T>(offset: u64) -> *mut T {
20-
(image_base()+offset) as *mut T
20+
(image_base() + offset) as *mut T
21+
}
22+
23+
extern {
24+
static ENCLAVE_SIZE: usize;
2125
}
2226

2327
// Do not remove inline: will result in relocation failure
@@ -26,6 +30,20 @@ pub unsafe fn rel_ptr_mut<T>(offset: u64) -> *mut T {
2630
#[inline(always)]
2731
fn image_base() -> u64 {
2832
let base;
29-
unsafe{asm!("lea IMAGE_BASE(%rip),$0":"=r"(base))};
33+
unsafe { asm!("lea IMAGE_BASE(%rip),$0":"=r"(base)) };
3034
base
3135
}
36+
37+
pub fn is_enclave_range(p: *const u8, len: usize) -> bool {
38+
let start=p as u64;
39+
let end=start + (len as u64);
40+
start >= image_base() &&
41+
end <= image_base() + (unsafe { ENCLAVE_SIZE } as u64) // unsafe ok: link-time constant
42+
}
43+
44+
pub fn is_user_range(p: *const u8, len: usize) -> bool {
45+
let start=p as u64;
46+
let end=start + (len as u64);
47+
end <= image_base() ||
48+
start >= image_base() + (unsafe { ENCLAVE_SIZE } as u64) // unsafe ok: link-time constant
49+
}

‎src/libstd/sys/sgx/abi/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,5 +96,5 @@ pub(super) fn exit_with_code(code: isize) -> ! {
9696
let _ = write!(out, "Exited with status code {}", code);
9797
}
9898
}
99-
unsafe { usercalls::raw::exit(code != 0) };
99+
usercalls::exit(code != 0);
100100
}
Lines changed: 404 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,404 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![allow(unused)]
12+
13+
use ptr;
14+
use mem;
15+
use cell::UnsafeCell;
16+
use slice;
17+
use ops::{Deref, DerefMut, Index, IndexMut, CoerceUnsized};
18+
use slice::SliceIndex;
19+
20+
use fortanix_sgx_abi::*;
21+
use super::super::mem::is_user_range;
22+
23+
/// A type that can be safely read from or written to userspace.
24+
///
25+
/// Non-exhaustive list of specific requirements for reading and writing:
26+
/// * **Type is `Copy`** (and therefore also not `Drop`). Copies will be
27+
/// created when copying from/to userspace. Destructors will not be called.
28+
/// * **No references or Rust-style owned pointers** (`Vec`, `Arc`, etc.). When
29+
/// reading from userspace, references into enclave memory must not be
30+
/// created. Also, only enclave memory is considered managed by the Rust
31+
/// compiler's static analysis. When reading from userspace, there can be no
32+
/// guarantee that the value correctly adheres to the expectations of the
33+
/// type. When writing to userspace, memory addresses of data in enclave
34+
/// memory must not be leaked for confidentiality reasons. `User` and
35+
/// `UserRef` are also not allowed for the same reasons.
36+
/// * **No fat pointers.** When reading from userspace, the size or vtable
37+
/// pointer could be automatically interpreted and used by the code. When
38+
/// writing to userspace, memory addresses of data in enclave memory (such
39+
/// as vtable pointers) must not be leaked for confidentiality reasons.
40+
///
41+
/// Non-exhaustive list of specific requirements for reading from userspace:
42+
/// * Any bit pattern is valid for this type (no `enum`s). There can be no
43+
/// guarantee that the value correctly adheres to the expectations of the
44+
/// type, so any value must be valid for this type.
45+
///
46+
/// Non-exhaustive list of specific requirements for writing to userspace:
47+
/// * No pointers to enclave memory. Memory addresses of data in enclave memory
48+
/// must not be leaked for confidentiality reasons.
49+
/// * No internal padding. Padding might contain previously-initialized secret
50+
/// data stored at that memory location and must not be leaked for
51+
/// confidentiality reasons.
52+
pub unsafe trait UserSafeSized: Copy + Sized {}
53+
54+
unsafe impl UserSafeSized for u8 {}
55+
unsafe impl<T> UserSafeSized for FifoDescriptor<T> {}
56+
unsafe impl UserSafeSized for ByteBuffer {}
57+
unsafe impl UserSafeSized for Usercall {}
58+
unsafe impl UserSafeSized for Return {}
59+
unsafe impl<T: UserSafeSized> UserSafeSized for [T; 2] {}
60+
61+
/// A type that can be represented in memory as one or more `UserSafeSized`s.
62+
pub unsafe trait UserSafe {
63+
unsafe fn align_of() -> usize;
64+
65+
/// NB. This takes a size, not a length!
66+
unsafe fn from_raw_sized_unchecked(ptr: *const u8, size: usize) -> *const Self;
67+
68+
/// NB. This takes a size, not a length!
69+
unsafe fn from_raw_sized(ptr: *const u8, size: usize) -> *const Self {
70+
let ret = Self::from_raw_sized_unchecked(ptr, size);
71+
Self::check_ptr(ret);
72+
ret
73+
}
74+
75+
unsafe fn check_ptr(ptr: *const Self) {
76+
let is_aligned = |p| -> bool {
77+
0 == (p as usize) & (Self::align_of() - 1)
78+
};
79+
80+
assert!(is_aligned(ptr as *const u8));
81+
assert!(is_user_range(ptr as _, mem::size_of_val(&*ptr)));
82+
assert!(!ptr.is_null());
83+
}
84+
}
85+
86+
unsafe impl<T: UserSafeSized> UserSafe for T {
87+
unsafe fn align_of() -> usize {
88+
mem::align_of::<T>()
89+
}
90+
91+
unsafe fn from_raw_sized_unchecked(ptr: *const u8, size: usize) -> *const Self {
92+
assert_eq!(size, mem::size_of::<T>());
93+
ptr as _
94+
}
95+
}
96+
97+
unsafe impl<T: UserSafeSized> UserSafe for [T] {
98+
unsafe fn align_of() -> usize {
99+
mem::align_of::<T>()
100+
}
101+
102+
unsafe fn from_raw_sized_unchecked(ptr: *const u8, size: usize) -> *const Self {
103+
let elem_size = mem::size_of::<T>();
104+
assert_eq!(size % elem_size, 0);
105+
let len = size / elem_size;
106+
slice::from_raw_parts(ptr as _, len)
107+
}
108+
}
109+
110+
/// A reference to some type in userspace memory. `&UserRef<T>` is equivalent
111+
/// to `&T` in enclave memory. Access to the memory is only allowed by copying
112+
/// to avoid TOCTTOU issues. After copying, code should make sure to completely
113+
/// check the value before use.
114+
pub struct UserRef<T: ?Sized>(UnsafeCell<T>);
115+
/// An owned type in userspace memory. `User<T>` is equivalent to `Box<T>` in
116+
/// enclave memory. Access to the memory is only allowed by copying to avoid
117+
/// TOCTTOU issues. The user memory will be freed when the value is dropped.
118+
/// After copying, code should make sure to completely check the value before
119+
/// use.
120+
pub struct User<T: UserSafe + ?Sized>(*mut UserRef<T>);
121+
122+
impl<T: ?Sized> User<T> where T: UserSafe {
123+
// This function returns memory that is practically uninitialized, but is
124+
// not considered "unspecified" or "undefined" for purposes of an
125+
// optimizing compiler. This is achieved by returning a pointer from
126+
// from outside as obtained by `super::alloc`.
127+
fn new_uninit_bytes(size: usize) -> Self {
128+
unsafe {
129+
let ptr = super::alloc(size, T::align_of()).expect("User memory allocation failed");
130+
User(T::from_raw_sized(ptr as _, size) as _)
131+
}
132+
}
133+
134+
pub fn new_from_enclave(val: &T) -> Self {
135+
unsafe {
136+
let ret = Self::new_uninit_bytes(mem::size_of_val(val));
137+
ptr::copy(
138+
val as *const T as *const u8,
139+
ret.0 as *mut T as *mut u8,
140+
mem::size_of_val(val)
141+
);
142+
ret
143+
}
144+
}
145+
146+
/// Create an owned `User<T>` from a raw pointer. The pointer should be
147+
/// freeable with the `free` usercall and the alignment of `T`.
148+
///
149+
/// # Panics
150+
/// This function panics if:
151+
///
152+
/// * The pointer is not aligned
153+
/// * The pointer is null
154+
/// * The pointed-to range is not in user memory
155+
pub unsafe fn from_raw(ptr: *mut T) -> Self {
156+
T::check_ptr(ptr);
157+
User(ptr as _)
158+
}
159+
160+
/// Convert this value into a raw pointer. The value will no longer be
161+
/// automatically freed.
162+
pub fn into_raw(self) -> *mut T {
163+
let ret = self.0;
164+
mem::forget(self);
165+
ret as _
166+
}
167+
}
168+
169+
impl<T> User<T> where T: UserSafe {
170+
pub fn uninitialized() -> Self {
171+
Self::new_uninit_bytes(mem::size_of::<T>())
172+
}
173+
}
174+
175+
impl<T> User<[T]> where [T]: UserSafe {
176+
pub fn uninitialized(n: usize) -> Self {
177+
Self::new_uninit_bytes(n * mem::size_of::<T>())
178+
}
179+
180+
/// Create an owned `User<[T]>` from a raw thin pointer and a slice length.
181+
/// The pointer should be freeable with the `free` usercall and the
182+
/// alignment of `T`.
183+
///
184+
/// # Panics
185+
/// This function panics if:
186+
///
187+
/// * The pointer is not aligned
188+
/// * The pointer is null
189+
/// * The pointed-to range is not in user memory
190+
pub unsafe fn from_raw_parts(ptr: *mut T, len: usize) -> Self {
191+
User(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::<T>()) as _)
192+
}
193+
}
194+
195+
impl<T: ?Sized> UserRef<T> where T: UserSafe {
196+
/// Create a `&UserRef<[T]>` from a raw pointer.
197+
///
198+
/// # Panics
199+
/// This function panics if:
200+
///
201+
/// * The pointer is not aligned
202+
/// * The pointer is null
203+
/// * The pointed-to range is not in user memory
204+
pub unsafe fn from_ptr<'a>(ptr: *const T) -> &'a Self {
205+
T::check_ptr(ptr);
206+
&*(ptr as *const Self)
207+
}
208+
209+
/// Create a `&mut UserRef<[T]>` from a raw pointer.
210+
///
211+
/// # Panics
212+
/// This function panics if:
213+
///
214+
/// * The pointer is not aligned
215+
/// * The pointer is null
216+
/// * The pointed-to range is not in user memory
217+
pub unsafe fn from_mut_ptr<'a>(ptr: *mut T) -> &'a mut Self {
218+
T::check_ptr(ptr);
219+
&mut*(ptr as *mut Self)
220+
}
221+
222+
/// # Panics
223+
/// This function panics if the destination doesn't have the same size as
224+
/// the source. This can happen for dynamically-sized types such as slices.
225+
pub fn copy_from_enclave(&mut self, val: &T) {
226+
unsafe {
227+
assert_eq!(mem::size_of_val(val), mem::size_of_val( &*self.0.get() ));
228+
ptr::copy(
229+
val as *const T as *const u8,
230+
self.0.get() as *mut T as *mut u8,
231+
mem::size_of_val(val)
232+
);
233+
}
234+
}
235+
236+
/// # Panics
237+
/// This function panics if the destination doesn't have the same size as
238+
/// the source. This can happen for dynamically-sized types such as slices.
239+
pub fn copy_to_enclave(&self, dest: &mut T) {
240+
unsafe {
241+
assert_eq!(mem::size_of_val(dest), mem::size_of_val( &*self.0.get() ));
242+
ptr::copy(
243+
self.0.get() as *const T as *const u8,
244+
dest as *mut T as *mut u8,
245+
mem::size_of_val(dest)
246+
);
247+
}
248+
}
249+
250+
pub fn as_raw_ptr(&self) -> *const T {
251+
self as *const _ as _
252+
}
253+
254+
pub fn as_raw_mut_ptr(&mut self) -> *mut T {
255+
self as *mut _ as _
256+
}
257+
}
258+
259+
impl<T> UserRef<T> where T: UserSafe {
260+
pub fn to_enclave(&self) -> T {
261+
unsafe { ptr::read(self.0.get()) }
262+
}
263+
}
264+
265+
impl<T> UserRef<[T]> where [T]: UserSafe {
266+
/// Create a `&UserRef<[T]>` from a raw thin pointer and a slice length.
267+
///
268+
/// # Panics
269+
/// This function panics if:
270+
///
271+
/// * The pointer is not aligned
272+
/// * The pointer is null
273+
/// * The pointed-to range is not in user memory
274+
pub unsafe fn from_raw_parts<'a>(ptr: *const T, len: usize) -> &'a Self {
275+
&*(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::<T>()) as *const Self)
276+
}
277+
278+
/// Create a `&mut UserRef<[T]>` from a raw thin pointer and a slice length.
279+
///
280+
/// # Panics
281+
/// This function panics if:
282+
///
283+
/// * The pointer is not aligned
284+
/// * The pointer is null
285+
/// * The pointed-to range is not in user memory
286+
pub unsafe fn from_raw_parts_mut<'a>(ptr: *mut T, len: usize) -> &'a mut Self {
287+
&mut*(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::<T>()) as *mut Self)
288+
}
289+
290+
pub fn as_ptr(&self) -> *const T {
291+
self.0.get() as _
292+
}
293+
294+
pub fn as_mut_ptr(&mut self) -> *mut T {
295+
self.0.get() as _
296+
}
297+
298+
pub fn len(&self) -> usize {
299+
unsafe { (*self.0.get()).len() }
300+
}
301+
302+
pub fn copy_to_enclave_vec(&self, dest: &mut Vec<T>) {
303+
unsafe {
304+
if let Some(missing) = self.len().checked_sub(dest.capacity()) {
305+
dest.reserve(missing)
306+
}
307+
dest.set_len(self.len());
308+
self.copy_to_enclave(&mut dest[..]);
309+
}
310+
}
311+
312+
pub fn to_enclave(&self) -> Vec<T> {
313+
let mut ret = Vec::with_capacity(self.len());
314+
self.copy_to_enclave_vec(&mut ret);
315+
ret
316+
}
317+
318+
pub fn iter(&self) -> Iter<T>
319+
where T: UserSafe // FIXME: should be implied by [T]: UserSafe?
320+
{
321+
unsafe {
322+
Iter((&*self.as_raw_ptr()).iter())
323+
}
324+
}
325+
326+
pub fn iter_mut(&mut self) -> IterMut<T>
327+
where T: UserSafe // FIXME: should be implied by [T]: UserSafe?
328+
{
329+
unsafe {
330+
IterMut((&mut*self.as_raw_mut_ptr()).iter_mut())
331+
}
332+
}
333+
}
334+
335+
pub struct Iter<'a, T: 'a + UserSafe>(slice::Iter<'a, T>);
336+
337+
impl<'a, T: UserSafe> Iterator for Iter<'a, T> {
338+
type Item = &'a UserRef<T>;
339+
340+
#[inline]
341+
fn next(&mut self) -> Option<Self::Item> {
342+
unsafe {
343+
self.0.next().map(|e| UserRef::from_ptr(e))
344+
}
345+
}
346+
}
347+
348+
pub struct IterMut<'a, T: 'a + UserSafe>(slice::IterMut<'a, T>);
349+
350+
impl<'a, T: UserSafe> Iterator for IterMut<'a, T> {
351+
type Item = &'a mut UserRef<T>;
352+
353+
#[inline]
354+
fn next(&mut self) -> Option<Self::Item> {
355+
unsafe {
356+
self.0.next().map(|e| UserRef::from_mut_ptr(e))
357+
}
358+
}
359+
}
360+
361+
impl<T: ?Sized> Deref for User<T> where T: UserSafe {
362+
type Target = UserRef<T>;
363+
364+
fn deref(&self) -> &Self::Target {
365+
unsafe { &*self.0 }
366+
}
367+
}
368+
369+
impl<T: ?Sized> DerefMut for User<T> where T: UserSafe {
370+
fn deref_mut(&mut self) -> &mut Self::Target {
371+
unsafe { &mut*self.0 }
372+
}
373+
}
374+
375+
impl<T: ?Sized> Drop for User<T> where T: UserSafe {
376+
fn drop(&mut self) {
377+
unsafe {
378+
let ptr = (*self.0).0.get();
379+
super::free(ptr as _, mem::size_of_val(&mut*ptr), T::align_of());
380+
}
381+
}
382+
}
383+
384+
impl<T: CoerceUnsized<U>, U> CoerceUnsized<UserRef<U>> for UserRef<T> {}
385+
386+
impl<T, I: SliceIndex<[T]>> Index<I> for UserRef<[T]> where [T]: UserSafe, I::Output: UserSafe {
387+
type Output = UserRef<I::Output>;
388+
389+
#[inline]
390+
fn index(&self, index: I) -> &UserRef<I::Output> {
391+
unsafe {
392+
UserRef::from_ptr(index.index(&*self.as_raw_ptr()))
393+
}
394+
}
395+
}
396+
397+
impl<T, I: SliceIndex<[T]>> IndexMut<I> for UserRef<[T]> where [T]: UserSafe, I::Output: UserSafe {
398+
#[inline]
399+
fn index_mut(&mut self, index: I) -> &mut UserRef<I::Output> {
400+
unsafe {
401+
UserRef::from_mut_ptr(index.index_mut(&mut*self.as_raw_mut_ptr()))
402+
}
403+
}
404+
}

‎src/libstd/sys/sgx/abi/usercalls/mod.rs

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,78 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
pub use fortanix_sgx_abi::*;
12+
13+
use io::{Error as IoError, Result as IoResult};
14+
15+
mod alloc;
1116
#[macro_use]
12-
pub mod raw;
17+
mod raw;
18+
19+
pub fn exit(panic: bool) -> ! {
20+
unsafe { raw::exit(panic) }
21+
}
22+
23+
pub fn alloc(size: usize, alignment: usize) -> IoResult<*mut u8> {
24+
unsafe { raw::alloc(size, alignment).from_sgx_result() }
25+
}
26+
27+
pub use self::raw::free;
28+
29+
fn check_os_error(err: Result) -> i32 {
30+
// FIXME: not sure how to make sure all variants of Error are covered
31+
if err == Error::NotFound as _ ||
32+
err == Error::PermissionDenied as _ ||
33+
err == Error::ConnectionRefused as _ ||
34+
err == Error::ConnectionReset as _ ||
35+
err == Error::ConnectionAborted as _ ||
36+
err == Error::NotConnected as _ ||
37+
err == Error::AddrInUse as _ ||
38+
err == Error::AddrNotAvailable as _ ||
39+
err == Error::BrokenPipe as _ ||
40+
err == Error::AlreadyExists as _ ||
41+
err == Error::WouldBlock as _ ||
42+
err == Error::InvalidInput as _ ||
43+
err == Error::InvalidData as _ ||
44+
err == Error::TimedOut as _ ||
45+
err == Error::WriteZero as _ ||
46+
err == Error::Interrupted as _ ||
47+
err == Error::Other as _ ||
48+
err == Error::UnexpectedEof as _ ||
49+
((Error::UserRangeStart as _)..=(Error::UserRangeEnd as _)).contains(&err)
50+
{
51+
err
52+
} else {
53+
panic!("Usercall: returned invalid error value {}", err)
54+
}
55+
}
56+
57+
trait FromSgxResult {
58+
type Return;
59+
60+
fn from_sgx_result(self) -> IoResult<Self::Return>;
61+
}
62+
63+
impl<T> FromSgxResult for (Result, T) {
64+
type Return = T;
65+
66+
fn from_sgx_result(self) -> IoResult<Self::Return> {
67+
if self.0 == RESULT_SUCCESS {
68+
Ok(self.1)
69+
} else {
70+
Err(IoError::from_raw_os_error(check_os_error(self.0)))
71+
}
72+
}
73+
}
74+
75+
impl FromSgxResult for Result {
76+
type Return = ();
77+
78+
fn from_sgx_result(self) -> IoResult<Self::Return> {
79+
if self == RESULT_SUCCESS {
80+
Ok(())
81+
} else {
82+
Err(IoError::from_raw_os_error(check_os_error(self)))
83+
}
84+
}
85+
}

0 commit comments

Comments
 (0)
Please sign in to comment.