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