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