Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add utility types and functions for low-level interfacing between compartments #198

Merged
merged 3 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/runtime/secgate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/secgate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
105 changes: 105 additions & 0 deletions src/runtime/secgate/src/util/buffer.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
123 changes: 123 additions & 0 deletions src/runtime/secgate/src/util/handle.rs
Original file line number Diff line number Diff line change
@@ -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<Self, Self::OpenError>
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<ServerData> {
handles: HashMap<ObjID, StableVec<ServerData>>,
max: usize,
}

impl<ServerData> HandleMgr<ServerData> {
/// 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<Descriptor> {
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<ServerData> {
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<HandleMgr<u32>>,
removed_data: Option<u32>,
}

impl Handle for FooHandle {
type OpenError = ();

type OpenInfo = (u32, RefCell<HandleMgr<u32>>);

fn open(info: Self::OpenInfo) -> Result<Self, Self::OpenError>
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());
}
}
7 changes: 7 additions & 0 deletions src/runtime/secgate/src/util/mod.rs
Original file line number Diff line number Diff line change
@@ -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::*;
1 change: 0 additions & 1 deletion tools/xtask/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()),
})
Expand Down
Loading