]> git.lizzy.rs Git - rust.git/commitdiff
SGX target: implement user memory management
authorJethro Beekman <jethro@fortanix.com>
Wed, 5 Sep 2018 23:18:53 +0000 (16:18 -0700)
committerJethro Beekman <jethro@fortanix.com>
Fri, 7 Dec 2018 05:56:50 +0000 (11:26 +0530)
src/libstd/lib.rs
src/libstd/sys/sgx/abi/mem.rs
src/libstd/sys/sgx/abi/mod.rs
src/libstd/sys/sgx/abi/usercalls/alloc.rs [new file with mode: 0644]
src/libstd/sys/sgx/abi/usercalls/mod.rs

index bf1e64efd37c4f3c13b4636e141e6192a629683b..66771b3c4a2f01fda1faaa4b77808b68d58c441a 100644 (file)
 #![feature(non_exhaustive)]
 #![feature(alloc_layout_extra)]
 #![feature(maybe_uninit)]
-#![cfg_attr(target_env = "sgx", feature(global_asm, range_contains))]
+#![cfg_attr(target_env = "sgx", feature(global_asm, range_contains, slice_index_methods,
+                                        decl_macro, coerce_unsized))]
 
 #![default_lib_allocator]
 
index aedf6ec7acb08f4b7d0a0ce6b2a969fa01586f35..508f2ff4d4fa5e4b9cb95308555111550cee95b1 100644 (file)
 // Do not remove inline: will result in relocation failure
 #[inline(always)]
 pub unsafe fn rel_ptr<T>(offset: u64) -> *const T {
-    (image_base()+offset) as *const T
+    (image_base() + offset) as *const T
 }
 
 // Do not remove inline: will result in relocation failure
 #[inline(always)]
 pub unsafe fn rel_ptr_mut<T>(offset: u64) -> *mut T {
-    (image_base()+offset) as *mut T
+    (image_base() + offset) as *mut T
+}
+
+extern {
+    static ENCLAVE_SIZE: usize;
 }
 
 // Do not remove inline: will result in relocation failure
@@ -26,6 +30,20 @@ pub unsafe fn rel_ptr_mut<T>(offset: u64) -> *mut T {
 #[inline(always)]
 fn image_base() -> u64 {
     let base;
-    unsafe{asm!("lea IMAGE_BASE(%rip),$0":"=r"(base))};
+    unsafe { asm!("lea IMAGE_BASE(%rip),$0":"=r"(base)) };
     base
 }
+
+pub fn is_enclave_range(p: *const u8, len: usize) -> bool {
+    let start=p as u64;
+    let end=start + (len as u64);
+    start >= image_base() &&
+        end <= image_base() + (unsafe { ENCLAVE_SIZE } as u64) // unsafe ok: link-time constant
+}
+
+pub fn is_user_range(p: *const u8, len: usize) -> bool {
+    let start=p as u64;
+    let end=start + (len as u64);
+    end <= image_base() ||
+        start >= image_base() + (unsafe { ENCLAVE_SIZE } as u64) // unsafe ok: link-time constant
+}
index 99ea7a381f37b2c27614ed4d25241611c4eb79a8..612049049627fedc3aed568b7019afd2af7faaa0 100644 (file)
@@ -96,5 +96,5 @@ pub(super) fn exit_with_code(code: isize) -> ! {
             let _ = write!(out, "Exited with status code {}", code);
         }
     }
-    unsafe { usercalls::raw::exit(code != 0) };
+    usercalls::exit(code != 0);
 }
diff --git a/src/libstd/sys/sgx/abi/usercalls/alloc.rs b/src/libstd/sys/sgx/abi/usercalls/alloc.rs
new file mode 100644 (file)
index 0000000..64968a9
--- /dev/null
@@ -0,0 +1,404 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(unused)]
+
+use ptr;
+use mem;
+use cell::UnsafeCell;
+use slice;
+use ops::{Deref, DerefMut, Index, IndexMut, CoerceUnsized};
+use slice::SliceIndex;
+
+use fortanix_sgx_abi::*;
+use super::super::mem::is_user_range;
+
+/// A type that can be safely read from or written to userspace.
+///
+/// Non-exhaustive list of specific requirements for reading and writing:
+/// * **Type is `Copy`** (and therefore also not `Drop`). Copies will be
+///   created when copying from/to userspace. Destructors will not be called.
+/// * **No references or Rust-style owned pointers** (`Vec`, `Arc`, etc.). When
+///   reading from userspace, references into enclave memory must not be
+///   created. Also, only enclave memory is considered managed by the Rust
+///   compiler's static analysis. When reading from userspace, there can be no
+///   guarantee that the value correctly adheres to the expectations of the
+///   type. When writing to userspace, memory addresses of data in enclave
+///   memory must not be leaked for confidentiality reasons. `User` and
+///   `UserRef` are also not allowed for the same reasons.
+/// * **No fat pointers.** When reading from userspace, the size or vtable
+///   pointer could be automatically interpreted and used by the code. When
+///   writing to userspace, memory addresses of data in enclave memory (such
+///   as vtable pointers) must not be leaked for confidentiality reasons.
+///
+/// Non-exhaustive list of specific requirements for reading from userspace:
+/// * Any bit pattern is valid for this type (no `enum`s). There can be no
+///   guarantee that the value correctly adheres to the expectations of the
+///   type, so any value must be valid for this type.
+///
+/// Non-exhaustive list of specific requirements for writing to userspace:
+/// * No pointers to enclave memory. Memory addresses of data in enclave memory
+///   must not be leaked for confidentiality reasons.
+/// * No internal padding. Padding might contain previously-initialized secret
+///   data stored at that memory location and must not be leaked for
+///   confidentiality reasons.
+pub unsafe trait UserSafeSized: Copy + Sized {}
+
+unsafe impl UserSafeSized for u8 {}
+unsafe impl<T> UserSafeSized for FifoDescriptor<T> {}
+unsafe impl UserSafeSized for ByteBuffer {}
+unsafe impl UserSafeSized for Usercall {}
+unsafe impl UserSafeSized for Return {}
+unsafe impl<T: UserSafeSized> UserSafeSized for [T; 2] {}
+
+/// A type that can be represented in memory as one or more `UserSafeSized`s.
+pub unsafe trait UserSafe {
+    unsafe fn align_of() -> usize;
+
+    /// NB. This takes a size, not a length!
+    unsafe fn from_raw_sized_unchecked(ptr: *const u8, size: usize) -> *const Self;
+
+    /// NB. This takes a size, not a length!
+    unsafe fn from_raw_sized(ptr: *const u8, size: usize) -> *const Self {
+        let ret = Self::from_raw_sized_unchecked(ptr, size);
+        Self::check_ptr(ret);
+        ret
+    }
+
+    unsafe fn check_ptr(ptr: *const Self) {
+        let is_aligned = |p| -> bool {
+            0 == (p as usize) & (Self::align_of() - 1)
+        };
+
+        assert!(is_aligned(ptr as *const u8));
+        assert!(is_user_range(ptr as _, mem::size_of_val(&*ptr)));
+        assert!(!ptr.is_null());
+    }
+}
+
+unsafe impl<T: UserSafeSized> UserSafe for T {
+    unsafe fn align_of() -> usize {
+        mem::align_of::<T>()
+    }
+
+    unsafe fn from_raw_sized_unchecked(ptr: *const u8, size: usize) -> *const Self {
+        assert_eq!(size, mem::size_of::<T>());
+        ptr as _
+    }
+}
+
+unsafe impl<T: UserSafeSized> UserSafe for [T] {
+    unsafe fn align_of() -> usize {
+        mem::align_of::<T>()
+    }
+
+    unsafe fn from_raw_sized_unchecked(ptr: *const u8, size: usize) -> *const Self {
+        let elem_size = mem::size_of::<T>();
+        assert_eq!(size % elem_size, 0);
+        let len = size / elem_size;
+        slice::from_raw_parts(ptr as _, len)
+    }
+}
+
+/// A reference to some type in userspace memory. `&UserRef<T>` is equivalent
+/// to `&T` in enclave memory. Access to the memory is only allowed by copying
+/// to avoid TOCTTOU issues. After copying, code should make sure to completely
+/// check the value before use.
+pub struct UserRef<T: ?Sized>(UnsafeCell<T>);
+/// An owned type in userspace memory. `User<T>` is equivalent to `Box<T>` in
+/// enclave memory. Access to the memory is only allowed by copying to avoid
+/// TOCTTOU issues. The user memory will be freed when the value is dropped.
+/// After copying, code should make sure to completely check the value before
+/// use.
+pub struct User<T: UserSafe + ?Sized>(*mut UserRef<T>);
+
+impl<T: ?Sized> User<T> where T: UserSafe {
+    // This function returns memory that is practically uninitialized, but is
+    // not considered "unspecified" or "undefined" for purposes of an
+    // optimizing compiler. This is achieved by returning a pointer from
+    // from outside as obtained by `super::alloc`.
+    fn new_uninit_bytes(size: usize) -> Self {
+        unsafe {
+            let ptr = super::alloc(size, T::align_of()).expect("User memory allocation failed");
+            User(T::from_raw_sized(ptr as _, size) as _)
+        }
+    }
+
+    pub fn new_from_enclave(val: &T) -> Self {
+        unsafe {
+            let ret = Self::new_uninit_bytes(mem::size_of_val(val));
+            ptr::copy(
+                val as *const T as *const u8,
+                ret.0 as *mut T as *mut u8,
+                mem::size_of_val(val)
+            );
+            ret
+        }
+    }
+
+    /// Create an owned `User<T>` from a raw pointer. The pointer should be
+    /// freeable with the `free` usercall and the alignment of `T`.
+    ///
+    /// # Panics
+    /// This function panics if:
+    ///
+    /// * The pointer is not aligned
+    /// * The pointer is null
+    /// * The pointed-to range is not in user memory
+    pub unsafe fn from_raw(ptr: *mut T) -> Self {
+        T::check_ptr(ptr);
+        User(ptr as _)
+    }
+
+    /// Convert this value into a raw pointer. The value will no longer be
+    /// automatically freed.
+    pub fn into_raw(self) -> *mut T {
+        let ret = self.0;
+        mem::forget(self);
+        ret as _
+    }
+}
+
+impl<T> User<T> where T: UserSafe {
+    pub fn uninitialized() -> Self {
+        Self::new_uninit_bytes(mem::size_of::<T>())
+    }
+}
+
+impl<T> User<[T]> where [T]: UserSafe {
+    pub fn uninitialized(n: usize) -> Self {
+        Self::new_uninit_bytes(n * mem::size_of::<T>())
+    }
+
+    /// Create an owned `User<[T]>` from a raw thin pointer and a slice length.
+    /// The pointer should be freeable with the `free` usercall and the
+    /// alignment of `T`.
+    ///
+    /// # Panics
+    /// This function panics if:
+    ///
+    /// * The pointer is not aligned
+    /// * The pointer is null
+    /// * The pointed-to range is not in user memory
+    pub unsafe fn from_raw_parts(ptr: *mut T, len: usize) -> Self {
+        User(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::<T>()) as _)
+    }
+}
+
+impl<T: ?Sized> UserRef<T> where T: UserSafe {
+    /// Create a `&UserRef<[T]>` from a raw pointer.
+    ///
+    /// # Panics
+    /// This function panics if:
+    ///
+    /// * The pointer is not aligned
+    /// * The pointer is null
+    /// * The pointed-to range is not in user memory
+    pub unsafe fn from_ptr<'a>(ptr: *const T) -> &'a Self {
+        T::check_ptr(ptr);
+        &*(ptr as *const Self)
+    }
+
+    /// Create a `&mut UserRef<[T]>` from a raw pointer.
+    ///
+    /// # Panics
+    /// This function panics if:
+    ///
+    /// * The pointer is not aligned
+    /// * The pointer is null
+    /// * The pointed-to range is not in user memory
+    pub unsafe fn from_mut_ptr<'a>(ptr: *mut T) -> &'a mut Self {
+        T::check_ptr(ptr);
+        &mut*(ptr as *mut Self)
+    }
+
+    /// # Panics
+    /// This function panics if the destination doesn't have the same size as
+    /// the source. This can happen for dynamically-sized types such as slices.
+    pub fn copy_from_enclave(&mut self, val: &T) {
+        unsafe {
+            assert_eq!(mem::size_of_val(val), mem::size_of_val( &*self.0.get() ));
+            ptr::copy(
+                val as *const T as *const u8,
+                self.0.get() as *mut T as *mut u8,
+                mem::size_of_val(val)
+            );
+        }
+    }
+
+    /// # Panics
+    /// This function panics if the destination doesn't have the same size as
+    /// the source. This can happen for dynamically-sized types such as slices.
+    pub fn copy_to_enclave(&self, dest: &mut T) {
+        unsafe {
+            assert_eq!(mem::size_of_val(dest), mem::size_of_val( &*self.0.get() ));
+            ptr::copy(
+                self.0.get() as *const T as *const u8,
+                dest as *mut T as *mut u8,
+                mem::size_of_val(dest)
+            );
+        }
+    }
+
+    pub fn as_raw_ptr(&self) -> *const T {
+        self as *const _ as _
+    }
+
+    pub fn as_raw_mut_ptr(&mut self) -> *mut T {
+        self as *mut _ as _
+    }
+}
+
+impl<T> UserRef<T> where T: UserSafe {
+    pub fn to_enclave(&self) -> T {
+        unsafe { ptr::read(self.0.get()) }
+    }
+}
+
+impl<T> UserRef<[T]> where [T]: UserSafe {
+    /// Create a `&UserRef<[T]>` from a raw thin pointer and a slice length.
+    ///
+    /// # Panics
+    /// This function panics if:
+    ///
+    /// * The pointer is not aligned
+    /// * The pointer is null
+    /// * The pointed-to range is not in user memory
+    pub unsafe fn from_raw_parts<'a>(ptr: *const T, len: usize) -> &'a Self {
+        &*(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::<T>()) as *const Self)
+    }
+
+    /// Create a `&mut UserRef<[T]>` from a raw thin pointer and a slice length.
+    ///
+    /// # Panics
+    /// This function panics if:
+    ///
+    /// * The pointer is not aligned
+    /// * The pointer is null
+    /// * The pointed-to range is not in user memory
+    pub unsafe fn from_raw_parts_mut<'a>(ptr: *mut T, len: usize) -> &'a mut Self {
+        &mut*(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::<T>()) as *mut Self)
+    }
+
+    pub fn as_ptr(&self) -> *const T {
+        self.0.get() as _
+    }
+
+    pub fn as_mut_ptr(&mut self) -> *mut T {
+        self.0.get() as _
+    }
+
+    pub fn len(&self) -> usize {
+        unsafe { (*self.0.get()).len() }
+    }
+
+    pub fn copy_to_enclave_vec(&self, dest: &mut Vec<T>) {
+        unsafe {
+            if let Some(missing) = self.len().checked_sub(dest.capacity()) {
+                dest.reserve(missing)
+            }
+            dest.set_len(self.len());
+            self.copy_to_enclave(&mut dest[..]);
+        }
+    }
+
+    pub fn to_enclave(&self) -> Vec<T> {
+        let mut ret = Vec::with_capacity(self.len());
+        self.copy_to_enclave_vec(&mut ret);
+        ret
+    }
+
+    pub fn iter(&self) -> Iter<T>
+        where T: UserSafe // FIXME: should be implied by [T]: UserSafe?
+    {
+        unsafe {
+            Iter((&*self.as_raw_ptr()).iter())
+        }
+    }
+
+    pub fn iter_mut(&mut self) -> IterMut<T>
+        where T: UserSafe // FIXME: should be implied by [T]: UserSafe?
+    {
+        unsafe {
+            IterMut((&mut*self.as_raw_mut_ptr()).iter_mut())
+        }
+    }
+}
+
+pub struct Iter<'a, T: 'a + UserSafe>(slice::Iter<'a, T>);
+
+impl<'a, T: UserSafe> Iterator for Iter<'a, T> {
+    type Item = &'a UserRef<T>;
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        unsafe {
+            self.0.next().map(|e| UserRef::from_ptr(e))
+        }
+    }
+}
+
+pub struct IterMut<'a, T: 'a + UserSafe>(slice::IterMut<'a, T>);
+
+impl<'a, T: UserSafe> Iterator for IterMut<'a, T> {
+    type Item = &'a mut UserRef<T>;
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        unsafe {
+            self.0.next().map(|e| UserRef::from_mut_ptr(e))
+        }
+    }
+}
+
+impl<T: ?Sized> Deref for User<T> where T: UserSafe {
+    type Target = UserRef<T>;
+
+    fn deref(&self) -> &Self::Target {
+        unsafe { &*self.0 }
+    }
+}
+
+impl<T: ?Sized> DerefMut for User<T> where T: UserSafe {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        unsafe { &mut*self.0 }
+    }
+}
+
+impl<T: ?Sized> Drop for User<T> where T: UserSafe {
+    fn drop(&mut self) {
+        unsafe {
+            let ptr = (*self.0).0.get();
+            super::free(ptr as _, mem::size_of_val(&mut*ptr), T::align_of());
+        }
+    }
+}
+
+impl<T: CoerceUnsized<U>, U> CoerceUnsized<UserRef<U>> for UserRef<T> {}
+
+impl<T, I: SliceIndex<[T]>> Index<I> for UserRef<[T]> where [T]: UserSafe, I::Output: UserSafe {
+    type Output = UserRef<I::Output>;
+
+    #[inline]
+    fn index(&self, index: I) -> &UserRef<I::Output> {
+        unsafe {
+            UserRef::from_ptr(index.index(&*self.as_raw_ptr()))
+        }
+    }
+}
+
+impl<T, I: SliceIndex<[T]>> IndexMut<I> for UserRef<[T]> where [T]: UserSafe, I::Output: UserSafe {
+    #[inline]
+    fn index_mut(&mut self, index: I) -> &mut UserRef<I::Output> {
+        unsafe {
+            UserRef::from_mut_ptr(index.index_mut(&mut*self.as_raw_mut_ptr()))
+        }
+    }
+}
index 370e058badf5988a8aa72fe702b223f2ab6e187d..f7a9c3da3b2c08e3aba15c28712b5aee2d21e2d4 100644 (file)
@@ -8,5 +8,78 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+pub use fortanix_sgx_abi::*;
+
+use io::{Error as IoError, Result as IoResult};
+
+mod alloc;
 #[macro_use]
-pub mod raw;
+mod raw;
+
+pub fn exit(panic: bool) -> ! {
+    unsafe { raw::exit(panic) }
+}
+
+pub fn alloc(size: usize, alignment: usize) -> IoResult<*mut u8> {
+    unsafe { raw::alloc(size, alignment).from_sgx_result() }
+}
+
+pub use self::raw::free;
+
+fn check_os_error(err: Result) -> i32 {
+    // FIXME: not sure how to make sure all variants of Error are covered
+    if err == Error::NotFound as _ ||
+       err == Error::PermissionDenied as _ ||
+       err == Error::ConnectionRefused as _ ||
+       err == Error::ConnectionReset as _ ||
+       err == Error::ConnectionAborted as _ ||
+       err == Error::NotConnected as _ ||
+       err == Error::AddrInUse as _ ||
+       err == Error::AddrNotAvailable as _ ||
+       err == Error::BrokenPipe as _ ||
+       err == Error::AlreadyExists as _ ||
+       err == Error::WouldBlock as _ ||
+       err == Error::InvalidInput as _ ||
+       err == Error::InvalidData as _ ||
+       err == Error::TimedOut as _ ||
+       err == Error::WriteZero as _ ||
+       err == Error::Interrupted as _ ||
+       err == Error::Other as _ ||
+       err == Error::UnexpectedEof as _ ||
+       ((Error::UserRangeStart as _)..=(Error::UserRangeEnd as _)).contains(&err)
+    {
+        err
+    } else {
+        panic!("Usercall: returned invalid error value {}", err)
+    }
+}
+
+trait FromSgxResult {
+    type Return;
+
+    fn from_sgx_result(self) -> IoResult<Self::Return>;
+}
+
+impl<T> FromSgxResult for (Result, T) {
+    type Return = T;
+
+    fn from_sgx_result(self) -> IoResult<Self::Return> {
+        if self.0 == RESULT_SUCCESS {
+            Ok(self.1)
+        } else {
+            Err(IoError::from_raw_os_error(check_os_error(self.0)))
+        }
+    }
+}
+
+impl FromSgxResult for Result {
+    type Return = ();
+
+    fn from_sgx_result(self) -> IoResult<Self::Return> {
+        if self == RESULT_SUCCESS {
+            Ok(())
+        } else {
+            Err(IoError::from_raw_os_error(check_os_error(self)))
+        }
+    }
+}