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