diff --git a/ipc-queue/src/fifo.rs b/ipc-queue/src/fifo.rs index 6b9cdda8..8c2f4579 100644 --- a/ipc-queue/src/fifo.rs +++ b/ipc-queue/src/fifo.rs @@ -5,14 +5,48 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::cell::UnsafeCell; +use std::marker::PhantomData; use std::mem; -use std::sync::atomic::{AtomicU32, AtomicU64, AtomicUsize, Ordering}; -use std::sync::Arc; +#[cfg(not(target_env = "sgx"))] +use { + std::sync::atomic::AtomicU64, + std::sync::Arc, +}; +use std::sync::atomic::{AtomicUsize, Ordering, Ordering::SeqCst}; use fortanix_sgx_abi::{FifoDescriptor, WithId}; use super::*; +// `fortanix_sgx_abi::WithId` is not `Copy` because it contains an `AtomicU64`. +// This type has the same memory layout but is `Copy` and can be marked as +// `UserSafeSized` which is needed for the `User::from_raw_parts()` below. +#[cfg(target_env = "sgx")] +#[repr(C)] +#[derive(Default, Clone, Copy)] +struct UserSafeWithId { + pub id: u64, + pub data: T, +} + +#[cfg(target_env = "sgx")] +unsafe impl UserSafeSized for UserSafeWithId {} + +#[cfg(target_env = "sgx")] +unsafe fn _sanity_check_with_id() { + use std::mem::size_of; + let _: [u8; size_of::>()] = [0u8; size_of::>()]; +} + +#[cfg(target_env = "sgx")] +#[repr(transparent)] +#[derive(Copy, Clone)] +struct WrapUsize(usize); + +#[cfg(target_env = "sgx")] +unsafe impl UserSafeSized for WrapUsize{} + +#[cfg(not(target_env = "sgx"))] pub fn bounded(len: usize, s: S) -> (Sender, Receiver) where T: Transmittable, @@ -25,6 +59,7 @@ where (tx, rx) } +#[cfg(not(target_env = "sgx"))] pub fn bounded_async(len: usize, s: S) -> (AsyncSender, AsyncReceiver) where T: Transmittable, @@ -33,15 +68,47 @@ where let arc = Arc::new(FifoBuffer::new(len)); let inner = Fifo::from_arc(arc); let tx = AsyncSender { inner: inner.clone(), synchronizer: s.clone() }; - let rx = AsyncReceiver { inner, synchronizer: s, read_epoch: Arc::new(AtomicU32::new(0)) }; + let rx = AsyncReceiver { inner, synchronizer: s, read_epoch: Arc::new(AtomicU64::new(0)) }; (tx, rx) } +#[cfg(all(test, target_env = "sgx"))] +pub(crate) fn bounded(len: usize, s: S) -> (Sender, Receiver) +where + T: Transmittable, + S: Synchronizer, +{ + use std::ops::DerefMut; + use std::os::fortanix_sgx::usercalls::alloc::User; + + // Allocate [WithId; len] in userspace + // WARNING: This creates dangling memory in userspace, use in tests only! + let mut data = User::<[UserSafeWithId]>::uninitialized(len); + data.deref_mut().iter_mut().for_each(|v| v.copy_from_enclave(&UserSafeWithId::default())); + + // WARNING: This creates dangling memory in userspace, use in tests only! + let offsets = User::::new_from_enclave(&WrapUsize(0)); + let offsets = offsets.into_raw() as *const AtomicUsize; + + let descriptor = FifoDescriptor { + data: data.into_raw() as _, + len, + offsets, + }; + + let inner = unsafe { Fifo::from_descriptor(descriptor) }; + let tx = Sender { inner: inner.clone(), synchronizer: s.clone() }; + let rx = Receiver { inner, synchronizer: s }; + (tx, rx) +} + +#[cfg(not(target_env = "sgx"))] pub(crate) struct FifoBuffer { data: Box<[WithId]>, offsets: Box, } +#[cfg(not(target_env = "sgx"))] impl FifoBuffer { fn new(len: usize) -> Self { assert!( @@ -57,16 +124,18 @@ impl FifoBuffer { } } -enum Storage { +enum Storage { + #[cfg(not(target_env = "sgx"))] Shared(Arc>), - Static, + Static(PhantomData<&'static T>), } impl Clone for Storage { fn clone(&self) -> Self { match self { + #[cfg(not(target_env = "sgx"))] Storage::Shared(arc) => Storage::Shared(arc.clone()), - Storage::Static => Storage::Static, + Storage::Static(p) => Storage::Static(*p), } } } @@ -100,42 +169,23 @@ impl Fifo { "Fifo len should be a power of two" ); #[cfg(target_env = "sgx")] { - use std::os::fortanix_sgx::usercalls::alloc::{User, UserRef}; - - // `fortanix_sgx_abi::WithId` is not `Copy` because it contains an `AtomicU64`. - // This type has the same memory layout but is `Copy` and can be marked as - // `UserSafeSized` which is needed for the `User::from_raw_parts()` below. - #[repr(C)] - #[derive(Clone, Copy)] - pub struct WithId { - pub id: u64, - pub data: T, - } - unsafe impl UserSafeSized for WithId {} - - unsafe fn _sanity_check_with_id() { - use std::mem::size_of; - let _: [u8; size_of::>()] = [0u8; size_of::>()]; - } - - #[repr(transparent)] - #[derive(Copy, Clone)] - struct WrapUsize(usize); - unsafe impl UserSafeSized for WrapUsize{} + use std::os::fortanix_sgx::usercalls::alloc::User; // check pointers are outside enclave range, etc. - let data = User::<[WithId]>::from_raw_parts(descriptor.data as _, descriptor.len); + let data = User::<[UserSafeWithId]>::from_raw_parts(descriptor.data as _, descriptor.len); mem::forget(data); UserRef::from_ptr(descriptor.offsets as *const WrapUsize); + } let data_slice = std::slice::from_raw_parts(descriptor.data, descriptor.len); Self { data: &*(data_slice as *const [WithId] as *const [UnsafeCell>]), offsets: &*descriptor.offsets, - storage: Storage::Static, + storage: Storage::Static(PhantomData::default()), } } + #[cfg(not(target_env = "sgx"))] fn from_arc(fifo: Arc>) -> Self { unsafe { Self { @@ -148,10 +198,11 @@ impl Fifo { /// Consumes `self` and returns a DescriptorGuard. /// Panics if `self` was created using `from_descriptor`. + #[cfg(not(target_env = "sgx"))] pub(crate) fn into_descriptor_guard(self) -> DescriptorGuard { let arc = match self.storage { Storage::Shared(arc) => arc, - Storage::Static => panic!("Sender/Receiver created using `from_descriptor()` cannot be turned into DescriptorGuard."), + Storage::Static(_) => panic!("Sender/Receiver created using `from_descriptor()` cannot be turned into DescriptorGuard."), }; let descriptor = FifoDescriptor { data: self.data.as_ptr() as _, @@ -183,9 +234,11 @@ impl Fifo { }; // 4. Write the data, then the `id`. - let slot = unsafe { &mut *self.data[new.write_offset()].get() }; - slot.data = val.data; - slot.id.store(val.id, Ordering::SeqCst); + unsafe { + let slot = &mut *self.data[new.write_offset()].get(); + T::write(&mut slot.data, &val.data); + slot.id.store(val.id, SeqCst); + } // 5. If the queue was empty in step 1, signal the reader to wake up. Ok(was_empty) @@ -216,8 +269,9 @@ impl Fifo { }; // 6. Read the data, then store `0` in the `id`. - let val = Identified { id, data: slot.data }; - slot.id.store(0, Ordering::SeqCst); + let data = unsafe { T::read(&slot.data) }; + let val = Identified { id, data }; + slot.id.store(0, SeqCst); // 7. Store the new read offset, retrieving the old offsets. let before = fetch_adjust( diff --git a/ipc-queue/src/interface_async.rs b/ipc-queue/src/interface_async.rs index 5571a763..68fd63c3 100644 --- a/ipc-queue/src/interface_async.rs +++ b/ipc-queue/src/interface_async.rs @@ -42,6 +42,7 @@ impl AsyncSender { /// Consumes `self` and returns a DescriptorGuard. /// The returned guard can be used to make `FifoDescriptor`s that remain /// valid as long as the guard is not dropped. + #[cfg(not(target_env = "sgx"))] pub fn into_descriptor_guard(self) -> DescriptorGuard { self.inner.into_descriptor_guard() } @@ -82,6 +83,7 @@ impl AsyncReceiver { /// Consumes `self` and returns a DescriptorGuard. /// The returned guard can be used to make `FifoDescriptor`s that remain /// valid as long as the guard is not dropped. + #[cfg(not(target_env = "sgx"))] pub fn into_descriptor_guard(self) -> DescriptorGuard { self.inner.into_descriptor_guard() } diff --git a/ipc-queue/src/interface_sync.rs b/ipc-queue/src/interface_sync.rs index dfed16d4..1e07cafa 100644 --- a/ipc-queue/src/interface_sync.rs +++ b/ipc-queue/src/interface_sync.rs @@ -156,6 +156,7 @@ impl<'r, T: Transmittable, S: Synchronizer> Iterator for TryIter<'r, T, S> { #[cfg(test)] mod tests { + use crate::fifo::bounded; use crate::test_support::pubsub::{Channel, Subscription}; use crate::test_support::TestValue; use crate::*; diff --git a/ipc-queue/src/lib.rs b/ipc-queue/src/lib.rs index 85b2a36b..b67fe0ac 100644 --- a/ipc-queue/src/lib.rs +++ b/ipc-queue/src/lib.rs @@ -7,15 +7,22 @@ #![cfg_attr(target_env = "sgx", feature(sgx_platform))] use std::future::Future; -#[cfg(target_env = "sgx")] -use std::os::fortanix_sgx::usercalls::alloc::UserSafeSized; use std::pin::Pin; -use std::sync::atomic::AtomicU32; +use std::sync::atomic::AtomicU64; use std::sync::Arc; use fortanix_sgx_abi::FifoDescriptor; -use self::fifo::{Fifo, FifoBuffer}; +use self::fifo::Fifo; + +#[cfg(target_env = "sgx")] +use std::os::fortanix_sgx::usercalls::alloc::{UserRef, UserSafeSized}; + +#[cfg(not(target_env = "sgx"))] +use { + std::ptr, + self::fifo::FifoBuffer, +}; mod fifo; mod interface_sync; @@ -25,17 +32,36 @@ mod position; mod test_support; #[cfg(target_env = "sgx")] -pub trait Transmittable: UserSafeSized + Default {} +pub trait Transmittable: UserSafeSized + Default { + unsafe fn write(ptr: *mut Self, val: &Self) { + UserRef::::from_mut_ptr(ptr).copy_from_enclave(val) + } + + unsafe fn read(ptr: *const Self) -> Self { + let mut data = Default::default(); + UserRef::::from_ptr(ptr).copy_to_enclave(&mut data); + data + } +} #[cfg(target_env = "sgx")] impl Transmittable for T where T: UserSafeSized + Default {} #[cfg(not(target_env = "sgx"))] -pub trait Transmittable: Copy + Sized + Default {} +pub trait Transmittable: Copy + Sized + Default { + unsafe fn write(ptr: *mut Self, val: &Self) { + ptr::write(ptr, *val); + } + + unsafe fn read(ptr: *const Self) -> Self { + ptr::read(ptr) + } +} #[cfg(not(target_env = "sgx"))] impl Transmittable for T where T: Copy + Sized + Default {} +#[cfg(not(target_env = "sgx"))] pub fn bounded(len: usize, s: S) -> (Sender, Receiver) where T: Transmittable, @@ -44,6 +70,7 @@ where self::fifo::bounded(len, s) } +#[cfg(not(target_env = "sgx"))] pub fn bounded_async(len: usize, s: S) -> (AsyncSender, AsyncReceiver) where T: Transmittable, @@ -125,13 +152,14 @@ pub struct AsyncSender { pub struct AsyncReceiver { inner: Fifo, synchronizer: S, - read_epoch: Arc, + read_epoch: Arc, } /// `DescriptorGuard` can produce a `FifoDescriptor` that is guaranteed /// to remain valid as long as the DescriptorGuard is not dropped. pub struct DescriptorGuard { descriptor: FifoDescriptor, + #[cfg(not(target_env = "sgx"))] _fifo: Arc>, } @@ -147,7 +175,7 @@ impl DescriptorGuard { /// read to/from the queue. This is useful in case we want to know whether or /// not a particular value written to the queue has been read. pub struct PositionMonitor { - read_epoch: Arc, + read_epoch: Arc, fifo: Fifo, }