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