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