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