From: Jethro Beekman Date: Wed, 5 Sep 2018 23:18:53 +0000 (-0700) Subject: SGX target: implement user memory management X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=1e44e2de6c302855c1da75f79227fca743d9b209;p=rust.git SGX target: implement user memory management --- diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index bf1e64efd37..66771b3c4a2 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -312,7 +312,8 @@ #![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] diff --git a/src/libstd/sys/sgx/abi/mem.rs b/src/libstd/sys/sgx/abi/mem.rs index aedf6ec7acb..508f2ff4d4f 100644 --- a/src/libstd/sys/sgx/abi/mem.rs +++ b/src/libstd/sys/sgx/abi/mem.rs @@ -11,13 +11,17 @@ // Do not remove inline: will result in relocation failure #[inline(always)] pub unsafe fn rel_ptr(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(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(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 +} diff --git a/src/libstd/sys/sgx/abi/mod.rs b/src/libstd/sys/sgx/abi/mod.rs index 99ea7a381f3..61204904962 100644 --- a/src/libstd/sys/sgx/abi/mod.rs +++ b/src/libstd/sys/sgx/abi/mod.rs @@ -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 index 00000000000..64968a9970c --- /dev/null +++ b/src/libstd/sys/sgx/abi/usercalls/alloc.rs @@ -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 or the MIT license +// , 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 UserSafeSized for FifoDescriptor {} +unsafe impl UserSafeSized for ByteBuffer {} +unsafe impl UserSafeSized for Usercall {} +unsafe impl UserSafeSized for Return {} +unsafe impl 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 UserSafe for T { + unsafe fn align_of() -> usize { + mem::align_of::() + } + + unsafe fn from_raw_sized_unchecked(ptr: *const u8, size: usize) -> *const Self { + assert_eq!(size, mem::size_of::()); + ptr as _ + } +} + +unsafe impl UserSafe for [T] { + unsafe fn align_of() -> usize { + mem::align_of::() + } + + unsafe fn from_raw_sized_unchecked(ptr: *const u8, size: usize) -> *const Self { + let elem_size = mem::size_of::(); + 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` 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(UnsafeCell); +/// An owned type in userspace memory. `User` is equivalent to `Box` 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(*mut UserRef); + +impl User 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` 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 User where T: UserSafe { + pub fn uninitialized() -> Self { + Self::new_uninit_bytes(mem::size_of::()) + } +} + +impl User<[T]> where [T]: UserSafe { + pub fn uninitialized(n: usize) -> Self { + Self::new_uninit_bytes(n * mem::size_of::()) + } + + /// 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::()) as _) + } +} + +impl UserRef 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 UserRef where T: UserSafe { + pub fn to_enclave(&self) -> T { + unsafe { ptr::read(self.0.get()) } + } +} + +impl 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::()) 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::()) 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) { + 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 { + let mut ret = Vec::with_capacity(self.len()); + self.copy_to_enclave_vec(&mut ret); + ret + } + + pub fn iter(&self) -> Iter + where T: UserSafe // FIXME: should be implied by [T]: UserSafe? + { + unsafe { + Iter((&*self.as_raw_ptr()).iter()) + } + } + + pub fn iter_mut(&mut self) -> IterMut + 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; + + #[inline] + fn next(&mut self) -> Option { + 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; + + #[inline] + fn next(&mut self) -> Option { + unsafe { + self.0.next().map(|e| UserRef::from_mut_ptr(e)) + } + } +} + +impl Deref for User where T: UserSafe { + type Target = UserRef; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.0 } + } +} + +impl DerefMut for User where T: UserSafe { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut*self.0 } + } +} + +impl Drop for User 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, U> CoerceUnsized> for UserRef {} + +impl> Index for UserRef<[T]> where [T]: UserSafe, I::Output: UserSafe { + type Output = UserRef; + + #[inline] + fn index(&self, index: I) -> &UserRef { + unsafe { + UserRef::from_ptr(index.index(&*self.as_raw_ptr())) + } + } +} + +impl> IndexMut for UserRef<[T]> where [T]: UserSafe, I::Output: UserSafe { + #[inline] + fn index_mut(&mut self, index: I) -> &mut UserRef { + unsafe { + UserRef::from_mut_ptr(index.index_mut(&mut*self.as_raw_mut_ptr())) + } + } +} diff --git a/src/libstd/sys/sgx/abi/usercalls/mod.rs b/src/libstd/sys/sgx/abi/usercalls/mod.rs index 370e058badf..f7a9c3da3b2 100644 --- a/src/libstd/sys/sgx/abi/usercalls/mod.rs +++ b/src/libstd/sys/sgx/abi/usercalls/mod.rs @@ -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; +} + +impl FromSgxResult for (Result, T) { + type Return = T; + + fn from_sgx_result(self) -> IoResult { + 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 { + if self == RESULT_SUCCESS { + Ok(()) + } else { + Err(IoError::from_raw_os_error(check_os_error(self))) + } + } +}