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