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