wasmtime_runtime/gc/
host_data.rs

1//! Implementation of the side table for `externref` host data.
2//!
3//! The actual host data is kept in a side table, rather than inside the GC
4//! heap, because we do not trust any data coming from the GC heap. If we
5//! constructed `&dyn Any`s from GC heap data and called any function loaded
6//! from the `dyn Any`'s vtable, then any bug in our collector could lead to
7//! corrupted vtables, which could lead to security vulnerabilities and sandbox
8//! escapes.
9//!
10//! Much better to store host data IDs inside the GC heap, and then do checked
11//! accesses into the host data table from those untrusted IDs. At worst, we can
12//! return the wrong (but still valid) host data object or panic. This is way
13//! less catastrophic than doing an indirect call to an attacker-controlled
14//! function pointer.
15
16use std::any::Any;
17use wasmtime_slab::{Id, Slab};
18
19/// Side table for each `externref`'s host data value.
20#[derive(Default)]
21pub struct ExternRefHostDataTable {
22    slab: Slab<Box<dyn Any + Send + Sync>>,
23}
24
25/// ID into the `externref` host data table.
26#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
27#[repr(transparent)]
28pub struct ExternRefHostDataId(Id);
29
30fn deref_box<T: ?Sized>(b: &Box<T>) -> &T {
31    &**b
32}
33
34fn deref_box_mut<T: ?Sized>(b: &mut Box<T>) -> &mut T {
35    &mut **b
36}
37
38impl ExternRefHostDataTable {
39    /// Allocate a new `externref` host data value.
40    pub fn alloc(&mut self, value: Box<dyn Any + Send + Sync>) -> ExternRefHostDataId {
41        let id = self.slab.alloc(value);
42        let id = ExternRefHostDataId(id);
43        log::trace!("allocated new externref host data: {id:?}");
44        id
45    }
46
47    /// Deallocate an `externref` host data value.
48    pub fn dealloc(&mut self, id: ExternRefHostDataId) -> Box<dyn Any + Send + Sync> {
49        log::trace!("deallocated externref host data: {id:?}");
50        self.slab.dealloc(id.0)
51    }
52
53    /// Get a shared borrow of the host data associated with the given ID.
54    pub fn get(&self, id: ExternRefHostDataId) -> &(dyn Any + Send + Sync) {
55        let data: &Box<dyn Any + Send + Sync> = self.slab.get(id.0).unwrap();
56        deref_box(data)
57    }
58
59    /// Get a mutable borrow of the host data associated with the given ID.
60    pub fn get_mut(&mut self, id: ExternRefHostDataId) -> &mut (dyn Any + Send + Sync) {
61        let data: &mut Box<dyn Any + Send + Sync> = self.slab.get_mut(id.0).unwrap();
62        deref_box_mut(data)
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn correct_dyn_object() {
72        let mut table = ExternRefHostDataTable::default();
73
74        let x = 42_u32;
75        let id = table.alloc(Box::new(x));
76        assert!(table.get(id).is::<u32>());
77        assert_eq!(*table.get(id).downcast_ref::<u32>().unwrap(), 42);
78        assert!(table.get_mut(id).is::<u32>());
79        assert_eq!(*table.get_mut(id).downcast_ref::<u32>().unwrap(), 42);
80    }
81}