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