1 use crate::prelude::*;
2 use crate::runtime::vm::const_expr::{ConstEvalContext, ConstExprEvaluator};
3 use crate::runtime::vm::imports::Imports;
4 use crate::runtime::vm::instance::{Instance, InstanceHandle};
5 use crate::runtime::vm::memory::Memory;
6 use crate::runtime::vm::mpk::ProtectionKey;
7 use crate::runtime::vm::table::Table;
8 use crate::runtime::vm::{CompiledModuleId, ModuleRuntimeInfo};
9 use crate::store::{Asyncness, InstanceId, StoreOpaque, StoreResourceLimiter};
10 use crate::{OpaqueRootScope, Val};
11 use core::future::Future;
12 use core::pin::Pin;
13 use core::{mem, ptr};
14 use wasmtime_environ::{
15     DefinedMemoryIndex, DefinedTableIndex, HostPtr, InitMemory, MemoryInitialization,
16     MemoryInitializer, Module, SizeOverflow, TableInitialValue, Trap, VMOffsets,
17 };
18 
19 #[cfg(feature = "gc")]
20 use crate::runtime::vm::{GcHeap, GcRuntime};
21 
22 #[cfg(feature = "component-model")]
23 use wasmtime_environ::{
24     StaticModuleIndex,
25     component::{Component, VMComponentOffsets},
26 };
27 
28 mod on_demand;
29 pub use self::on_demand::OnDemandInstanceAllocator;
30 
31 #[cfg(feature = "pooling-allocator")]
32 mod pooling;
33 #[cfg(feature = "pooling-allocator")]
34 pub use self::pooling::{
35     InstanceLimits, PoolConcurrencyLimitError, PoolingAllocatorMetrics, PoolingInstanceAllocator,
36     PoolingInstanceAllocatorConfig,
37 };
38 
39 /// Represents a request for a new runtime instance.
40 pub struct InstanceAllocationRequest<'a, 'b> {
41     /// The instance id that this will be assigned within the store once the
42     /// allocation has finished.
43     pub id: InstanceId,
44 
45     /// The info related to the compiled version of this module,
46     /// needed for instantiation: function metadata, JIT code
47     /// addresses, precomputed images for lazy memory and table
48     /// initialization, and the like. This Arc is cloned and held for
49     /// the lifetime of the instance.
50     pub runtime_info: &'a ModuleRuntimeInfo,
51 
52     /// The imports to use for the instantiation.
53     pub imports: Imports<'a>,
54 
55     /// The store that this instance is being allocated into.
56     pub store: &'a StoreOpaque,
57 
58     /// The store's resource limiter, if configured by the embedder.
59     pub limiter: Option<&'a mut StoreResourceLimiter<'b>>,
60 }
61 
62 /// The index of a memory allocation within an `InstanceAllocator`.
63 #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
64 pub struct MemoryAllocationIndex(u32);
65 
66 impl Default for MemoryAllocationIndex {
67     fn default() -> Self {
68         // A default `MemoryAllocationIndex` that can be used with
69         // `InstanceAllocator`s that don't actually need indices.
70         MemoryAllocationIndex(u32::MAX)
71     }
72 }
73 
74 impl MemoryAllocationIndex {
75     /// Get the underlying index of this `MemoryAllocationIndex`.
76     #[cfg(feature = "pooling-allocator")]
77     pub fn index(&self) -> usize {
78         self.0 as usize
79     }
80 }
81 
82 /// The index of a table allocation within an `InstanceAllocator`.
83 #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
84 pub struct TableAllocationIndex(u32);
85 
86 impl Default for TableAllocationIndex {
87     fn default() -> Self {
88         // A default `TableAllocationIndex` that can be used with
89         // `InstanceAllocator`s that don't actually need indices.
90         TableAllocationIndex(u32::MAX)
91     }
92 }
93 
94 impl TableAllocationIndex {
95     /// Get the underlying index of this `TableAllocationIndex`.
96     #[cfg(feature = "pooling-allocator")]
97     pub fn index(&self) -> usize {
98         self.0 as usize
99     }
100 }
101 
102 /// The index of a table allocation within an `InstanceAllocator`.
103 #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
104 pub struct GcHeapAllocationIndex(u32);
105 
106 impl Default for GcHeapAllocationIndex {
107     fn default() -> Self {
108         // A default `GcHeapAllocationIndex` that can be used with
109         // `InstanceAllocator`s that don't actually need indices.
110         GcHeapAllocationIndex(u32::MAX)
111     }
112 }
113 
114 impl GcHeapAllocationIndex {
115     /// Get the underlying index of this `GcHeapAllocationIndex`.
116     pub fn index(&self) -> usize {
117         self.0 as usize
118     }
119 }
120 
121 /// Trait that represents the hooks needed to implement an instance allocator.
122 ///
123 /// Implement this trait when implementing new instance allocators, but don't
124 /// use this trait when you need an instance allocator. Instead use the
125 /// `InstanceAllocator` trait for that, which has additional helper methods and
126 /// a blanket implementation for all types that implement this trait.
127 ///
128 /// # Safety
129 ///
130 /// This trait is unsafe as it requires knowledge of Wasmtime's runtime
131 /// internals to implement correctly.
132 pub unsafe trait InstanceAllocator: Send + Sync {
133     /// Validate whether a component (including all of its contained core
134     /// modules) is allocatable by this instance allocator.
135     #[cfg(feature = "component-model")]
136     fn validate_component<'a>(
137         &self,
138         component: &Component,
139         offsets: &VMComponentOffsets<HostPtr>,
140         get_module: &'a dyn Fn(StaticModuleIndex) -> &'a Module,
141     ) -> Result<()>;
142 
143     /// Validate whether a module is allocatable by this instance allocator.
144     fn validate_module(&self, module: &Module, offsets: &VMOffsets<HostPtr>) -> Result<()>;
145 
146     /// Validate whether a memory is allocatable by this instance allocator.
147     #[cfg(feature = "gc")]
148     fn validate_memory(&self, memory: &wasmtime_environ::Memory) -> Result<()>;
149 
150     /// Increment the count of concurrent component instances that are currently
151     /// allocated, if applicable.
152     ///
153     /// Not all instance allocators will have limits for the maximum number of
154     /// concurrent component instances that can be live at the same time, and
155     /// these allocators may implement this method with a no-op.
156     //
157     // Note: It would be nice to have an associated type that on construction
158     // does the increment and on drop does the decrement but there are two
159     // problems with this:
160     //
161     // 1. This trait's implementations are always used as trait objects, and
162     //    associated types are not object safe.
163     //
164     // 2. We would want a parameterized `Drop` implementation so that we could
165     //    pass in the `InstanceAllocator` on drop, but this doesn't exist in
166     //    Rust. Therefore, we would be forced to add reference counting and
167     //    stuff like that to keep a handle on the instance allocator from this
168     //    theoretical type. That's a bummer.
169     #[cfg(feature = "component-model")]
170     fn increment_component_instance_count(&self) -> Result<()>;
171 
172     /// The dual of `increment_component_instance_count`.
173     #[cfg(feature = "component-model")]
174     fn decrement_component_instance_count(&self);
175 
176     /// Increment the count of concurrent core module instances that are
177     /// currently allocated, if applicable.
178     ///
179     /// Not all instance allocators will have limits for the maximum number of
180     /// concurrent core module instances that can be live at the same time, and
181     /// these allocators may implement this method with a no-op.
182     fn increment_core_instance_count(&self) -> Result<()>;
183 
184     /// The dual of `increment_core_instance_count`.
185     fn decrement_core_instance_count(&self);
186 
187     /// Allocate a memory for an instance.
188     ///
189     /// Returns `Err(OutOfMemory)` if boxing the future fails. The inner
190     /// `Result` covers other allocation errors (e.g. resource limits).
191     fn allocate_memory<'a, 'b: 'a, 'c: 'a>(
192         &'a self,
193         request: &'a mut InstanceAllocationRequest<'b, 'c>,
194         ty: &'a wasmtime_environ::Memory,
195         memory_index: Option<DefinedMemoryIndex>,
196     ) -> Result<
197         Pin<Box<dyn Future<Output = Result<(MemoryAllocationIndex, Memory)>> + Send + 'a>>,
198         OutOfMemory,
199     >;
200 
201     /// Deallocate an instance's previously allocated memory.
202     ///
203     /// # Unsafety
204     ///
205     /// The memory must have previously been allocated by
206     /// `Self::allocate_memory`, be at the given index, and must currently be
207     /// allocated. It must never be used again.
208     unsafe fn deallocate_memory(
209         &self,
210         memory_index: Option<DefinedMemoryIndex>,
211         allocation_index: MemoryAllocationIndex,
212         memory: Memory,
213     );
214 
215     /// Allocate a table for an instance.
216     ///
217     /// Returns `Err(OutOfMemory)` if boxing the future fails. The inner
218     /// `Result` covers other allocation errors (e.g. resource limits).
219     fn allocate_table<'a, 'b: 'a, 'c: 'a>(
220         &'a self,
221         req: &'a mut InstanceAllocationRequest<'b, 'c>,
222         table: &'a wasmtime_environ::Table,
223         table_index: DefinedTableIndex,
224     ) -> Result<
225         Pin<Box<dyn Future<Output = Result<(TableAllocationIndex, Table)>> + Send + 'a>>,
226         OutOfMemory,
227     >;
228 
229     /// Deallocate an instance's previously allocated table.
230     ///
231     /// # Unsafety
232     ///
233     /// The table must have previously been allocated by `Self::allocate_table`,
234     /// be at the given index, and must currently be allocated. It must never be
235     /// used again.
236     unsafe fn deallocate_table(
237         &self,
238         table_index: DefinedTableIndex,
239         allocation_index: TableAllocationIndex,
240         table: Table,
241     );
242 
243     /// Allocates a fiber stack for calling async functions on.
244     #[cfg(feature = "async")]
245     fn allocate_fiber_stack(&self) -> Result<wasmtime_fiber::FiberStack>;
246 
247     /// Deallocates a fiber stack that was previously allocated with
248     /// `allocate_fiber_stack`.
249     ///
250     /// # Safety
251     ///
252     /// The provided stack is required to have been allocated with
253     /// `allocate_fiber_stack`.
254     #[cfg(feature = "async")]
255     unsafe fn deallocate_fiber_stack(&self, stack: wasmtime_fiber::FiberStack);
256 
257     /// Allocate a GC heap for allocating Wasm GC objects within.
258     #[cfg(feature = "gc")]
259     fn allocate_gc_heap(
260         &self,
261         engine: &crate::Engine,
262         gc_runtime: &dyn GcRuntime,
263         memory_alloc_index: MemoryAllocationIndex,
264         memory: Memory,
265     ) -> Result<(GcHeapAllocationIndex, Box<dyn GcHeap>)>;
266 
267     /// Deallocate a GC heap that was previously allocated with
268     /// `allocate_gc_heap`.
269     #[cfg(feature = "gc")]
270     #[must_use = "it is the caller's responsibility to deallocate the GC heap's underlying memory \
271                   storage after the GC heap is deallocated"]
272     fn deallocate_gc_heap(
273         &self,
274         allocation_index: GcHeapAllocationIndex,
275         gc_heap: Box<dyn GcHeap>,
276     ) -> (MemoryAllocationIndex, Memory);
277 
278     /// Purges all lingering resources related to `module` from within this
279     /// allocator.
280     ///
281     /// Primarily present for the pooling allocator to remove mappings of
282     /// this module from slots in linear memory.
283     fn purge_module(&self, module: CompiledModuleId);
284 
285     /// Use the next available protection key.
286     ///
287     /// The pooling allocator can use memory protection keys (MPK) for
288     /// compressing the guard regions protecting against OOB. Each
289     /// pool-allocated store needs its own key.
290     fn next_available_pkey(&self) -> Option<ProtectionKey>;
291 
292     /// Restrict access to memory regions protected by `pkey`.
293     ///
294     /// This is useful for the pooling allocator, which can use memory
295     /// protection keys (MPK). Note: this may still allow access to other
296     /// protection keys, such as the default kernel key; see implementations of
297     /// this.
298     fn restrict_to_pkey(&self, pkey: ProtectionKey);
299 
300     /// Allow access to memory regions protected by any protection key.
301     fn allow_all_pkeys(&self);
302 
303     /// Returns `Some(&PoolingInstanceAllocator)` if this is one.
304     #[cfg(feature = "pooling-allocator")]
305     fn as_pooling(&self) -> Option<&PoolingInstanceAllocator> {
306         None
307     }
308 }
309 
310 impl dyn InstanceAllocator + '_ {
311     /// Allocates a fresh `InstanceHandle` for the `req` given.
312     ///
313     /// This will allocate memories and tables internally from this allocator
314     /// and weave that altogether into a final and complete `InstanceHandle`
315     /// ready to be registered with a store.
316     ///
317     /// Note that the returned instance must still have `.initialize(..)` called
318     /// on it to complete the instantiation process.
319     ///
320     /// # Safety
321     ///
322     /// The `request` provided must be valid, e.g. the imports within are
323     /// correctly sized/typed for the instance being created.
324     pub(crate) async unsafe fn allocate_module(
325         &self,
326         mut request: InstanceAllocationRequest<'_, '_>,
327     ) -> Result<InstanceHandle> {
328         let module = request.runtime_info.env_module();
329 
330         if cfg!(debug_assertions) {
331             InstanceAllocator::validate_module(self, module, request.runtime_info.offsets())
332                 .expect("module should have already been validated before allocation");
333         }
334 
335         self.increment_core_instance_count()?;
336 
337         let num_defined_memories = module.num_defined_memories();
338         let num_defined_tables = module.num_defined_tables();
339 
340         let mut guard = DeallocateOnDrop {
341             run_deallocate: true,
342             memories: TryPrimaryMap::with_capacity(num_defined_memories)?,
343             tables: TryPrimaryMap::with_capacity(num_defined_tables)?,
344             allocator: self,
345         };
346 
347         self.allocate_memories(&mut request, &mut guard.memories)
348             .await?;
349         self.allocate_tables(&mut request, &mut guard.tables)
350             .await?;
351         guard.run_deallocate = false;
352         // SAFETY: memories/tables were just allocated from the store within
353         // `request` and this function's own contract requires that the
354         // imports are valid.
355         return unsafe {
356             Ok(Instance::new(
357                 request,
358                 mem::take(&mut guard.memories),
359                 mem::take(&mut guard.tables),
360             )?)
361         };
362 
363         struct DeallocateOnDrop<'a> {
364             run_deallocate: bool,
365             memories: TryPrimaryMap<DefinedMemoryIndex, (MemoryAllocationIndex, Memory)>,
366             tables: TryPrimaryMap<DefinedTableIndex, (TableAllocationIndex, Table)>,
367             allocator: &'a (dyn InstanceAllocator + 'a),
368         }
369 
370         impl Drop for DeallocateOnDrop<'_> {
371             fn drop(&mut self) {
372                 if !self.run_deallocate {
373                     return;
374                 }
375                 // SAFETY: these were previously allocated by this allocator
376                 unsafe {
377                     self.allocator.deallocate_memories(&mut self.memories);
378                     self.allocator.deallocate_tables(&mut self.tables);
379                 }
380                 self.allocator.decrement_core_instance_count();
381             }
382         }
383     }
384 
385     /// Deallocates the provided instance.
386     ///
387     /// This will null-out the pointer within `handle` and otherwise reclaim
388     /// resources such as tables, memories, and the instance memory itself.
389     ///
390     /// # Unsafety
391     ///
392     /// The instance must have previously been allocated by `Self::allocate`.
393     pub(crate) unsafe fn deallocate_module(&self, handle: &mut InstanceHandle) {
394         // SAFETY: the contract of `deallocate_*` is itself a contract of this
395         // function, that the memories/tables were previously allocated from
396         // here.
397         unsafe {
398             self.deallocate_memories(handle.get_mut().memories_mut());
399             self.deallocate_tables(handle.get_mut().tables_mut());
400         }
401 
402         self.decrement_core_instance_count();
403     }
404 
405     /// Allocate the memories for the given instance allocation request, pushing
406     /// them into `memories`.
407     async fn allocate_memories<'a, 'b: 'a, 'c: 'a>(
408         &'a self,
409         request: &'a mut InstanceAllocationRequest<'b, 'c>,
410         memories: &mut TryPrimaryMap<DefinedMemoryIndex, (MemoryAllocationIndex, Memory)>,
411     ) -> Result<()> {
412         let module = request.runtime_info.env_module();
413 
414         if cfg!(debug_assertions) {
415             InstanceAllocator::validate_module(self, module, request.runtime_info.offsets())
416                 .expect("module should have already been validated before allocation");
417         }
418 
419         for (memory_index, ty) in module.memories.iter().skip(module.num_imported_memories) {
420             let memory_index = module
421                 .defined_memory_index(memory_index)
422                 .expect("should be a defined memory since we skipped imported ones");
423 
424             let memory = self
425                 .allocate_memory(request, ty, Some(memory_index))?
426                 .await?;
427             memories.push(memory)?;
428         }
429 
430         Ok(())
431     }
432 
433     /// Deallocate all the memories in the given primary map.
434     ///
435     /// # Unsafety
436     ///
437     /// The memories must have previously been allocated by
438     /// `Self::allocate_memories`.
439     unsafe fn deallocate_memories(
440         &self,
441         memories: &mut TryPrimaryMap<DefinedMemoryIndex, (MemoryAllocationIndex, Memory)>,
442     ) {
443         for (memory_index, (allocation_index, memory)) in mem::take(memories) {
444             // Because deallocating memory is infallible, we don't need to worry
445             // about leaking subsequent memories if the first memory failed to
446             // deallocate. If deallocating memory ever becomes fallible, we will
447             // need to be careful here!
448             //
449             // SAFETY: the unsafe contract here is the same as the unsafe
450             // contract of this function, that the memories were previously
451             // allocated by this allocator.
452             unsafe {
453                 self.deallocate_memory(Some(memory_index), allocation_index, memory);
454             }
455         }
456     }
457 
458     /// Allocate tables for the given instance allocation request, pushing them
459     /// into `tables`.
460     async fn allocate_tables<'a, 'b: 'a, 'c: 'a>(
461         &'a self,
462         request: &'a mut InstanceAllocationRequest<'b, 'c>,
463         tables: &mut TryPrimaryMap<DefinedTableIndex, (TableAllocationIndex, Table)>,
464     ) -> Result<()> {
465         let module = request.runtime_info.env_module();
466 
467         if cfg!(debug_assertions) {
468             InstanceAllocator::validate_module(self, module, request.runtime_info.offsets())
469                 .expect("module should have already been validated before allocation");
470         }
471 
472         for (index, table) in module.tables.iter().skip(module.num_imported_tables) {
473             let def_index = module
474                 .defined_table_index(index)
475                 .expect("should be a defined table since we skipped imported ones");
476 
477             let table = self.allocate_table(request, table, def_index)?.await?;
478             tables.push(table)?;
479         }
480 
481         Ok(())
482     }
483 
484     /// Deallocate all the tables in the given primary map.
485     ///
486     /// # Unsafety
487     ///
488     /// The tables must have previously been allocated by
489     /// `Self::allocate_tables`.
490     unsafe fn deallocate_tables(
491         &self,
492         tables: &mut TryPrimaryMap<DefinedTableIndex, (TableAllocationIndex, Table)>,
493     ) {
494         for (table_index, (allocation_index, table)) in mem::take(tables) {
495             // SAFETY: the tables here were allocated from this allocator per
496             // the contract on this function itself.
497             unsafe {
498                 self.deallocate_table(table_index, allocation_index, table);
499             }
500         }
501     }
502 }
503 
504 fn check_table_init_bounds(
505     store: &mut StoreOpaque,
506     instance: InstanceId,
507     module: &Module,
508     context: &mut ConstEvalContext,
509     const_evaluator: &mut ConstExprEvaluator,
510 ) -> Result<()> {
511     let mut store = OpaqueRootScope::new(store);
512 
513     for segment in module.table_initialization.segments.iter() {
514         let start = const_evaluator
515             .eval_int(&mut store, context, &segment.offset)
516             .expect("const expression should be valid");
517         let start = usize::try_from(start.unwrap_i32().cast_unsigned()).unwrap();
518         let end = start.checked_add(usize::try_from(segment.elements.len()).unwrap());
519 
520         let table = store.instance_mut(instance).get_table(segment.table_index);
521         match end {
522             Some(end) if end <= table.size() => {
523                 // Initializer is in bounds
524             }
525             _ => {
526                 bail!("table out of bounds: elements segment does not fit")
527             }
528         }
529     }
530 
531     Ok(())
532 }
533 
534 async fn initialize_tables(
535     store: &mut StoreOpaque,
536     mut limiter: Option<&mut StoreResourceLimiter<'_>>,
537     context: &mut ConstEvalContext,
538     const_evaluator: &mut ConstExprEvaluator,
539     module: &Module,
540 ) -> Result<()> {
541     let mut store = OpaqueRootScope::new(store);
542     for (table, init) in module.table_initialization.initial_values.iter() {
543         match init {
544             // Tables are always initially null-initialized at this time
545             TableInitialValue::Null { precomputed: _ } => {}
546 
547             TableInitialValue::Expr(expr) => {
548                 let init = const_evaluator
549                     .eval(&mut store, limiter.as_deref_mut(), context, expr)
550                     .await?;
551                 let idx = module.table_index(table);
552                 let id = store.id();
553                 let table = store
554                     .instance_mut(context.instance)
555                     .get_exported_table(id, idx);
556                 let size = table._size(&store);
557                 table._fill(&mut store, 0, init.ref_().unwrap(), size)?;
558             }
559         }
560     }
561 
562     // Note: if the module's table initializer state is in
563     // FuncTable mode, we will lazily initialize tables based on
564     // any statically-precomputed image of FuncIndexes, but there
565     // may still be "leftover segments" that could not be
566     // incorporated. So we have a unified handler here that
567     // iterates over all segments (Segments mode) or leftover
568     // segments (FuncTable mode) to initialize.
569     for segment in module.table_initialization.segments.iter() {
570         let start = const_evaluator
571             .eval_int(&mut store, context, &segment.offset)
572             .expect("const expression should be valid");
573         let start = get_index(
574             start,
575             store.instance(context.instance).env_module().tables[segment.table_index].idx_type,
576         );
577         Instance::table_init_segment(
578             &mut store,
579             limiter.as_deref_mut(),
580             context.asyncness,
581             context.instance,
582             const_evaluator,
583             segment.table_index,
584             &segment.elements,
585             start,
586             0,
587             segment.elements.len(),
588         )
589         .await?;
590     }
591 
592     Ok(())
593 }
594 
595 fn get_index(val: &Val, ty: wasmtime_environ::IndexType) -> u64 {
596     match ty {
597         wasmtime_environ::IndexType::I32 => val.unwrap_i32().cast_unsigned().into(),
598         wasmtime_environ::IndexType::I64 => val.unwrap_i64().cast_unsigned(),
599     }
600 }
601 
602 fn get_memory_init_start(
603     store: &mut StoreOpaque,
604     init: &MemoryInitializer,
605     instance: InstanceId,
606     context: &mut ConstEvalContext,
607     const_evaluator: &mut ConstExprEvaluator,
608 ) -> Result<u64> {
609     let mut store = OpaqueRootScope::new(store);
610     const_evaluator
611         .eval_int(&mut store, context, &init.offset)
612         .map(|v| {
613             get_index(
614                 v,
615                 store.instance(instance).env_module().memories[init.memory_index].idx_type,
616             )
617         })
618 }
619 
620 fn check_memory_init_bounds(
621     store: &mut StoreOpaque,
622     instance: InstanceId,
623     initializers: &[MemoryInitializer],
624     context: &mut ConstEvalContext,
625     const_evaluator: &mut ConstExprEvaluator,
626 ) -> Result<()> {
627     for init in initializers {
628         let memory = store.instance_mut(instance).get_memory(init.memory_index);
629         let start = get_memory_init_start(store, init, instance, context, const_evaluator)?;
630         let end = usize::try_from(start)
631             .ok()
632             .and_then(|start| start.checked_add(init.data.len()));
633 
634         match end {
635             Some(end) if end <= memory.current_length() => {
636                 // Initializer is in bounds
637             }
638             _ => {
639                 bail!("memory out of bounds: data segment does not fit")
640             }
641         }
642     }
643 
644     Ok(())
645 }
646 
647 fn initialize_memories(
648     store: &mut StoreOpaque,
649     context: &mut ConstEvalContext,
650     const_evaluator: &mut ConstExprEvaluator,
651     module: &Module,
652 ) -> Result<()> {
653     // Delegates to the `init_memory` method which is sort of a duplicate of
654     // `instance.memory_init_segment` but is used at compile-time in other
655     // contexts so is shared here to have only one method of memory
656     // initialization.
657     //
658     // This call to `init_memory` notably implements all the bells and whistles
659     // so errors only happen if an out-of-bounds segment is found, in which case
660     // a trap is returned.
661 
662     struct InitMemoryAtInstantiation<'a> {
663         module: &'a Module,
664         store: &'a mut StoreOpaque,
665         context: &'a mut ConstEvalContext,
666         const_evaluator: &'a mut ConstExprEvaluator,
667     }
668 
669     impl InitMemory for InitMemoryAtInstantiation<'_> {
670         fn memory_size_in_bytes(
671             &mut self,
672             memory: wasmtime_environ::MemoryIndex,
673         ) -> Result<u64, SizeOverflow> {
674             let len = self
675                 .store
676                 .instance(self.context.instance)
677                 .get_memory(memory)
678                 .current_length();
679             let len = u64::try_from(len).unwrap();
680             Ok(len)
681         }
682 
683         fn eval_offset(
684             &mut self,
685             memory: wasmtime_environ::MemoryIndex,
686             expr: &wasmtime_environ::ConstExpr,
687         ) -> Option<u64> {
688             let mut store = OpaqueRootScope::new(&mut *self.store);
689             let val = self
690                 .const_evaluator
691                 .eval_int(&mut store, self.context, expr)
692                 .expect("const expression should be valid");
693             Some(get_index(
694                 val,
695                 store.instance(self.context.instance).env_module().memories[memory].idx_type,
696             ))
697         }
698 
699         fn write(
700             &mut self,
701             memory_index: wasmtime_environ::MemoryIndex,
702             init: &wasmtime_environ::StaticMemoryInitializer,
703         ) -> bool {
704             // If this initializer applies to a defined memory but that memory
705             // doesn't need initialization, due to something like copy-on-write
706             // pre-initializing it via mmap magic, then this initializer can be
707             // skipped entirely.
708             let instance = self.store.instance_mut(self.context.instance);
709             if let Some(memory_index) = self.module.defined_memory_index(memory_index) {
710                 if !instance.memories[memory_index].1.needs_init() {
711                     return true;
712                 }
713             }
714             let memory = instance.get_memory(memory_index);
715 
716             unsafe {
717                 let src = instance.wasm_data(init.data.clone());
718                 let offset = usize::try_from(init.offset).unwrap();
719                 let dst = memory.base.as_ptr().add(offset);
720 
721                 assert!(offset + src.len() <= memory.current_length());
722 
723                 // FIXME audit whether this is safe in the presence of shared
724                 // memory
725                 // (https://github.com/bytecodealliance/wasmtime/issues/4203).
726                 ptr::copy_nonoverlapping(src.as_ptr(), dst, src.len())
727             }
728             true
729         }
730     }
731 
732     let ok = module
733         .memory_initialization
734         .init_memory(&mut InitMemoryAtInstantiation {
735             module,
736             store,
737             context,
738             const_evaluator,
739         });
740     if !ok {
741         return Err(Trap::MemoryOutOfBounds.into());
742     }
743 
744     Ok(())
745 }
746 
747 fn check_init_bounds(
748     store: &mut StoreOpaque,
749     instance: InstanceId,
750     context: &mut ConstEvalContext,
751     const_evaluator: &mut ConstExprEvaluator,
752     module: &Module,
753 ) -> Result<()> {
754     check_table_init_bounds(store, instance, module, context, const_evaluator)?;
755 
756     match &module.memory_initialization {
757         MemoryInitialization::Segmented(initializers) => {
758             check_memory_init_bounds(store, instance, initializers, context, const_evaluator)?;
759         }
760         // Statically validated already to have everything in-bounds.
761         MemoryInitialization::Static { .. } => {}
762     }
763 
764     Ok(())
765 }
766 
767 async fn initialize_globals(
768     store: &mut StoreOpaque,
769     mut limiter: Option<&mut StoreResourceLimiter<'_>>,
770     context: &mut ConstEvalContext,
771     const_evaluator: &mut ConstExprEvaluator,
772     module: &Module,
773 ) -> Result<()> {
774     assert!(core::ptr::eq(
775         &**store.instance(context.instance).env_module(),
776         module
777     ));
778 
779     let mut store = OpaqueRootScope::new(store);
780 
781     for (index, init) in module.global_initializers.iter() {
782         // Attempt a simple, synchronous evaluation before hitting the
783         // general-purpose `.await` point below. This benchmarks ~15% faster in
784         // instantiation vs just falling through to `.await` below.
785         let val = if let Some(val) = const_evaluator.try_simple(init) {
786             val
787         } else {
788             const_evaluator
789                 .eval(&mut store, limiter.as_deref_mut(), context, init)
790                 .await?
791         };
792 
793         let id = store.id();
794         let index = module.global_index(index);
795         let mut instance = store.instance_mut(context.instance);
796 
797         #[cfg(feature = "wmemcheck")]
798         if index.as_u32() == 0
799             && module.globals[index].wasm_ty == wasmtime_environ::WasmValType::I32
800         {
801             if let Some(wmemcheck) = instance.as_mut().wmemcheck_state_mut() {
802                 let size = usize::try_from(val.unwrap_i32()).unwrap();
803                 wmemcheck.set_stack_size(size);
804             }
805         }
806 
807         let global = instance.as_mut().get_exported_global(id, index);
808 
809         // Note that mutability is bypassed here because this is, by definition,
810         // initialization of globals meaning that if it's an immutable global
811         // this is the one and only write.
812         //
813         // SAFETY: this is a valid module so `val` should have the correct type
814         // for this global, and it's safe to write to a global for the first
815         // time as-is happening here.
816         unsafe {
817             global.set_unchecked(&mut store, &val)?;
818         }
819     }
820     Ok(())
821 }
822 
823 pub async fn initialize_instance(
824     store: &mut StoreOpaque,
825     mut limiter: Option<&mut StoreResourceLimiter<'_>>,
826     instance: InstanceId,
827     module: &Module,
828     is_bulk_memory: bool,
829     asyncness: Asyncness,
830 ) -> Result<()> {
831     let mut context = ConstEvalContext::new(instance, asyncness);
832     let mut const_evaluator = ConstExprEvaluator::default();
833 
834     // If bulk memory is not enabled, bounds check the data and element segments before
835     // making any changes. With bulk memory enabled, initializers are processed
836     // in-order and side effects are observed up to the point of an out-of-bounds
837     // initializer, so the early checking is not desired.
838     if !is_bulk_memory {
839         check_init_bounds(store, instance, &mut context, &mut const_evaluator, module)?;
840     }
841 
842     initialize_globals(
843         store,
844         limiter.as_deref_mut(),
845         &mut context,
846         &mut const_evaluator,
847         module,
848     )
849     .await?;
850     initialize_tables(
851         store,
852         limiter.as_deref_mut(),
853         &mut context,
854         &mut const_evaluator,
855         module,
856     )
857     .await?;
858     initialize_memories(store, &mut context, &mut const_evaluator, &module)?;
859 
860     Ok(())
861 }
862 
863 #[cfg(test)]
864 mod tests {
865     use super::*;
866 
867     #[test]
868     fn allocator_traits_are_object_safe() {
869         fn _instance_allocator(_: &dyn InstanceAllocator) {}
870     }
871 }
872