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 16 use crate::prelude::*; 17 use core::any::Any; 18 use wasmtime_slab::{Id, Slab}; 19 20 /// Side table for each `externref`'s host data value. 21 #[derive(Default)] 22 pub 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)] 29 pub struct ExternRefHostDataId(Id); 30 31 fn deref_box<T: ?Sized>(b: &Box<T>) -> &T { 32 &**b 33 } 34 35 fn deref_box_mut<T: ?Sized>(b: &mut Box<T>) -> &mut T { 36 &mut **b 37 } 38 39 impl 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)] 68 mod 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 } 83