1 //! Implementation of the side table for `funcref`s in the GC heap. 2 //! 3 //! The actual `VMFuncRef`s are kept in a side table, rather than inside the GC 4 //! heap, for the same reasons that an `externref`'s host data is kept in a side 5 //! table. We cannot trust any data coming from the GC heap, but `VMFuncRef`s 6 //! contain raw pointers, so if we stored `VMFuncRef`s inside the GC heap, we 7 //! wouldn't be able to use the raw pointers from any `VMFuncRef` we got out of 8 //! the heap. And that means we wouldn't be able to, for example, call a 9 //! `funcref` we got from inside the GC heap. 10 11 use crate::{ 12 hash_map::HashMap, 13 type_registry::TypeRegistry, 14 vm::{SendSyncPtr, VMFuncRef}, 15 }; 16 use wasmtime_environ::VMSharedTypeIndex; 17 use wasmtime_slab::{Id, Slab}; 18 19 /// An identifier into the `FuncRefTable`. 20 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 21 #[repr(transparent)] 22 pub struct FuncRefTableId(Id); 23 24 impl FuncRefTableId { 25 /// Convert this `FuncRefTableId` into its raw `u32` ID. 26 pub fn into_raw(self) -> u32 { 27 self.0.into_raw() 28 } 29 30 /// Create a `FuncRefTableId` from a raw `u32` ID. 31 pub fn from_raw(raw: u32) -> Self { 32 Self(Id::from_raw(raw)) 33 } 34 } 35 36 /// Side table mapping `FuncRefTableId`s that can be stored in the GC heap to 37 /// raw `VMFuncRef`s. 38 #[derive(Default)] 39 pub struct FuncRefTable { 40 interned: HashMap<Option<SendSyncPtr<VMFuncRef>>, FuncRefTableId>, 41 slab: Slab<Option<SendSyncPtr<VMFuncRef>>>, 42 } 43 44 impl FuncRefTable { 45 /// Intern a `VMFuncRef` in the side table, returning an ID that can be 46 /// stored in the GC heap. 47 /// 48 /// # Safety 49 /// 50 /// The given `func_ref` must point to a valid `VMFuncRef` and must remain 51 /// valid for the duration of this table's lifetime. 52 pub unsafe fn intern(&mut self, func_ref: Option<SendSyncPtr<VMFuncRef>>) -> FuncRefTableId { 53 *self 54 .interned 55 .entry(func_ref) 56 .or_insert_with(|| FuncRefTableId(self.slab.alloc(func_ref))) 57 } 58 59 /// Get the `VMFuncRef` associated with the given ID. 60 /// 61 /// Checks that the `VMFuncRef` is a subtype of the expected type. 62 pub fn get_typed( 63 &self, 64 types: &TypeRegistry, 65 id: FuncRefTableId, 66 expected_ty: VMSharedTypeIndex, 67 ) -> Option<SendSyncPtr<VMFuncRef>> { 68 let f = self.slab.get(id.0).copied().expect("bad FuncRefTableId"); 69 70 if let Some(f) = f { 71 // The safety contract for `intern` ensures that deref'ing `f` is safe. 72 let actual_ty = unsafe { f.as_ref().type_index }; 73 74 // Ensure that the funcref actually is a subtype of the expected 75 // type. This protects against GC heap corruption being leveraged in 76 // attacks: if the attacker has a write gadget inside the GC heap, they 77 // can overwrite a funcref ID to point to a different funcref, but this 78 // assertion ensures that any calls to that wrong funcref at least 79 // remain well-typed, which reduces the attack surface and maintains 80 // memory safety. 81 assert!(types.is_subtype(actual_ty, expected_ty)); 82 } 83 84 f 85 } 86 87 /// Get the `VMFuncRef` associated with the given ID, without checking the 88 /// type. 89 /// 90 /// Prefer `get_typed`. This method is only suitable for getting a 91 /// `VMFuncRef` as an untyped `funcref` function reference, and never as a 92 /// typed `(ref $some_func_type)` function reference. 93 pub fn get_untyped(&self, id: FuncRefTableId) -> Option<SendSyncPtr<VMFuncRef>> { 94 self.slab.get(id.0).copied().expect("bad FuncRefTableId") 95 } 96 } 97