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