diff --git a/Cargo.lock b/Cargo.lock index c0ba2562..4760cb9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2694,8 +2694,10 @@ dependencies = [ "alloca", "cc", "secgate-macros", + "stable-vec", "static_assertions", "twizzler-abi", + "twizzler-runtime-api", ] [[package]] diff --git a/src/runtime/secgate/Cargo.toml b/src/runtime/secgate/Cargo.toml index e59c1d96..13705644 100644 --- a/src/runtime/secgate/Cargo.toml +++ b/src/runtime/secgate/Cargo.toml @@ -13,6 +13,8 @@ secgate-macros = { path = "secgate-macros" } static_assertions = "1.1.0" alloca = {version = "0.4", default-features = false} twizzler-abi = { path = "../../lib/twizzler-abi", default-features = false } +twizzler-runtime-api = { path = "../../lib/twizzler-runtime-api" } +stable-vec = "0.4" [build-dependencies] cc = "1.0" diff --git a/src/runtime/secgate/src/lib.rs b/src/runtime/secgate/src/lib.rs index d3aefae0..ac2640af 100644 --- a/src/runtime/secgate/src/lib.rs +++ b/src/runtime/secgate/src/lib.rs @@ -11,6 +11,8 @@ use std::{cell::UnsafeCell, marker::Tuple, mem::MaybeUninit}; pub use secgate_macros::*; use twizzler_abi::object::ObjID; +pub mod util; + /// Enum of possible return codes, similar to [Result], but with specific /// variants of possible failures of initializing or invoking the secure gate call. #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] diff --git a/src/runtime/secgate/src/util/buffer.rs b/src/runtime/secgate/src/util/buffer.rs new file mode 100644 index 00000000..a242803f --- /dev/null +++ b/src/runtime/secgate/src/util/buffer.rs @@ -0,0 +1,105 @@ +use twizzler_abi::object::{MAX_SIZE, NULLPAGE_SIZE}; +use twizzler_runtime_api::ObjectHandle; + +/// A simple buffer to use for transferring bytes between compartments, using shared memory via +/// objects underneath. +pub struct SimpleBuffer { + handle: ObjectHandle, +} + +impl SimpleBuffer { + fn ptr_to_base(&self) -> *const u8 { + unsafe { self.handle.start.add(NULLPAGE_SIZE) } + } + + fn mut_ptr_to_base(&mut self) -> *mut u8 { + unsafe { self.handle.start.add(NULLPAGE_SIZE) } + } + + /// Build a new SimpleBuffer from an object handle. + pub fn new(handle: ObjectHandle) -> Self { + Self { handle } + } + + /// Returns the maximum length of a read or write. + pub fn max_len(&self) -> usize { + MAX_SIZE - NULLPAGE_SIZE * 2 + } + + /// Read bytes from the SimpleBuffer into `buffer`, up to the size of the supplied buffer. The + /// actual number of bytes copied is returned. + pub fn read(&self, buffer: &mut [u8]) -> usize { + let base_raw = self.ptr_to_base(); + // Note that our len is not bounded by a previous write. But Twizzler objects are + // 0-initialized by default, so all bytes are initialized to 0u8. If any other data _was_ + // written to the object, that still can be read as bytes. + let len = core::cmp::min(buffer.len(), self.max_len()); + // Safety: technically, we cannot statically assert that no one else is writing this memory. + // However, since we are reading bytes directly, we will assert that this is safe, + // up to seeing torn writes. That is still UB, but it's the best we can do before + // having to introduce synchronization overhead, but since this is intended to be + // used by secure gates, that synchronization will have occurred via the secure gate call. + // While we cannot stop another compartment from violating this assumption, we are still + // reading bytes from object memory and not interpreting them. If a consumer of this + // interface chooses to cast those bytes into another type, or process them + // as UTF-8, or something, it is up to them to uphold safety guarantees (e.g. we cannot + // assume it is valid UTF-8). + let base = unsafe { core::slice::from_raw_parts(base_raw, len) }; + (&mut buffer[0..len]).copy_from_slice(base); + len + } + + /// Write bytes from `buffer` into the SimpleBuffer, up to the size of the supplied buffer. The + /// actual number of bytes copied is returned. + pub fn write(&mut self, buffer: &[u8]) -> usize { + let base_raw = self.mut_ptr_to_base(); + let len = core::cmp::min(buffer.len(), self.max_len()); + // Safety: See read function. + let base = unsafe { core::slice::from_raw_parts_mut(base_raw, len) }; + base.copy_from_slice(&buffer[0..len]); + len + } +} + +#[cfg(test)] +mod test { + use twizzler_abi::syscall::{ + sys_object_create, BackingType, LifetimeType, ObjectCreate, ObjectCreateFlags, + }; + use twizzler_runtime_api::{get_runtime, MapFlags, ObjectHandle}; + + use super::*; + + fn new_handle() -> ObjectHandle { + let id = sys_object_create( + ObjectCreate::new( + BackingType::Normal, + LifetimeType::Volatile, + None, + ObjectCreateFlags::empty(), + ), + &[], + &[], + ) + .unwrap(); + get_runtime() + .map_object(id, MapFlags::READ | MapFlags::WRITE) + .unwrap() + } + + #[test] + fn transfer() { + let obj = new_handle(); + let mut sb = SimpleBuffer::new(obj); + + let data = b"simple buffer test!"; + let wlen = sb.write(data); + let mut buf = [0u8; 19]; + assert_eq!(buf.len(), data.len()); + assert_eq!(buf.len(), wlen); + + let rlen = sb.read(&mut buf); + assert_eq!(rlen, wlen); + assert_eq!(&buf, data); + } +} diff --git a/src/runtime/secgate/src/util/handle.rs b/src/runtime/secgate/src/util/handle.rs new file mode 100644 index 00000000..fd80cdf9 --- /dev/null +++ b/src/runtime/secgate/src/util/handle.rs @@ -0,0 +1,123 @@ +use std::collections::HashMap; + +use stable_vec::StableVec; +use twizzler_runtime_api::ObjID; + +/// A handle that can be opened and released. +pub trait Handle { + /// The error type returned by open. + type OpenError; + + /// The arguments to open. + type OpenInfo; + + /// Open a handle. + fn open(info: Self::OpenInfo) -> Result + where + Self: Sized; + + /// Release a handle. After this, the handle should not be used. + fn release(&mut self); +} + +/// A handle descriptor. +pub type Descriptor = u32; + +/// A manager for open handles, per compartment. +#[derive(Default, Clone)] +pub struct HandleMgr { + handles: HashMap>, + max: usize, +} + +impl HandleMgr { + /// Construct a new HandleMgr. + pub fn new(max: usize) -> Self { + Self { + handles: HashMap::new(), + max, + } + } + + /// Get the maximum number of open handles. + pub fn max(&self) -> usize { + self.max + } + + /// Lookup the server data associated with a descriptor. + pub fn lookup(&self, comp: ObjID, ds: Descriptor) -> Option<&ServerData> { + let idx: usize = ds.try_into().ok()?; + self.handles.get(&comp).and_then(|sv| sv.get(idx)) + } + + /// Insert new server data, and return a descriptor for it. + pub fn insert(&mut self, comp: ObjID, sd: ServerData) -> Option { + let entry = self.handles.entry(comp).or_insert_with(|| StableVec::new()); + let idx = entry.next_push_index(); + if idx >= self.max && self.max > 0 { + return None; + } + let ds: Descriptor = idx.try_into().ok()?; + let pushed_idx = entry.push(sd); + debug_assert_eq!(pushed_idx, idx); + + Some(ds) + } + + /// Remove a descriptor, returning the server data if present. + pub fn remove(&mut self, comp: ObjID, ds: Descriptor) -> Option { + let idx: usize = ds.try_into().ok()?; + self.handles.get_mut(&comp)?.remove(idx) + } +} + +#[cfg(test)] +mod test { + use std::cell::RefCell; + + use super::*; + + struct FooHandle { + desc: Descriptor, + x: u32, + mgr: RefCell>, + removed_data: Option, + } + + impl Handle for FooHandle { + type OpenError = (); + + type OpenInfo = (u32, RefCell>); + + fn open(info: Self::OpenInfo) -> Result + where + Self: Sized, + { + let desc = info.1.borrow_mut().insert(0.into(), info.0).unwrap(); + Ok(Self { + desc, + x: info.0, + mgr: info.1, + removed_data: None, + }) + } + + fn release(&mut self) { + self.removed_data = self.mgr.borrow_mut().remove(0.into(), self.desc); + } + } + + #[test] + fn handle() { + let mgr = RefCell::new(HandleMgr::new(8)); + let mut foo = FooHandle::open((42, mgr)).unwrap(); + + assert_eq!(foo.x, 42); + let sd = foo.mgr.borrow().lookup(0.into(), foo.desc).cloned(); + assert_eq!(sd, Some(42)); + + foo.release(); + assert_eq!(foo.removed_data, Some(42)); + assert!(foo.mgr.borrow().lookup(0.into(), foo.desc).is_none()); + } +} diff --git a/src/runtime/secgate/src/util/mod.rs b/src/runtime/secgate/src/util/mod.rs new file mode 100644 index 00000000..95d82d24 --- /dev/null +++ b/src/runtime/secgate/src/util/mod.rs @@ -0,0 +1,7 @@ +//! A set of utility types for low-level communication between compartments. + +mod buffer; +mod handle; + +pub use buffer::*; +pub use handle::*; diff --git a/tools/xtask/src/build.rs b/tools/xtask/src/build.rs index de3a7aa5..0bb2ea01 100644 --- a/tools/xtask/src/build.rs +++ b/tools/xtask/src/build.rs @@ -285,7 +285,6 @@ fn maybe_build_tests<'a>( "twz-rt" => None, "monitor" => None, "bootstrap" => None, - "secgate" => None, "secgate-macros" => None, _ => Some(p.name().to_string()), })