1 use crate::MemoryType; 2 use crate::memory::{LinearMemory, MemoryCreator}; 3 use crate::prelude::*; 4 use crate::runtime::vm::mpk::ProtectionKey; 5 use crate::runtime::vm::{ 6 CompiledModuleId, InstanceAllocationRequest, InstanceAllocatorImpl, Memory, 7 MemoryAllocationIndex, MemoryBase, ModuleRuntimeInfo, OnDemandInstanceAllocator, 8 RuntimeLinearMemory, RuntimeMemoryCreator, SharedMemory, Table, TableAllocationIndex, 9 }; 10 use crate::store::{AllocateInstanceKind, InstanceId, StoreOpaque}; 11 use alloc::sync::Arc; 12 use wasmtime_environ::{ 13 DefinedMemoryIndex, DefinedTableIndex, EntityIndex, HostPtr, Module, Tunables, VMOffsets, 14 }; 15 16 #[cfg(feature = "component-model")] 17 use wasmtime_environ::{ 18 StaticModuleIndex, 19 component::{Component, VMComponentOffsets}, 20 }; 21 22 /// Create a "frankenstein" instance with a single memory. 23 /// 24 /// This separate instance is necessary because Wasm objects in Wasmtime must be 25 /// attached to instances (versus the store, e.g.) and some objects exist 26 /// outside: a host-provided memory import, shared memory. 27 pub fn create_memory( 28 store: &mut StoreOpaque, 29 memory_ty: &MemoryType, 30 preallocation: Option<&SharedMemory>, 31 ) -> Result<InstanceId> { 32 let mut module = Module::new(); 33 34 // Create a memory, though it will never be used for constructing a memory 35 // with an allocator: instead the memories are either preallocated (i.e., 36 // shared memory) or allocated manually below. 37 let memory_id = module.memories.push(*memory_ty.wasmtime_memory()); 38 39 // Since we have only associated a single memory with the "frankenstein" 40 // instance, it will be exported at index 0. 41 debug_assert_eq!(memory_id.as_u32(), 0); 42 module 43 .exports 44 .insert(String::new(), EntityIndex::Memory(memory_id)); 45 46 // We create an instance in the on-demand allocator when creating handles 47 // associated with external objects. The configured instance allocator 48 // should only be used when creating module instances as we don't want host 49 // objects to count towards instance limits. 50 let allocator = SingleMemoryInstance { 51 preallocation, 52 ondemand: OnDemandInstanceAllocator::default(), 53 }; 54 unsafe { 55 store.allocate_instance( 56 AllocateInstanceKind::Dummy { 57 allocator: &allocator, 58 }, 59 &ModuleRuntimeInfo::bare(Arc::new(module)), 60 Default::default(), 61 ) 62 } 63 } 64 65 struct LinearMemoryProxy { 66 mem: Box<dyn LinearMemory>, 67 } 68 69 impl RuntimeLinearMemory for LinearMemoryProxy { 70 fn byte_size(&self) -> usize { 71 self.mem.byte_size() 72 } 73 74 fn byte_capacity(&self) -> usize { 75 self.mem.byte_capacity() 76 } 77 78 fn grow_to(&mut self, new_size: usize) -> Result<()> { 79 self.mem.grow_to(new_size) 80 } 81 82 fn base(&self) -> MemoryBase { 83 MemoryBase::new_raw(self.mem.as_ptr()) 84 } 85 } 86 87 #[derive(Clone)] 88 pub(crate) struct MemoryCreatorProxy(pub Arc<dyn MemoryCreator>); 89 90 impl RuntimeMemoryCreator for MemoryCreatorProxy { 91 fn new_memory( 92 &self, 93 ty: &wasmtime_environ::Memory, 94 tunables: &Tunables, 95 minimum: usize, 96 maximum: Option<usize>, 97 ) -> Result<Box<dyn RuntimeLinearMemory>> { 98 let reserved_size_in_bytes = Some(tunables.memory_reservation.try_into().unwrap()); 99 self.0 100 .new_memory( 101 MemoryType::from_wasmtime_memory(ty), 102 minimum, 103 maximum, 104 reserved_size_in_bytes, 105 usize::try_from(tunables.memory_guard_size).unwrap(), 106 ) 107 .map(|mem| Box::new(LinearMemoryProxy { mem }) as Box<dyn RuntimeLinearMemory>) 108 .map_err(|e| anyhow!(e)) 109 } 110 } 111 112 struct SingleMemoryInstance<'a> { 113 preallocation: Option<&'a SharedMemory>, 114 ondemand: OnDemandInstanceAllocator, 115 } 116 117 unsafe impl InstanceAllocatorImpl for SingleMemoryInstance<'_> { 118 #[cfg(feature = "component-model")] 119 fn validate_component_impl<'a>( 120 &self, 121 _component: &Component, 122 _offsets: &VMComponentOffsets<HostPtr>, 123 _get_module: &'a dyn Fn(StaticModuleIndex) -> &'a Module, 124 ) -> Result<()> { 125 unreachable!("`SingleMemoryInstance` allocator never used with components") 126 } 127 128 fn validate_module_impl(&self, module: &Module, offsets: &VMOffsets<HostPtr>) -> Result<()> { 129 anyhow::ensure!( 130 module.memories.len() == 1, 131 "`SingleMemoryInstance` allocator can only be used for modules with a single memory" 132 ); 133 self.ondemand.validate_module_impl(module, offsets)?; 134 Ok(()) 135 } 136 137 #[cfg(feature = "gc")] 138 fn validate_memory_impl(&self, memory: &wasmtime_environ::Memory) -> Result<()> { 139 self.ondemand.validate_memory_impl(memory) 140 } 141 142 #[cfg(feature = "component-model")] 143 fn increment_component_instance_count(&self) -> Result<()> { 144 self.ondemand.increment_component_instance_count() 145 } 146 147 #[cfg(feature = "component-model")] 148 fn decrement_component_instance_count(&self) { 149 self.ondemand.decrement_component_instance_count(); 150 } 151 152 fn increment_core_instance_count(&self) -> Result<()> { 153 self.ondemand.increment_core_instance_count() 154 } 155 156 fn decrement_core_instance_count(&self) { 157 self.ondemand.decrement_core_instance_count(); 158 } 159 160 unsafe fn allocate_memory( 161 &self, 162 request: &mut InstanceAllocationRequest, 163 ty: &wasmtime_environ::Memory, 164 tunables: &Tunables, 165 memory_index: Option<DefinedMemoryIndex>, 166 ) -> Result<(MemoryAllocationIndex, Memory)> { 167 #[cfg(debug_assertions)] 168 { 169 let module = request.runtime_info.env_module(); 170 let offsets = request.runtime_info.offsets(); 171 self.validate_module_impl(module, offsets) 172 .expect("should have already validated the module before allocating memory"); 173 } 174 175 match self.preallocation { 176 Some(shared_memory) => Ok(( 177 MemoryAllocationIndex::default(), 178 shared_memory.clone().as_memory(), 179 )), 180 None => self 181 .ondemand 182 .allocate_memory(request, ty, tunables, memory_index), 183 } 184 } 185 186 unsafe fn deallocate_memory( 187 &self, 188 memory_index: Option<DefinedMemoryIndex>, 189 allocation_index: MemoryAllocationIndex, 190 memory: Memory, 191 ) { 192 self.ondemand 193 .deallocate_memory(memory_index, allocation_index, memory) 194 } 195 196 unsafe fn allocate_table( 197 &self, 198 req: &mut InstanceAllocationRequest, 199 ty: &wasmtime_environ::Table, 200 tunables: &Tunables, 201 table_index: DefinedTableIndex, 202 ) -> Result<(TableAllocationIndex, Table)> { 203 self.ondemand.allocate_table(req, ty, tunables, table_index) 204 } 205 206 unsafe fn deallocate_table( 207 &self, 208 table_index: DefinedTableIndex, 209 allocation_index: TableAllocationIndex, 210 table: Table, 211 ) { 212 self.ondemand 213 .deallocate_table(table_index, allocation_index, table) 214 } 215 216 #[cfg(feature = "async")] 217 fn allocate_fiber_stack(&self) -> Result<wasmtime_fiber::FiberStack> { 218 unreachable!() 219 } 220 221 #[cfg(feature = "async")] 222 unsafe fn deallocate_fiber_stack(&self, _stack: wasmtime_fiber::FiberStack) { 223 unreachable!() 224 } 225 226 fn purge_module(&self, _: CompiledModuleId) { 227 unreachable!() 228 } 229 230 fn next_available_pkey(&self) -> Option<ProtectionKey> { 231 unreachable!() 232 } 233 234 fn restrict_to_pkey(&self, _: ProtectionKey) { 235 unreachable!() 236 } 237 238 fn allow_all_pkeys(&self) { 239 unreachable!() 240 } 241 242 #[cfg(feature = "gc")] 243 fn allocate_gc_heap( 244 &self, 245 _engine: &crate::Engine, 246 _gc_runtime: &dyn crate::vm::GcRuntime, 247 _memory_alloc_index: crate::vm::MemoryAllocationIndex, 248 _memory: Memory, 249 ) -> Result<(crate::vm::GcHeapAllocationIndex, Box<dyn crate::vm::GcHeap>)> { 250 unreachable!() 251 } 252 253 #[cfg(feature = "gc")] 254 fn deallocate_gc_heap( 255 &self, 256 _allocation_index: crate::vm::GcHeapAllocationIndex, 257 _gc_heap: Box<dyn crate::vm::GcHeap>, 258 ) -> (crate::vm::MemoryAllocationIndex, crate::vm::Memory) { 259 unreachable!() 260 } 261 } 262