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