monitor_api/
lib.rs

1//! This crate exists to break a circular dependency between twz-rt and monitor. We use extern
2//! symbols so that we can just call into the monitor without having to have it as an explicit
3//! dependency.
4
5#![feature(linkage)]
6#![feature(thread_local)]
7#![feature(pointer_is_aligned_to)]
8#![feature(tuple_trait)]
9#![allow(unexpected_cfgs)]
10use std::{
11    alloc::Layout,
12    cell::UnsafeCell,
13    marker::{PhantomData, Tuple},
14    ptr::NonNull,
15    sync::{
16        atomic::{AtomicPtr, AtomicU32, AtomicU64, Ordering},
17        OnceLock,
18    },
19};
20
21pub use dynlink::{
22    context::NewCompartmentFlags,
23    tls::{Tcb, TlsRegion},
24};
25use secgate::{
26    util::{Descriptor, Handle},
27    Crossing, DynamicSecGate,
28};
29use twizzler_abi::{
30    object::{ObjID, MAX_SIZE, NULLPAGE_SIZE},
31    syscall::{sys_thread_sync, ThreadSync, ThreadSyncReference, ThreadSyncWake},
32};
33use twizzler_rt_abi::{
34    bindings::{binding_info, ctor_set},
35    debug::{DlPhdrInfo, LinkMap, LoadedImageId},
36    error::{ArgumentError, TwzError},
37    thread::ThreadSpawnArgs,
38};
39
40#[secgate::gatecall]
41pub fn monitor_rt_get_library_info(desc: Descriptor) -> Result<LibraryInfoRaw, TwzError> {}
42
43#[secgate::gatecall]
44pub fn monitor_rt_spawn_thread(
45    args: ThreadSpawnArgs,
46    thread_pointer: usize,
47    stack_pointer: usize,
48) -> Result<ObjID, TwzError> {
49}
50
51#[secgate::gatecall]
52pub fn monitor_rt_get_comp_config() -> Result<usize, TwzError> {}
53#[secgate::gatecall]
54pub fn monitor_rt_get_library_handle(
55    compartment: Option<Descriptor>,
56    lib_n: usize,
57) -> Result<Descriptor, TwzError> {
58}
59
60#[secgate::gatecall]
61pub fn monitor_rt_get_compartment_handle(compartment: ObjID) -> Result<Descriptor, TwzError> {}
62
63#[secgate::gatecall]
64pub fn monitor_rt_get_compartment_info(
65    desc: Option<Descriptor>,
66) -> Result<CompartmentInfoRaw, TwzError> {
67}
68
69#[secgate::gatecall]
70pub fn monitor_rt_compartment_dynamic_gate(
71    desc: Option<Descriptor>,
72    name_len: usize,
73) -> Result<usize, TwzError> {
74}
75
76#[secgate::gatecall]
77pub fn monitor_rt_get_compartment_deps(
78    desc: Option<Descriptor>,
79    dep_n: usize,
80) -> Result<Descriptor, TwzError> {
81}
82
83#[secgate::gatecall]
84pub fn monitor_rt_get_compartment_thread(
85    desc: Option<Descriptor>,
86    dep_n: usize,
87) -> Result<ThreadInfo, TwzError> {
88}
89
90#[secgate::gatecall]
91pub fn monitor_rt_lookup_compartment(name_len: usize) -> Result<Descriptor, TwzError> {}
92
93#[secgate::gatecall]
94pub fn monitor_rt_load_compartment(
95    root_object: ObjID,
96    name_len: u64,
97    args_len: u64,
98    env_len: u64,
99    flags: u32,
100    config: u64,
101) -> Result<Descriptor, TwzError> {
102}
103
104#[secgate::gatecall]
105pub fn monitor_rt_compartment_wait(desc: Option<Descriptor>, flags: u64) -> Result<u64, TwzError> {}
106
107#[secgate::gatecall]
108pub fn monitor_rt_drop_compartment_handle(desc: Descriptor) -> Result<(), TwzError> {}
109
110#[secgate::gatecall]
111pub fn monitor_rt_load_library(
112    compartment: Option<Descriptor>,
113    id: Option<ObjID>,
114    name_len: usize,
115) -> Result<(Descriptor, usize), TwzError> {
116}
117
118#[secgate::gatecall]
119pub fn monitor_rt_drop_library_handle(desc: Descriptor) -> Result<(), TwzError> {}
120
121#[secgate::gatecall]
122pub fn monitor_rt_object_map(
123    id: ObjID,
124    flags: twizzler_rt_abi::object::MapFlags,
125) -> Result<crate::MappedObjectAddrs, TwzError> {
126}
127
128#[secgate::gatecall]
129pub fn monitor_rt_object_pair_map(
130    id: ObjID,
131    flags: twizzler_rt_abi::object::MapFlags,
132    id2: ObjID,
133    flags2: twizzler_rt_abi::object::MapFlags,
134) -> Result<(crate::MappedObjectAddrs, crate::MappedObjectAddrs), TwzError> {
135}
136
137#[secgate::gatecall]
138pub fn monitor_rt_object_unmap(
139    id: ObjID,
140    flags: twizzler_rt_abi::object::MapFlags,
141) -> Result<(), TwzError> {
142}
143
144#[secgate::gatecall]
145pub fn monitor_rt_get_thread_simple_buffer() -> Result<ObjID, TwzError> {}
146#[secgate::gatecall]
147pub fn monitor_rt_comp_ctrl(cmd: MonitorCompControlCmd) -> Result<Option<i32>, TwzError> {}
148#[secgate::gatecall]
149pub fn monitor_rt_stats() -> Result<MonitorStats, TwzError> {}
150#[secgate::gatecall]
151pub fn monitor_rt_set_nameroot(root: ObjID) -> Result<(), TwzError> {}
152#[secgate::gatecall]
153pub fn monitor_rt_post_signal(
154    comp: Option<ObjID>,
155    signal: u64,
156    flags: PostSignalFlags,
157) -> Result<(), TwzError> {
158}
159
160#[secgate::gatecall]
161pub fn monitor_rt_libname_map(namelen: usize, id: ObjID) -> Result<(), TwzError> {}
162
163#[secgate::gatecall]
164pub fn monitor_rt_libname_unmap(namelen: Option<usize>, id: Option<ObjID>) -> Result<(), TwzError> {
165}
166
167#[secgate::gatecall]
168pub fn monitor_rt_set_controller(comp: ObjID, controller: ObjID) -> Result<(), TwzError> {}
169
170#[secgate::gatecall]
171pub fn monitor_rt_lookup_compartment_id(id: ObjID) -> Result<Descriptor, TwzError> {}
172
173/// Look up a symbol by name, searching within the library identified by `lib_desc`
174/// (or across all libraries in the caller's compartment if `lib_desc` is `None`).
175/// The symbol name (as UTF-8 bytes) must be written to the per-thread simple buffer before
176/// calling, and `name_len` must be the number of bytes written.
177/// Returns the relocated symbol address (as a `usize`) on success.
178#[secgate::gatecall]
179pub fn monitor_rt_lookup_symbol(
180    lib_desc: Option<Descriptor>,
181    name_len: usize,
182) -> Result<usize, TwzError> {
183}
184/// Shared data between the monitor and a compartment runtime. Written to by the monitor, and
185/// read-only from the compartment.
186#[repr(C)]
187pub struct SharedCompConfig {
188    /// The security context that this compartment derives from. Read-only, will not be
189    /// overwritten.
190    pub sctx: ObjID,
191    // Pointer to the current TLS template. Read-only by compartment, writable by monitor.
192    tls_template: AtomicPtr<TlsTemplateInfo>,
193    /// The root library ID for this compartment. May be None if no libraries have been loaded.
194    pub root_library_id: Option<LoadedImageId>,
195    pub posted_signals: AtomicU64,
196    pub loader_config: CompartmentLoaderConfig,
197}
198
199struct CompConfigFinder {
200    config: *const SharedCompConfig,
201}
202
203// Safety: the compartment config address is stable over the life of the compartment and doesn't
204// change after init.
205unsafe impl Sync for CompConfigFinder {}
206unsafe impl Send for CompConfigFinder {}
207
208static COMP_CONFIG: OnceLock<CompConfigFinder> = OnceLock::new();
209
210/// Get a reference to this compartment's [SharedCompConfig].
211pub fn get_comp_config() -> &'static SharedCompConfig {
212    unsafe {
213        COMP_CONFIG
214            .get_or_init(|| CompConfigFinder {
215                config: monitor_rt_get_comp_config().unwrap() as *const _,
216            })
217            .config
218            .as_ref()
219            .unwrap()
220    }
221}
222
223/// Tries to set the comp config pointer. May fail, as this can only be set once.
224/// The comp config pointer is automatically determined if [get_comp_config] is called
225/// without comp config being set, by cross-compartment call into monitor.
226pub fn set_comp_config(cfg: &'static SharedCompConfig) -> Result<(), ()> {
227    COMP_CONFIG
228        .set(CompConfigFinder { config: cfg })
229        .map_err(|_| ())
230}
231
232/// Information about a monitor-generated TLS template.
233#[repr(C)]
234#[derive(Clone, Copy, Debug)]
235pub struct TlsTemplateInfo {
236    pub gen: u64,
237    pub layout: Layout,
238    pub alloc_base: NonNull<u8>,
239    pub tp_offset: usize,
240    pub dtv_offset: usize,
241    pub num_dtv_entries: usize,
242    pub module_top_offset: usize,
243}
244
245// Safety: this type is designed to pass pointers to thread-local memory across boundaries, so we
246// assert this is safe.
247unsafe impl Send for TlsTemplateInfo {}
248unsafe impl Sync for TlsTemplateInfo {}
249
250impl From<TlsRegion> for TlsTemplateInfo {
251    fn from(value: TlsRegion) -> Self {
252        let offset = |ptr: NonNull<u8>| -> usize {
253            unsafe {
254                ptr.as_ptr()
255                    .byte_offset_from(value.alloc_base.as_ptr())
256                    .try_into()
257                    .unwrap()
258            }
259        };
260        Self {
261            gen: value.gen,
262            layout: value.layout,
263            alloc_base: value.alloc_base,
264            num_dtv_entries: value.num_dtv_entries,
265            tp_offset: offset(value.thread_pointer),
266            dtv_offset: offset(value.dtv.cast()),
267            module_top_offset: offset(value.module_top),
268        }
269    }
270}
271
272impl TlsTemplateInfo {
273    /// Initialize a newly allocated memory region with a TLS template and TCB data.
274    ///
275    /// # Safety
276    /// The new pointer must point to a memory region that meets the requirements in self.layout.
277    pub unsafe fn init_new_tls_region<T>(&self, new: *mut u8, tcb_data: T) -> *mut Tcb<T> {
278        assert!(new.is_aligned_to(self.layout.align()));
279        // Step 1: copy the template to the new memory.
280        core::ptr::copy_nonoverlapping(self.alloc_base.as_ptr(), new, self.layout.size());
281
282        let tcb = new.add(self.tp_offset) as *mut Tcb<T>;
283        let dtv_ptr = new.add(self.dtv_offset) as *mut *mut u8;
284        let dtv = core::slice::from_raw_parts_mut(dtv_ptr, self.num_dtv_entries);
285
286        // Step 2a: "relocate" the pointers inside the DTV. First entry is the gen count, so skip
287        // that.
288        for entry in dtv.iter_mut().skip(1) {
289            let offset = (*entry).byte_offset_from(self.alloc_base.as_ptr());
290            *entry = new.byte_offset(offset);
291        }
292
293        // Step 2b: DTV[0] holds the TLS generation ID.
294        let dtv_0 = dtv_ptr as *mut u64;
295        *dtv_0 = self.gen;
296
297        // Step 3: Update the TCB data, including pointer to DTV and self.
298        {
299            let tcb = tcb.as_mut().unwrap();
300            tcb.dtv = dtv_ptr as *const usize;
301            tcb.self_ptr = tcb;
302            tcb.runtime_data = tcb_data;
303        }
304
305        tcb
306    }
307}
308
309impl SharedCompConfig {
310    pub fn new(
311        sctx: ObjID,
312        tls_template: *mut TlsTemplateInfo,
313        loader_config: CompartmentLoaderConfig,
314    ) -> Self {
315        Self {
316            sctx,
317            tls_template: AtomicPtr::new(tls_template),
318            root_library_id: None,
319            posted_signals: AtomicU64::new(0),
320            loader_config,
321        }
322    }
323
324    /// Set the current TLS template for a compartment. Only the monitor can call this.
325    pub fn set_tls_template(&self, ptr: *mut TlsTemplateInfo) {
326        self.tls_template.store(ptr, Ordering::SeqCst);
327    }
328
329    /// Get the current TLS template for the compartment.
330    pub fn get_tls_template(&self) -> *const TlsTemplateInfo {
331        self.tls_template.load(Ordering::SeqCst)
332    }
333
334    pub fn read_posted_signals(&self) -> u64 {
335        self.posted_signals.swap(0, Ordering::SeqCst)
336    }
337
338    pub fn peek_posted_signals(&self) -> u64 {
339        self.posted_signals.load(Ordering::SeqCst)
340    }
341
342    pub fn post_signal(&self, signal: u64) {
343        self.posted_signals.fetch_or(1 << signal, Ordering::SeqCst);
344        let _ = sys_thread_sync(
345            &mut [ThreadSync::new_wake(ThreadSyncWake::new(
346                ThreadSyncReference::Virtual(&self.posted_signals),
347                usize::MAX,
348            ))],
349            None,
350        );
351    }
352}
353
354/// Contains information about a library loaded into the address space.
355#[derive(Debug)]
356pub struct LibraryInfo<'a> {
357    /// The library's name
358    pub name: String,
359    /// The compartment of the library
360    pub compartment_id: ObjID,
361    /// The object ID that the library was loaded from
362    pub objid: ObjID,
363    /// The start address of range the library was loaded to
364    pub start: *const u8,
365    /// Length of range library was loaded to
366    pub len: usize,
367    /// The DlPhdrInfo for this library
368    pub dl_info: DlPhdrInfo,
369    /// The link_map structure for this library
370    pub link_map: LinkMap,
371    /// The slot of the library text.
372    pub slot: usize,
373    _pd: PhantomData<&'a ()>,
374    internal_name: Vec<u8>,
375}
376
377impl<'a> LibraryInfo<'a> {
378    pub fn from_raw(raw: LibraryInfoRaw) -> Self {
379        let name = lazy_sb::read_bytes_from_sb(raw.name_len);
380        let mut this = Self {
381            name: lazy_sb::read_string_from_sb(raw.name_len),
382            compartment_id: raw.compartment_id,
383            objid: raw.objid,
384            start: raw.start,
385            len: raw.len,
386            dl_info: raw.dl_info,
387            slot: raw.slot,
388            _pd: PhantomData,
389            internal_name: name,
390            link_map: raw.link_map,
391        };
392        this.dl_info.name = this.internal_name.as_ptr().cast();
393        this
394    }
395}
396
397/// A handle to a loaded library. On drop, the library may unload.
398#[derive(Debug)]
399pub struct LibraryHandle {
400    desc: Descriptor,
401}
402
403impl LibraryHandle {
404    /// Get the library info.
405    pub fn info(&self) -> LibraryInfo<'_> {
406        LibraryInfo::from_raw(monitor_rt_get_library_info(self.desc).unwrap())
407    }
408
409    /// Get the descriptor for this handle.
410    pub fn desc(&self) -> Descriptor {
411        self.desc
412    }
413
414    pub fn into_raw(self) -> Descriptor {
415        let desc = self.desc;
416        core::mem::forget(self);
417        desc
418    }
419}
420
421/// A builder-type for loading libraries.
422pub struct LibraryLoader<'a> {
423    id: Option<ObjID>,
424    name: String,
425    comp: Option<&'a CompartmentHandle>,
426}
427
428impl<'a> LibraryLoader<'a> {
429    /// Make a new LibraryLoader.
430    pub fn new(name: impl ToString, id: Option<ObjID>) -> Self {
431        Self {
432            name: name.to_string(),
433            id,
434            comp: None,
435        }
436    }
437
438    /// Load the library in the given compartment.
439    pub fn in_compartment(&'a mut self, comp: &'a CompartmentHandle) -> &'a mut Self {
440        self.comp = Some(comp);
441        self
442    }
443
444    /// Load the library.
445    pub fn load(&self) -> Result<LibraryHandle, TwzError> {
446        let len = lazy_sb::write_bytes_to_sb(self.name.as_bytes());
447        let (desc, ctor_len): (Descriptor, usize) =
448            monitor_rt_load_library(self.comp.map(|comp| comp.desc).flatten(), self.id, len)?;
449
450        // Call ctors.
451        let ctors = lazy_sb::read_bytes_from_sb(ctor_len);
452        let ctor_slice = unsafe {
453            core::slice::from_raw_parts(
454                ctors.as_ptr().cast::<ctor_set>(),
455                ctor_len / core::mem::size_of::<ctor_set>(),
456            )
457        };
458        for ctor in ctor_slice {
459            unsafe {
460                let init_array = core::slice::from_raw_parts(ctor.init_array, ctor.init_array_len);
461                for func in init_array {
462                    if let Some(f) = func {
463                        f();
464                    }
465                }
466                if let Some(f) = ctor.legacy_init {
467                    f();
468                }
469            }
470        }
471
472        Ok(LibraryHandle { desc })
473    }
474}
475
476/// A compartment handle. On drop, the compartment may be unloaded.
477pub struct CompartmentHandle {
478    desc: Option<Descriptor>,
479}
480
481impl CompartmentHandle {
482    /// Get the compartment info.
483    pub fn info(&self) -> CompartmentInfo<'_> {
484        CompartmentInfo::from_raw(monitor_rt_get_compartment_info(self.desc).unwrap())
485    }
486
487    /// Get the descriptor for this handle, or None if the handle refers to the current compartment.
488    pub fn desc(&self) -> Option<Descriptor> {
489        self.desc
490    }
491
492    pub unsafe fn dynamic_gate<A: Tuple + Crossing + Copy, R: Crossing + Copy>(
493        &self,
494        name: &str,
495    ) -> Result<DynamicSecGate<'_, A, R>, TwzError> {
496        let name_len = lazy_sb::write_bytes_to_sb(name.as_bytes());
497        let address = monitor_rt_compartment_dynamic_gate(self.desc, name_len)?;
498        Ok(DynamicSecGate::new(address))
499    }
500
501    pub fn signal(&self, sig: u64) -> Result<(), TwzError> {
502        let target = self.info().id;
503        monitor_rt_post_signal(Some(target), sig, PostSignalFlags::empty())
504    }
505}
506
507/// A builder-type for loading compartments.
508pub struct CompartmentLoader {
509    name: String,
510    root_object: ObjID,
511    args: Vec<String>,
512    env: Option<Vec<String>>,
513    flags: NewCompartmentFlags,
514    config: CompartmentLoaderConfig,
515    _fd_spec: Vec<binding_info>,
516}
517
518fn load_fd_specs_from_runtime() -> Vec<binding_info> {
519    let mut v = vec![binding_info::default(); 8];
520    loop {
521        let len =
522            unsafe { twizzler_rt_abi::bindings::twz_rt_fd_read_binds(v.as_mut_ptr(), v.len()) };
523        if len == v.len() {
524            v.extend_from_slice(&[binding_info::default(); 8]);
525        } else {
526            v.truncate(len);
527            break;
528        }
529    }
530    v
531}
532
533impl CompartmentLoader {
534    /// Make a new compartment loader.
535    pub fn new(
536        compname: impl ToString,
537        exename: impl ToString,
538        root_object: ObjID,
539        flags: NewCompartmentFlags,
540    ) -> Self {
541        let mut config = CompartmentLoaderConfig::default();
542        let fd_spec = load_fd_specs_from_runtime();
543        config.with_fd_spec(&fd_spec);
544        Self {
545            name: format!("{}::{}", compname.to_string(), exename.to_string()),
546            flags,
547            env: None,
548            args: vec![],
549            config,
550            root_object,
551            _fd_spec: fd_spec,
552        }
553    }
554
555    pub fn with_controller(&mut self, con: ControllerOption) -> &mut Self {
556        self.config.controller = con;
557        self
558    }
559
560    pub fn with_fd_specs<'a>(&'a mut self, spec: &'a [binding_info]) -> &'a mut Self {
561        self.config.with_fd_spec(spec);
562        self
563    }
564
565    /// Append args to this compartment.
566    pub fn args<S: ToString>(&mut self, args: impl IntoIterator<Item = S>) -> &mut Self {
567        for arg in args.into_iter() {
568            self.args.push(arg.to_string())
569        }
570        self
571    }
572
573    /// Set the environment for the compartment
574    pub fn env<S: ToString>(&mut self, env: impl IntoIterator<Item = S>) -> &mut Self {
575        self.env = Some(env.into_iter().map(|s| s.to_string()).collect());
576        self
577    }
578
579    /// Load the compartment.
580    pub fn load(&self) -> Result<CompartmentHandle, TwzError> {
581        fn get_current_env() -> Vec<String> {
582            std::env::vars()
583                .map(|(var, val)| format!("{}={}", var, val))
584                .collect()
585        }
586        let name_len = self.name.as_bytes().len();
587        let args_len = self
588            .args
589            .iter()
590            .fold(0, |acc, arg| acc + arg.as_bytes().len() + 1);
591        let env = self.env.clone().unwrap_or_else(|| get_current_env());
592        let envs_len = env
593            .iter()
594            .fold(0, |acc, arg| acc + arg.as_bytes().len() + 1);
595        let mut bytes = self.name.as_bytes().to_vec();
596        for arg in &self.args {
597            bytes.extend_from_slice(arg.as_bytes());
598            bytes.push(0);
599        }
600        for env in env {
601            bytes.extend_from_slice(env.as_bytes());
602            bytes.push(0);
603        }
604        let len = lazy_sb::write_bytes_to_sb(&bytes);
605        if len < envs_len + args_len + name_len {
606            return Err(ArgumentError::InvalidArgument.into());
607        }
608        let desc = monitor_rt_load_compartment(
609            self.root_object,
610            name_len as u64,
611            args_len as u64,
612            envs_len as u64,
613            self.flags.bits(),
614            (&self.config as *const _) as usize as u64,
615        )?;
616        Ok(CompartmentHandle { desc: Some(desc) })
617    }
618}
619
620impl Handle for CompartmentHandle {
621    type OpenError = TwzError;
622
623    type OpenInfo = ObjID;
624
625    fn open(info: Self::OpenInfo) -> Result<Self, Self::OpenError>
626    where
627        Self: Sized,
628    {
629        let desc = monitor_rt_get_compartment_handle(info)?;
630        Ok(CompartmentHandle { desc: Some(desc) })
631    }
632
633    fn release(&mut self) {
634        if let Some(desc) = self.desc {
635            let _ = monitor_rt_drop_compartment_handle(desc);
636        }
637    }
638}
639
640impl Drop for CompartmentHandle {
641    fn drop(&mut self) {
642        self.release();
643    }
644}
645
646impl Handle for LibraryHandle {
647    type OpenError = TwzError;
648
649    type OpenInfo = (Option<Descriptor>, usize);
650
651    fn open(info: Self::OpenInfo) -> Result<Self, Self::OpenError>
652    where
653        Self: Sized,
654    {
655        let desc = monitor_rt_get_library_handle(info.0, info.1)?;
656        Ok(LibraryHandle { desc })
657    }
658
659    fn release(&mut self) {
660        let _ = monitor_rt_drop_library_handle(self.desc);
661    }
662}
663
664impl Drop for LibraryHandle {
665    fn drop(&mut self) {
666        self.release()
667    }
668}
669
670/// Information about a compartment.
671#[derive(Clone, Debug)]
672pub struct CompartmentInfo<'a> {
673    /// The name of the compartment.
674    pub name: String,
675    /// The instance ID.
676    pub id: ObjID,
677    /// The security context.
678    pub sctx: ObjID,
679    /// The compartment flags and status.
680    pub flags: CompartmentFlags,
681    /// Number of libraries
682    pub nr_libs: usize,
683    /// Exit code of the main thread (valid when EXITED flag is set).
684    pub exit_code: u64,
685    _pd: PhantomData<&'a ()>,
686}
687
688impl<'a> CompartmentInfo<'a> {
689    fn from_raw(raw: CompartmentInfoRaw) -> Self {
690        Self {
691            name: lazy_sb::read_string_from_sb(raw.name_len),
692            id: raw.id,
693            sctx: raw.sctx,
694            flags: CompartmentFlags::from_bits_truncate(raw.flags),
695            nr_libs: raw.nr_libs,
696            exit_code: raw.exit_code,
697            _pd: PhantomData,
698        }
699    }
700}
701
702impl CompartmentHandle {
703    /// Get a handle to the current compartment.
704    pub fn current() -> Self {
705        Self { desc: None }
706    }
707
708    /// Lookup a compartment by name.
709    pub fn lookup(name: impl AsRef<str>) -> Result<Self, TwzError> {
710        let name_len = lazy_sb::write_bytes_to_sb(name.as_ref().as_bytes());
711        Ok(Self {
712            desc: Some(monitor_rt_lookup_compartment(name_len)?),
713        })
714    }
715
716    /// Lookup a compartment by ID.
717    pub fn lookup_id(name: ObjID) -> Result<Self, TwzError> {
718        Ok(Self {
719            desc: Some(monitor_rt_lookup_compartment_id(name)?),
720        })
721    }
722
723    /// Get an iterator over this compartment's dependencies.
724    pub fn deps(&self) -> CompartmentDepsIter<'_> {
725        CompartmentDepsIter::new(self)
726    }
727
728    /// Get the root library for this compartment.
729    pub fn root(&self) -> LibraryHandle {
730        self.libs().next().unwrap()
731    }
732
733    /// Get an iterator over the libraries for this compartment.
734    pub fn libs(&self) -> LibraryIter<'_> {
735        LibraryIter::new(self)
736    }
737
738    /// Get an iterator over the libraries for this compartment.
739    pub fn threads(&self) -> CompartmentThreadsIter<'_> {
740        CompartmentThreadsIter::new(self)
741    }
742
743    pub fn wait(&self, flags: CompartmentFlags) -> CompartmentFlags {
744        CompartmentFlags::from_bits_truncate(
745            monitor_rt_compartment_wait(self.desc(), flags.bits()).unwrap(),
746        )
747    }
748}
749
750/// An iterator over libraries in a compartment.
751pub struct LibraryIter<'a> {
752    n: usize,
753    comp: &'a CompartmentHandle,
754}
755
756impl<'a> LibraryIter<'a> {
757    fn new(comp: &'a CompartmentHandle) -> Self {
758        Self { n: 0, comp }
759    }
760}
761
762impl<'a> Iterator for LibraryIter<'a> {
763    type Item = LibraryHandle;
764
765    fn next(&mut self) -> Option<Self::Item> {
766        let handle = LibraryHandle::open((self.comp.desc, self.n)).ok();
767        if handle.is_some() {
768            self.n += 1;
769        }
770        handle
771    }
772}
773
774/// An iterator over a compartment's dependencies.
775pub struct CompartmentDepsIter<'a> {
776    n: usize,
777    comp: &'a CompartmentHandle,
778}
779
780impl<'a> CompartmentDepsIter<'a> {
781    fn new(comp: &'a CompartmentHandle) -> Self {
782        Self { n: 0, comp }
783    }
784}
785
786impl<'a> Iterator for CompartmentDepsIter<'a> {
787    type Item = CompartmentHandle;
788
789    fn next(&mut self) -> Option<Self::Item> {
790        let desc = monitor_rt_get_compartment_deps(self.comp.desc, self.n).ok()?;
791        self.n += 1;
792        Some(CompartmentHandle { desc: Some(desc) })
793    }
794
795    fn nth(&mut self, n: usize) -> Option<Self::Item> {
796        self.n += n;
797        self.next()
798    }
799}
800
801/// An iterator over a compartment's threads.
802pub struct CompartmentThreadsIter<'a> {
803    n: usize,
804    comp: &'a CompartmentHandle,
805}
806
807impl<'a> CompartmentThreadsIter<'a> {
808    fn new(comp: &'a CompartmentHandle) -> Self {
809        Self { n: 0, comp }
810    }
811}
812
813impl<'a> Iterator for CompartmentThreadsIter<'a> {
814    type Item = ThreadInfo;
815
816    fn next(&mut self) -> Option<Self::Item> {
817        let info = monitor_rt_get_compartment_thread(self.comp.desc, self.n).ok()?;
818        self.n += 1;
819        Some(info)
820    }
821
822    fn nth(&mut self, n: usize) -> Option<Self::Item> {
823        self.n += n;
824        self.next()
825    }
826}
827
828bitflags::bitflags! {
829    /// Compartment state flags.
830    #[derive(Clone, Debug, Copy, PartialEq, PartialOrd, Ord, Eq, Hash)]
831    pub struct CompartmentFlags : u64 {
832        /// Compartment is ready (loaded, reloacated, runtime started and ctors run).
833        const READY = 0x1;
834        /// Compartment is a binary, not a library.
835        const IS_BINARY = 0x2;
836        /// Compartment runtime thread may exit.
837        const THREAD_CAN_EXIT = 0x4;
838        /// Compartment thread has been started once.
839        const STARTED = 0x8;
840        /// Compartment destructors have run.
841        const DESTRUCTED = 0x10;
842        /// Compartment thread has exited.
843        const EXITED = 0x20;
844    }
845}
846
847/// Contains raw mapping addresses, for use when translating to object handles for the runtime.
848#[derive(Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Debug)]
849pub struct MappedObjectAddrs {
850    pub slot: usize,
851    pub start: usize,
852    pub meta: usize,
853}
854
855impl MappedObjectAddrs {
856    pub fn new(slot: usize) -> Self {
857        Self {
858            start: slot * MAX_SIZE,
859            meta: (slot + 1) * MAX_SIZE - NULLPAGE_SIZE,
860            slot,
861        }
862    }
863}
864
865/// Get stats from the monitor
866pub fn stats() -> Option<MonitorStats> {
867    monitor_rt_stats().ok()
868}
869
870mod lazy_sb {
871    //! A per-thread per-compartment simple buffer used for transferring strings between
872    //! compartments and the monitor. This is necessary because the monitor runs at too low of a
873    //! level for us to use nice shared memory techniques. This is simpler and more secure.
874    use std::cell::{OnceCell, RefCell};
875
876    use secgate::util::SimpleBuffer;
877    use twizzler_rt_abi::object::MapFlags;
878
879    struct LazyThreadSimpleBuffer {
880        sb: OnceCell<SimpleBuffer>,
881    }
882
883    impl LazyThreadSimpleBuffer {
884        const fn new() -> Self {
885            Self {
886                sb: OnceCell::new(),
887            }
888        }
889
890        fn init() -> SimpleBuffer {
891            let id = super::monitor_rt_get_thread_simple_buffer()
892                .expect("failed to get per-thread monitor simple buffer");
893            let oh =
894                twizzler_rt_abi::object::twz_rt_map_object(id, MapFlags::READ | MapFlags::WRITE)
895                    .unwrap();
896            SimpleBuffer::new(oh)
897        }
898
899        fn read(&mut self, buf: &mut [u8]) -> usize {
900            let sb = self.sb.get_or_init(|| Self::init());
901            sb.read(buf)
902        }
903
904        fn write(&mut self, buf: &[u8]) -> usize {
905            if self.sb.get().is_none() {
906                // Unwrap-Ok: we know it's empty.
907                self.sb.set(Self::init()).unwrap();
908            }
909            let sb = self.sb.get_mut().unwrap();
910            sb.write(buf)
911        }
912    }
913
914    #[thread_local]
915    static LAZY_SB: RefCell<LazyThreadSimpleBuffer> = RefCell::new(LazyThreadSimpleBuffer::new());
916
917    pub(super) fn read_string_from_sb(len: usize) -> String {
918        let mut buf = vec![0u8; len];
919        let len = LAZY_SB.borrow_mut().read(&mut buf);
920        String::from_utf8_lossy(&buf[0..len]).to_string()
921    }
922
923    pub(super) fn read_bytes_from_sb(len: usize) -> Vec<u8> {
924        let mut buf = vec![0u8; len];
925        let len = LAZY_SB.borrow_mut().read(&mut buf);
926        buf.truncate(len);
927        buf
928    }
929
930    pub(super) fn write_bytes_to_sb(buf: &[u8]) -> usize {
931        LAZY_SB.borrow_mut().write(buf)
932    }
933}
934
935pub const THREAD_STARTED: u32 = 1;
936#[repr(C)]
937pub struct RuntimeThreadControl {
938    pub id: UnsafeCell<u32>,
939    pub did_exit: UnsafeCell<u32>,
940    // Need to keep a lock for the ID, though we don't expect to use it much.
941    pub internal_lock: AtomicU32,
942    pub flags: AtomicU32,
943    pub stack_canary: u64,
944    pub libc_data: [u64; 16],
945}
946
947impl Default for RuntimeThreadControl {
948    fn default() -> Self {
949        Self::new(0)
950    }
951}
952
953impl RuntimeThreadControl {
954    pub const fn new(id: u32) -> Self {
955        Self {
956            internal_lock: AtomicU32::new(0),
957            flags: AtomicU32::new(0),
958            id: UnsafeCell::new(id),
959            did_exit: UnsafeCell::new(0),
960            stack_canary: 0,
961            libc_data: [0; 16],
962        }
963    }
964
965    fn write_lock(&self) {
966        loop {
967            let old = self.internal_lock.fetch_or(1, Ordering::Acquire);
968            if old == 0 {
969                break;
970            }
971        }
972    }
973
974    fn write_unlock(&self) {
975        self.internal_lock.fetch_and(!1, Ordering::Release);
976    }
977
978    fn read_lock(&self) {
979        loop {
980            let old = self.internal_lock.fetch_add(2, Ordering::Acquire);
981            // If this happens, something has gone very wrong.
982            if old > i32::MAX as u32 {
983                twizzler_rt_abi::core::twz_rt_abort();
984            }
985            if old & 1 == 0 {
986                break;
987            }
988        }
989    }
990
991    fn read_unlock(&self) {
992        self.internal_lock.fetch_sub(2, Ordering::Release);
993    }
994
995    pub fn set_id(&self, id: u32) {
996        self.write_lock();
997        unsafe {
998            *self.id.get().as_mut().unwrap() = id;
999        }
1000        self.write_unlock();
1001    }
1002
1003    pub fn id(&self) -> u32 {
1004        self.read_lock();
1005        let id = unsafe { *self.id.get().as_ref().unwrap() };
1006        self.read_unlock();
1007        id
1008    }
1009}
1010
1011pub fn post_signal(
1012    target: Option<ObjID>,
1013    signal: u64,
1014    flags: PostSignalFlags,
1015) -> Result<(), TwzError> {
1016    monitor_rt_post_signal(target, signal, flags)
1017}
1018
1019#[derive(Copy, Clone, Debug)]
1020#[repr(C)]
1021pub struct MonitorStats {
1022    pub space: SpaceStats,
1023    pub thread_mgr: ThreadMgrStats,
1024    pub comp_mgr: CompartmentMgrStats,
1025    pub handles: HandleStats,
1026    pub dynlink: DynlinkStats,
1027}
1028
1029#[derive(Copy, Clone, Debug)]
1030#[repr(C)]
1031pub struct SpaceStats {
1032    pub mapped: usize,
1033}
1034
1035#[derive(Copy, Clone, Debug)]
1036#[repr(C)]
1037pub struct ThreadMgrStats {
1038    pub nr_threads: usize,
1039}
1040
1041#[derive(Copy, Clone, Debug)]
1042#[repr(C)]
1043pub struct CompartmentMgrStats {
1044    pub nr_compartments: usize,
1045}
1046
1047#[derive(Copy, Clone, Debug)]
1048#[repr(C)]
1049pub struct HandleStats {
1050    pub nr_comp_handles: usize,
1051    pub nr_lib_handles: usize,
1052}
1053
1054#[derive(Copy, Clone, Debug)]
1055#[repr(C)]
1056pub struct DynlinkStats {
1057    pub nr_libs: usize,
1058    pub nr_comps: usize,
1059}
1060
1061#[derive(Debug, Copy, Clone)]
1062#[repr(C)]
1063#[allow(dead_code)]
1064pub enum MonitorCompControlCmd {
1065    RuntimeReady,
1066    RuntimePostMain,
1067}
1068
1069#[repr(C)]
1070#[derive(Clone, Copy, Debug)]
1071pub struct LibraryInfoRaw {
1072    pub name_len: usize,
1073    pub compartment_id: ObjID,
1074    pub objid: ObjID,
1075    pub slot: usize,
1076    pub start: *const u8,
1077    pub len: usize,
1078    pub dl_info: DlPhdrInfo,
1079    pub link_map: LinkMap,
1080    pub desc: Descriptor,
1081}
1082
1083unsafe impl Crossing for LibraryInfoRaw {}
1084
1085#[repr(C)]
1086#[derive(Clone, Copy, Debug)]
1087pub struct CompartmentInfoRaw {
1088    pub name_len: usize,
1089    pub id: ObjID,
1090    pub sctx: ObjID,
1091    pub flags: u64,
1092    pub nr_libs: usize,
1093    /// Exit code of the compartment's main thread (valid when EXITED flag is set).
1094    pub exit_code: u64,
1095}
1096
1097#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash, Copy)]
1098#[repr(C)]
1099pub struct ThreadInfo {
1100    pub repr_id: ObjID,
1101}
1102
1103/// Reserved instance ID for the security monitor.
1104pub const MONITOR_INSTANCE_ID: ObjID = ObjID::new(0);
1105
1106bitflags::bitflags! {
1107    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1108    pub struct PostSignalFlags : u32 {
1109        const GROUP = 1;
1110        const CONTROLLER = 2;
1111    }
1112}
1113
1114#[repr(C)]
1115#[derive(Default, Copy, Clone, Debug)]
1116#[allow(dead_code)]
1117pub enum ControllerOption {
1118    #[default]
1119    Inherit,
1120    NoController,
1121    Object(ObjID),
1122}
1123
1124#[repr(C)]
1125#[derive(Default, Copy, Clone, Debug)]
1126#[allow(dead_code)]
1127pub struct CompartmentLoaderConfig {
1128    pub controller: ControllerOption,
1129    pub fd_spec: *const binding_info,
1130    pub fd_spec_len: usize,
1131}
1132
1133impl CompartmentLoaderConfig {
1134    #[allow(dead_code)]
1135    pub fn with_controller(&mut self, controller: ControllerOption) -> &mut Self {
1136        self.controller = controller;
1137        self
1138    }
1139
1140    #[allow(dead_code)]
1141    pub fn with_fd_spec<'a>(&'a mut self, spec: &'a [binding_info]) -> &'a mut Self {
1142        self.fd_spec = spec.as_ptr();
1143        self.fd_spec_len = spec.len();
1144        self
1145    }
1146
1147    #[allow(dead_code)]
1148    pub fn fd_spec(&self) -> &[binding_info] {
1149        unsafe { core::slice::from_raw_parts(self.fd_spec, self.fd_spec_len) }
1150    }
1151}
1152
1153pub fn libname_map(name: &str, id: ObjID) -> Result<(), TwzError> {
1154    let namelen = lazy_sb::write_bytes_to_sb(name.as_bytes());
1155    monitor_rt_libname_map(namelen, id)
1156}
1157
1158pub fn libname_unmap(name: Option<&str>, id: Option<ObjID>) -> Result<(), TwzError> {
1159    let namelen = name.map(|name| lazy_sb::write_bytes_to_sb(name.as_bytes()));
1160    monitor_rt_libname_unmap(namelen, id)
1161}
1162
1163/// Look up a library by name in the caller's compartment. Returns a library handle descriptor.
1164pub fn lookup_library_by_name(name: &str) -> Result<secgate::util::Descriptor, TwzError> {
1165    LibraryHandle::open((None, lazy_sb::write_bytes_to_sb(name.as_bytes())))
1166        .map(|handle| handle.desc())
1167}
1168
1169/// Look up a symbol by name. If `lib_desc` is `None`, searches across all libraries in the
1170/// caller's compartment (RTLD_DEFAULT semantics). Returns the relocated symbol address.
1171pub fn lookup_symbol_by_name(
1172    lib_desc: Option<secgate::util::Descriptor>,
1173    name: &str,
1174) -> Result<usize, TwzError> {
1175    let name_len = lazy_sb::write_bytes_to_sb(name.as_bytes());
1176    monitor_rt_lookup_symbol(lib_desc, name_len)
1177}