wasmtime/runtime/vm/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 crate::prelude::*;
17use core::any::Any;
18use wasmtime_slab::{Id, Slab};
19
20/// Side table for each `externref`'s host data value.
21#[derive(Default)]
22pub struct ExternRefHostDataTable {
23    slab: Slab<Box<dyn Any + Send + Sync>>,
24}
25
26/// ID into the `externref` host data table.
27#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
28#[repr(transparent)]
29pub struct ExternRefHostDataId(Id);
30
31fn deref_box<T: ?Sized>(b: &Box<T>) -> &T {
32    &**b
33}
34
35fn deref_box_mut<T: ?Sized>(b: &mut Box<T>) -> &mut T {
36    &mut **b
37}
38
39impl ExternRefHostDataTable {
40    /// Allocate a new `externref` host data value.
41    pub fn alloc(&mut self, value: Box<dyn Any + Send + Sync>) -> ExternRefHostDataId {
42        let id = self.slab.alloc(value);
43        let id = ExternRefHostDataId(id);
44        log::trace!("allocated new externref host data: {id:?}");
45        id
46    }
47
48    /// Deallocate an `externref` host data value.
49    pub fn dealloc(&mut self, id: ExternRefHostDataId) -> Box<dyn Any + Send + Sync> {
50        log::trace!("deallocated externref host data: {id:?}");
51        self.slab.dealloc(id.0)
52    }
53
54    /// Get a shared borrow of the host data associated with the given ID.
55    pub fn get(&self, id: ExternRefHostDataId) -> &(dyn Any + Send + Sync) {
56        let data: &Box<dyn Any + Send + Sync> = self.slab.get(id.0).unwrap();
57        deref_box(data)
58    }
59
60    /// Get a mutable borrow of the host data associated with the given ID.
61    pub fn get_mut(&mut self, id: ExternRefHostDataId) -> &mut (dyn Any + Send + Sync) {
62        let data: &mut Box<dyn Any + Send + Sync> = self.slab.get_mut(id.0).unwrap();
63        deref_box_mut(data)
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn correct_dyn_object() {
73        let mut table = ExternRefHostDataTable::default();
74
75        let x = 42_u32;
76        let id = table.alloc(Box::new(x));
77        assert!(table.get(id).is::<u32>());
78        assert_eq!(*table.get(id).downcast_ref::<u32>().unwrap(), 42);
79        assert!(table.get_mut(id).is::<u32>());
80        assert_eq!(*table.get_mut(id).downcast_ref::<u32>().unwrap(), 42);
81    }
82}