From baa93d26b4dd35d35af56abbe70f728103a375f9 Mon Sep 17 00:00:00 2001 From: Daniel Bittman Date: Wed, 7 Aug 2024 11:51:29 -0700 Subject: [PATCH] Documented. --- src/lib/twizzler-abi/src/thread.rs | 25 ++++++++++- src/runtime/monitor/src/mon/mod.rs | 9 ++++ src/runtime/monitor/src/mon/space.rs | 5 +++ src/runtime/monitor/src/mon/thread.rs | 64 ++++++++------------------- 4 files changed, 57 insertions(+), 46 deletions(-) diff --git a/src/lib/twizzler-abi/src/thread.rs b/src/lib/twizzler-abi/src/thread.rs index d257f2d44..410da7a9b 100644 --- a/src/lib/twizzler-abi/src/thread.rs +++ b/src/lib/twizzler-abi/src/thread.rs @@ -4,9 +4,12 @@ use core::sync::atomic::{AtomicU64, Ordering}; #[cfg(not(feature = "kernel"))] use core::time::Duration; -use crate::marker::BaseType; #[cfg(not(feature = "kernel"))] use crate::syscall::*; +use crate::{ + marker::BaseType, + syscall::{ThreadSyncFlags, ThreadSyncOp, ThreadSyncReference, ThreadSyncSleep}, +}; #[allow(unused_imports)] use crate::{ object::{ObjID, Protections}, @@ -125,6 +128,26 @@ impl ThreadRepr { } } + /// Create a [ThreadSyncSleep] that will wait until the thread's state matches `state`. + pub fn waitable(&self, state: ExecutionState) -> ThreadSyncSleep { + ThreadSyncSleep::new( + ThreadSyncReference::Virtual(&self.status), + state as u64, + ThreadSyncOp::Equal, + ThreadSyncFlags::empty(), + ) + } + + /// Create a [ThreadSyncSleep] that will wait until the thread's state is _not_ `state`. + pub fn waitable_until_not(&self, state: ExecutionState) -> ThreadSyncSleep { + ThreadSyncSleep::new( + ThreadSyncReference::Virtual(&self.status), + state as u64, + ThreadSyncOp::Equal, + ThreadSyncFlags::INVERT, + ) + } + #[cfg(not(feature = "kernel"))] /// Wait for a thread's status to change, optionally timing out. Return value is None if timeout /// occurs, or Some((ExecutionState, code)) otherwise. diff --git a/src/runtime/monitor/src/mon/mod.rs b/src/runtime/monitor/src/mon/mod.rs index 335453abf..76d66146e 100644 --- a/src/runtime/monitor/src/mon/mod.rs +++ b/src/runtime/monitor/src/mon/mod.rs @@ -8,6 +8,12 @@ pub(crate) mod compartment; pub(crate) mod space; pub(crate) mod thread; +/// A security monitor instance. All monitor logic is implemented as methods for this type. +/// We split the state into the following components: 'space', managing the virtual memory space and +/// mapping objects, 'thread_mgr', which manages all threads owned by the monitor (typically, all +/// threads started by compartments), 'compartments', which manages compartment state, and +/// 'dynlink', which contains the dynamic linker state. The unmapper allows for background unmapping +/// and cleanup of objects and handles. pub struct Monitor { space: RwLock, thread_mgr: RwLock, @@ -18,10 +24,13 @@ pub struct Monitor { static MONITOR: OnceLock = OnceLock::new(); +/// Get the monitor instance. Panics if called before first call to [set_monitor]. pub fn get_monitor() -> &'static Monitor { MONITOR.get().unwrap() } +/// Set the monitor instance. Can only be called once. Must be called before any call to +/// [get_monitor]. pub fn set_monitor(monitor: Monitor) { if MONITOR.set(monitor).is_err() { panic!("second call to set_monitor"); diff --git a/src/runtime/monitor/src/mon/space.rs b/src/runtime/monitor/src/mon/space.rs index bdb58b2f0..9835d885c 100644 --- a/src/runtime/monitor/src/mon/space.rs +++ b/src/runtime/monitor/src/mon/space.rs @@ -14,12 +14,14 @@ pub use handle::MapHandle; pub use unmapper::Unmapper; #[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)] +/// A mapping of an object and flags. pub struct MapInfo { pub(crate) id: ObjID, pub(crate) flags: MapFlags, } #[derive(Default)] +/// An address space we can map objects into. pub struct Space { maps: HashMap, } @@ -44,6 +46,7 @@ fn mapflags_into_prot(flags: MapFlags) -> Protections { } impl Space { + /// Map an object into the space. pub fn map(&mut self, info: MapInfo) -> Result { // Can't use the entry API here because the closure may fail. let item = match self.maps.get_mut(&info) { @@ -80,6 +83,8 @@ impl Space { Ok(Arc::new(MapHandleInner::new(info, item.addrs))) } + /// Remove an object from the space. The actual unmapping syscall only happens once the returned + /// value from this function is dropped. pub fn handle_drop(&mut self, info: MapInfo) -> Option { // Missing maps in unmap should be ignored. let Some(item) = self.maps.get_mut(&info) else { diff --git a/src/runtime/monitor/src/mon/thread.rs b/src/runtime/monitor/src/mon/thread.rs index c18c04083..f8ef7dd08 100644 --- a/src/runtime/monitor/src/mon/thread.rs +++ b/src/runtime/monitor/src/mon/thread.rs @@ -3,8 +3,10 @@ use std::{collections::HashMap, mem::MaybeUninit, sync::Arc}; use dynlink::tls::TlsRegion; use twizzler_abi::{ object::NULLPAGE_SIZE, - syscall::{sys_spawn, sys_thread_exit, ThreadSyncSleep, UpcallTargetSpawnOption}, - thread::ThreadRepr, + syscall::{ + sys_spawn, sys_thread_exit, ThreadSyncReference, ThreadSyncSleep, UpcallTargetSpawnOption, + }, + thread::{ExecutionState, ThreadRepr}, upcall::{UpcallFlags, UpcallInfo, UpcallMode, UpcallOptions, UpcallTarget}, }; use twizzler_runtime_api::{ObjID, SpawnError}; @@ -14,6 +16,9 @@ use crate::{api::MONITOR_INSTANCE_ID, thread::SUPER_UPCALL_STACK_SIZE}; mod cleaner; +/// Manages all threads owned by the monitor. Typically, this is all threads. +/// Threads are spawned here and tracked in the background by a [cleaner::ThreadCleaner]. The thread +/// cleaner detects when a thread has exited and performs any final thread cleanup logic. pub struct ThreadMgr { all: HashMap, cleaner: cleaner::ThreadCleaner, @@ -21,7 +26,7 @@ pub struct ThreadMgr { impl ThreadMgr { fn do_remove(&mut self, thread: &ManagedThread) { - todo!() + self.all.remove(&thread.id); } unsafe fn spawn_thread( @@ -57,48 +62,12 @@ impl ThreadMgr { } fn do_spawn(start: unsafe extern "C" fn(usize) -> !, arg: usize) -> Result { - /* - let mut cm = COMPMAN.lock(); - let mon_comp = cm.get_monitor_dynlink_compartment(); - let super_tls = mon_comp - .build_tls_region(RuntimeThreadControl::default(), |layout| unsafe { - NonNull::new(std::alloc::alloc_zeroed(layout)) - }) - .map_err(|_| SpawnError::Other)?; - drop(cm); - let super_thread_pointer = super_tls.get_thread_pointer_value(); - - let super_stack = Box::new_zeroed_slice(SUPER_UPCALL_STACK_SIZE); - - // Safety: we are allocating and tracking both the stack and the tls region for greater than - // the lifetime of this thread. The start entry points to our given start function. - let id = unsafe { - Self::spawn_thread( - start as *const () as usize, - super_stack.as_ptr() as usize, - super_thread_pointer, - arg, - ) - }?; - - // TODO - let repr = crate::mapman::map_object(MapInfo { - id, - flags: MapFlags::READ, - }) - .unwrap(); - - Ok(Self { - id, - super_stack, - super_tls, - repr: ManagedThreadRepr::new(repr), - }) - */ todo!() } - fn do_start(main: Box) -> Result { + /// Start a thread, running the provided Box'd closure. The thread will be running in + /// monitor-mode, and will have no connection to any compartment. + pub fn start_thread(main: Box) -> Result { let main_addr = Box::into_raw(Box::new(main)) as usize; unsafe extern "C" fn managed_thread_entry(main: usize) -> ! { { @@ -113,7 +82,7 @@ impl ThreadMgr { } } -#[allow(dead_code)] +/// Internal managed thread data. pub struct ManagedThreadInner { pub id: ObjID, pub(crate) repr: ManagedThreadRepr, @@ -122,12 +91,14 @@ pub struct ManagedThreadInner { } impl ManagedThreadInner { + /// Check if this thread has exited. pub fn has_exited(&self) -> bool { - todo!() + self.repr.get_repr().get_state() == ExecutionState::Exited } + /// Create a ThreadSyncSleep that will wait until the thread has exited. pub fn waitable_until_exit(&self) -> ThreadSyncSleep { - todo!() + self.repr.get_repr().waitable(ExecutionState::Exited) } } @@ -147,8 +118,10 @@ impl Drop for ManagedThreadInner { } } +/// A thread managed by the monitor. pub type ManagedThread = Arc; +/// An owned handle to a thread's repr object. pub(crate) struct ManagedThreadRepr { handle: MapHandle, } @@ -158,6 +131,7 @@ impl ManagedThreadRepr { Self { handle } } + /// Get the thread representation structure for the associated thread. pub fn get_repr(&self) -> &ThreadRepr { let addr = self.handle.addrs().start + NULLPAGE_SIZE; unsafe { (addr as *const ThreadRepr).as_ref().unwrap() }