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