/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #![allow(unsafe_code)] use owning_ref::StableAddress; use shared_memory::EventSet; use shared_memory::EventState; use shared_memory::EventWait; use shared_memory::SharedMem; use shared_memory::SharedMemCast; use shared_memory::Timeout; use std::cell::UnsafeCell; use std::mem; use std::ops::Deref; use std::ptr; use std::slice; use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicPtr; use std::sync::atomic::AtomicU64; use std::sync::atomic::AtomicUsize; use crate::allocator::ShmemMetadata; use crate::shared_channel::SharedChannel; use crate::shared_channel::SharedReceiver; use crate::shared_channel::SharedSender; use crate::shared_rc::SharedRcContents; use crate::AtomicSharedAddress; use crate::AtomicSharedAddressRange; use crate::ObjectOffset; use crate::ObjectSize; use crate::SharedAddress; use crate::SharedAddressRange; use crate::SharedBox; use crate::SharedOption; use crate::SharedRc; use crate::SharedVec; use crate::ShmemId; use crate::ShmemName; /// A marker trait for types that it's safe to take a reference to in shared memory. /// /// This is more restrictive than `SharedMemoryCast`, for example even though `usize` is /// `SharedMemoryCast` it's not safe to make a `&usize` pointing into shared memory, /// because if another process writes to that address, then that causes UB /// (the rust compiler is allowed to assume that the value pointed to by a `&usize` /// doesn't change during the references lifetime). /// /// I don't think there's a safe mutable version of this, since it would then be /// possible to create `&mut T` which alias. For example if `Box` /// implemented this, and an honest agent put two such boxes into shared memory, /// and an attacker overwrote the address of the second by the address of the first, /// then an honest agent could create two `&mut AtomicUsize` that alias, which is /// insta-UB. /// /// `SharedMemRef` is to `SharedMemCast` as `Sync` is to `Send`. pub unsafe trait SharedMemRef {} unsafe impl SharedMemRef for AtomicBool {} unsafe impl SharedMemRef for AtomicUsize {} unsafe impl SharedMemRef for AtomicU64 {} unsafe impl SharedMemRef for AtomicPtr {} // etc. unsafe impl SharedMemRef for () {} unsafe impl SharedMemRef for (T1,) where T1: SharedMemRef {} unsafe impl SharedMemRef for (T1, T2) where T1: SharedMemRef, T2: SharedMemRef, { } unsafe impl SharedMemRef for (T1, T2, T3) where T1: SharedMemRef, T2: SharedMemRef, T3: SharedMemRef, { } // etc unsafe impl SharedMemRef for AtomicSharedAddress {} unsafe impl SharedMemRef for AtomicSharedAddressRange {} unsafe impl SharedMemRef for ShmemMetadata {} unsafe impl SharedMemRef for SharedChannel {} unsafe impl SharedMemRef for SharedOption {} unsafe impl SharedMemRef for SharedRc {} unsafe impl SharedMemRef for SharedRcContents {} unsafe impl SharedMemRef for SharedReceiver {} unsafe impl SharedMemRef for SharedSender {} unsafe impl SharedMemRef for SharedVec {} unsafe impl SharedMemRef for Volatile {} // Implementations of `SharedMemCast` for types in this crate unsafe impl SharedMemCast for AtomicSharedAddress {} unsafe impl SharedMemCast for AtomicSharedAddressRange {} unsafe impl SharedMemCast for ObjectOffset {} unsafe impl SharedMemCast for ObjectSize {} unsafe impl SharedMemCast for SharedAddress {} unsafe impl SharedMemCast for SharedAddressRange {} unsafe impl SharedMemCast for ShmemId {} unsafe impl SharedMemCast for ShmemMetadata {} unsafe impl SharedMemCast for ShmemName {} unsafe impl SharedMemCast for SharedBox {} unsafe impl SharedMemCast for SharedChannel {} unsafe impl SharedMemCast for SharedOption {} unsafe impl SharedMemCast for SharedRc {} unsafe impl SharedMemCast for SharedRcContents {} unsafe impl SharedMemCast for SharedReceiver {} unsafe impl SharedMemCast for SharedSender {} unsafe impl SharedMemCast for SharedVec {} unsafe impl SharedMemCast for Volatile {} // `Volatile` is `Send` and `Sync`. unsafe impl Sync for Volatile {} unsafe impl Send for Volatile {} /// A wrapper round the `SharedMem` type which implements `Sync`. pub struct SyncSharedMem(*mut Volatile, usize, SharedMem); impl SyncSharedMem { /// Create a new `SyncSharedMem` from a `SharedMem`. pub fn from_shmem(shmem: SharedMem) -> SyncSharedMem { let ptr = shmem.get_ptr() as *mut Volatile; let size = shmem.get_size(); let result = SyncSharedMem(ptr, size, shmem); result } /// Set an event pub fn set_event(&self, index: usize, state: EventState) { // Very annoyingly, we have to do this INCREDIBLY UNSAFE cast, // because set takes a &mut self, even though it never uses // the fact it's &mut. It would be nice if there was a way // to use events safely without locking. let this = &self.2 as *const SharedMem as *mut SharedMem; let this = unsafe { &mut *this }; let _ = this.set(index, state); } /// Wait for an event pub fn wait_event(&self, index: usize, timeout: Timeout) { // Very annoyingly, we have to do this INCREDIBLY UNSAFE cast, // because wait takes a &mut self, even though it never uses // the fact it's &mut. It would be nice if there was a way // to use events safely without locking. let this = &self.2 as *const SharedMem as *mut SharedMem; let this = unsafe { &mut *this }; let _ = this.wait(index, timeout); } } impl Deref for SyncSharedMem { type Target = [Volatile]; fn deref(&self) -> &[Volatile] { unsafe { slice::from_raw_parts(self.0, self.1) } } } unsafe impl Sync for SyncSharedMem {} unsafe impl StableAddress for SyncSharedMem {} /// Data stored in memory that can be changed /// at any time, for example shared memory. /// /// It is safe to take a reference to a `Volatile` in shared memory, /// even when it is not safe to take a reference to a `T` in shared memory. /// For example if a `usize` is stored in shared memory, it is not safe to take /// a `&usize` reference to it, since the Rust compiler assumes that the value pointed /// to is immutable, and it's UB if the value changes. /// /// The trait `SharedMemRef` allows a `&Volatile` to be dereferenced as a `&T` when /// its safe to do so, for example for `AtomicUsize`. pub struct Volatile(UnsafeCell); impl Volatile { /// Create a new volatile. pub fn new(value: T) -> Volatile { Volatile(UnsafeCell::new(value)) } /// A volatile whose byte representation is all zeros. pub fn zeroed() -> Volatile { unsafe { mem::zeroed() } } /// Try to create a volatile from some volatile bytes. /// Returns `None` if there are not enough bytes. pub fn from_volatile_bytes(bytes: &[Volatile]) -> Option<&Volatile> { unsafe { if mem::size_of::() <= bytes.len() { (bytes.as_ptr() as *const Volatile).as_ref() } else { None } } } /// Try to create a slice of volatiles from some volatile bytes. /// Returns `None` if there are not enough bytes. pub fn slice_from_volatile_bytes( bytes: &[Volatile], length: usize, ) -> Option<&[Volatile]> { unsafe { if mem::size_of::() * length <= bytes.len() { let ptr = bytes.as_ptr() as *const Volatile; Some(slice::from_raw_parts(ptr, length)) } else { None } } } /// The volatile as a raw pointer. pub fn as_ptr(&self) -> *mut T { self.0.get() } /// Performs a volatile read of the underlying value without moving it. This leaves the memory unchanged. /// /// This has the same semantics as `ptr::read_volatile`. /// /// This may have unexpected results if `T` does not implement `Copy`, pub fn read_volatile(&self) -> T { unsafe { self.as_ptr().read_volatile() } } /// Performs a volatile write of the underlying value with the given value without reading or dropping the old value. /// /// This has the same semantics as `ptr::write_volatile`. /// /// This may have unexpected results if `T` has drop code. pub fn write_volatile(&self, value: T) { unsafe { self.as_ptr().write_volatile(value); } } } impl Deref for Volatile { type Target = T; fn deref(&self) -> &T { unsafe { &*self.as_ptr() } } } /// Convert a slice of volatile values that implement `SharedMemRef` /// into a slice of values. pub fn slice_from_volatile(slice: &[Volatile]) -> &[T] { unsafe { mem::transmute(slice) } } // Is there a way to do this with safe code? pub(crate) fn slice_empty<'a, T: 'a>() -> &'a [T] { unsafe { slice::from_raw_parts(ptr::NonNull::dangling().as_ptr(), 0) } } // Various conversions between `u64` and shared addresses. impl From for SharedAddress { fn from(data: u64) -> SharedAddress { unsafe { mem::transmute(data) } } } impl From for u64 { fn from(address: SharedAddress) -> u64 { unsafe { mem::transmute(address) } } } impl From for SharedAddressRange { fn from(data: u64) -> SharedAddressRange { unsafe { mem::transmute(data) } } } impl From for u64 { fn from(address: SharedAddressRange) -> u64 { unsafe { mem::transmute(address) } } }