wasmtime/runtime/trampoline/
memory.rs

1use crate::MemoryType;
2use crate::memory::{LinearMemory, MemoryCreator};
3use crate::prelude::*;
4use crate::runtime::vm::mpk::ProtectionKey;
5use crate::runtime::vm::{
6    CompiledModuleId, InstanceAllocationRequest, InstanceAllocator, Memory, MemoryAllocationIndex,
7    MemoryBase, ModuleRuntimeInfo, OnDemandInstanceAllocator, RuntimeLinearMemory,
8    RuntimeMemoryCreator, SharedMemory, Table, TableAllocationIndex,
9};
10use crate::store::{AllocateInstanceKind, InstanceId, StoreOpaque, StoreResourceLimiter};
11use alloc::sync::Arc;
12use wasmtime_environ::{
13    DefinedMemoryIndex, DefinedTableIndex, EntityIndex, HostPtr, Module, StaticModuleIndex,
14    Tunables, VMOffsets,
15};
16
17#[cfg(feature = "component-model")]
18use 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.
25pub 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
67struct LinearMemoryProxy {
68    mem: Box<dyn LinearMemory>,
69}
70
71impl 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)]
98pub(crate) struct MemoryCreatorProxy(pub Arc<dyn MemoryCreator>);
99
100impl 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
122struct SingleMemoryInstance<'a> {
123    preallocation: Option<&'a SharedMemory>,
124    ondemand: OnDemandInstanceAllocator,
125}
126
127#[async_trait::async_trait]
128unsafe 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}