wasmtime/runtime/trampoline/
memory.rs

1use crate::memory::{LinearMemory, MemoryCreator};
2use crate::prelude::*;
3use crate::runtime::vm::mpk::ProtectionKey;
4use crate::runtime::vm::{
5    CompiledModuleId, Imports, InstanceAllocationRequest, InstanceAllocator, InstanceAllocatorImpl,
6    Memory, MemoryAllocationIndex, MemoryBase, ModuleRuntimeInfo, OnDemandInstanceAllocator,
7    RuntimeLinearMemory, RuntimeMemoryCreator, SharedMemory, StorePtr, Table, TableAllocationIndex,
8};
9use crate::store::{InstanceId, StoreOpaque};
10use crate::MemoryType;
11use alloc::sync::Arc;
12use wasmtime_environ::{
13    DefinedMemoryIndex, DefinedTableIndex, EntityIndex, HostPtr, Module, Tunables, VMOffsets,
14};
15
16#[cfg(feature = "component-model")]
17use wasmtime_environ::{
18    component::{Component, VMComponentOffsets},
19    StaticModuleIndex,
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.
27pub 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 runtime_info = &ModuleRuntimeInfo::bare_maybe_imported_func(Arc::new(module), None);
51    let host_state = Box::new(());
52    let imports = Imports::default();
53    let request = InstanceAllocationRequest {
54        imports,
55        host_state,
56        store: StorePtr::new(store.traitobj()),
57        runtime_info,
58        wmemcheck: false,
59        pkey: None,
60        tunables: store.engine().tunables(),
61    };
62
63    unsafe {
64        let handle = SingleMemoryInstance {
65            preallocation,
66            ondemand: OnDemandInstanceAllocator::default(),
67        }
68        .allocate_module(request)?;
69        let instance_id = store.add_dummy_instance(handle.clone());
70        Ok(instance_id)
71    }
72}
73
74struct LinearMemoryProxy {
75    mem: Box<dyn LinearMemory>,
76}
77
78impl RuntimeLinearMemory for LinearMemoryProxy {
79    fn byte_size(&self) -> usize {
80        self.mem.byte_size()
81    }
82
83    fn byte_capacity(&self) -> usize {
84        self.mem.byte_capacity()
85    }
86
87    fn grow_to(&mut self, new_size: usize) -> Result<()> {
88        self.mem.grow_to(new_size)
89    }
90
91    fn base(&self) -> MemoryBase {
92        MemoryBase::new_raw(self.mem.as_ptr())
93    }
94}
95
96#[derive(Clone)]
97pub(crate) struct MemoryCreatorProxy(pub Arc<dyn MemoryCreator>);
98
99impl RuntimeMemoryCreator for MemoryCreatorProxy {
100    fn new_memory(
101        &self,
102        ty: &wasmtime_environ::Memory,
103        tunables: &Tunables,
104        minimum: usize,
105        maximum: Option<usize>,
106    ) -> Result<Box<dyn RuntimeLinearMemory>> {
107        let reserved_size_in_bytes = Some(tunables.memory_reservation.try_into().unwrap());
108        self.0
109            .new_memory(
110                MemoryType::from_wasmtime_memory(ty),
111                minimum,
112                maximum,
113                reserved_size_in_bytes,
114                usize::try_from(tunables.memory_guard_size).unwrap(),
115            )
116            .map(|mem| Box::new(LinearMemoryProxy { mem }) as Box<dyn RuntimeLinearMemory>)
117            .map_err(|e| anyhow!(e))
118    }
119}
120
121struct SingleMemoryInstance<'a> {
122    preallocation: Option<&'a SharedMemory>,
123    ondemand: OnDemandInstanceAllocator,
124}
125
126unsafe impl InstanceAllocatorImpl for SingleMemoryInstance<'_> {
127    #[cfg(feature = "component-model")]
128    fn validate_component_impl<'a>(
129        &self,
130        _component: &Component,
131        _offsets: &VMComponentOffsets<HostPtr>,
132        _get_module: &'a dyn Fn(StaticModuleIndex) -> &'a Module,
133    ) -> Result<()> {
134        unreachable!("`SingleMemoryInstance` allocator never used with components")
135    }
136
137    fn validate_module_impl(&self, module: &Module, offsets: &VMOffsets<HostPtr>) -> Result<()> {
138        anyhow::ensure!(
139            module.memories.len() == 1,
140            "`SingleMemoryInstance` allocator can only be used for modules with a single memory"
141        );
142        self.ondemand.validate_module_impl(module, offsets)?;
143        Ok(())
144    }
145
146    fn increment_component_instance_count(&self) -> Result<()> {
147        self.ondemand.increment_component_instance_count()
148    }
149
150    fn decrement_component_instance_count(&self) {
151        self.ondemand.decrement_component_instance_count();
152    }
153
154    fn increment_core_instance_count(&self) -> Result<()> {
155        self.ondemand.increment_core_instance_count()
156    }
157
158    fn decrement_core_instance_count(&self) {
159        self.ondemand.decrement_core_instance_count();
160    }
161
162    unsafe fn allocate_memory(
163        &self,
164        request: &mut InstanceAllocationRequest,
165        ty: &wasmtime_environ::Memory,
166        tunables: &Tunables,
167        memory_index: DefinedMemoryIndex,
168    ) -> Result<(MemoryAllocationIndex, Memory)> {
169        #[cfg(debug_assertions)]
170        {
171            let module = request.runtime_info.env_module();
172            let offsets = request.runtime_info.offsets();
173            self.validate_module_impl(module, offsets)
174                .expect("should have already validated the module before allocating memory");
175        }
176
177        match self.preallocation {
178            Some(shared_memory) => Ok((
179                MemoryAllocationIndex::default(),
180                shared_memory.clone().as_memory(),
181            )),
182            None => self
183                .ondemand
184                .allocate_memory(request, ty, tunables, memory_index),
185        }
186    }
187
188    unsafe fn deallocate_memory(
189        &self,
190        memory_index: DefinedMemoryIndex,
191        allocation_index: MemoryAllocationIndex,
192        memory: Memory,
193    ) {
194        self.ondemand
195            .deallocate_memory(memory_index, allocation_index, memory)
196    }
197
198    unsafe fn allocate_table(
199        &self,
200        req: &mut InstanceAllocationRequest,
201        ty: &wasmtime_environ::Table,
202        tunables: &Tunables,
203        table_index: DefinedTableIndex,
204    ) -> Result<(TableAllocationIndex, Table)> {
205        self.ondemand.allocate_table(req, ty, tunables, table_index)
206    }
207
208    unsafe fn deallocate_table(
209        &self,
210        table_index: DefinedTableIndex,
211        allocation_index: TableAllocationIndex,
212        table: Table,
213    ) {
214        self.ondemand
215            .deallocate_table(table_index, allocation_index, table)
216    }
217
218    #[cfg(feature = "async")]
219    fn allocate_fiber_stack(&self) -> Result<wasmtime_fiber::FiberStack> {
220        unreachable!()
221    }
222
223    #[cfg(feature = "async")]
224    unsafe fn deallocate_fiber_stack(&self, _stack: wasmtime_fiber::FiberStack) {
225        unreachable!()
226    }
227
228    fn purge_module(&self, _: CompiledModuleId) {
229        unreachable!()
230    }
231
232    fn next_available_pkey(&self) -> Option<ProtectionKey> {
233        unreachable!()
234    }
235
236    fn restrict_to_pkey(&self, _: ProtectionKey) {
237        unreachable!()
238    }
239
240    fn allow_all_pkeys(&self) {
241        unreachable!()
242    }
243
244    #[cfg(feature = "gc")]
245    fn allocate_gc_heap(
246        &self,
247        _gc_runtime: &dyn crate::runtime::vm::GcRuntime,
248    ) -> Result<(
249        crate::runtime::vm::GcHeapAllocationIndex,
250        Box<dyn crate::runtime::vm::GcHeap>,
251    )> {
252        unreachable!()
253    }
254
255    #[cfg(feature = "gc")]
256    fn deallocate_gc_heap(
257        &self,
258        _allocation_index: crate::runtime::vm::GcHeapAllocationIndex,
259        _gc_heap: Box<dyn crate::runtime::vm::GcHeap>,
260    ) {
261        unreachable!()
262    }
263}