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, 'b> { 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<'b>>, 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 mut limiter: Option<&mut StoreResourceLimiter<'_>>, 519 context: &mut ConstEvalContext, 520 const_evaluator: &mut ConstExprEvaluator, 521 module: &Module, 522 ) -> Result<()> { 523 let mut store = OpaqueRootScope::new(store); 524 for (table, init) in module.table_initialization.initial_values.iter() { 525 match init { 526 // Tables are always initially null-initialized at this time 527 TableInitialValue::Null { precomputed: _ } => {} 528 529 TableInitialValue::Expr(expr) => { 530 let init = const_evaluator 531 .eval(&mut store, limiter.as_deref_mut(), context, expr) 532 .await 533 .expect("const expression should be valid"); 534 let idx = module.table_index(table); 535 let id = store.id(); 536 let table = store 537 .instance_mut(context.instance) 538 .get_exported_table(id, idx); 539 let size = table._size(&store); 540 table._fill(&mut store, 0, init.ref_().unwrap(), size)?; 541 } 542 } 543 } 544 545 // Note: if the module's table initializer state is in 546 // FuncTable mode, we will lazily initialize tables based on 547 // any statically-precomputed image of FuncIndexes, but there 548 // may still be "leftover segments" that could not be 549 // incorporated. So we have a unified handler here that 550 // iterates over all segments (Segments mode) or leftover 551 // segments (FuncTable mode) to initialize. 552 for segment in module.table_initialization.segments.iter() { 553 let start = const_evaluator 554 .eval_int(&mut store, context, &segment.offset) 555 .expect("const expression should be valid"); 556 let start = get_index( 557 start, 558 store.instance(context.instance).env_module().tables[segment.table_index].idx_type, 559 ); 560 Instance::table_init_segment( 561 &mut store, 562 limiter.as_deref_mut(), 563 context.instance, 564 const_evaluator, 565 segment.table_index, 566 &segment.elements, 567 start, 568 0, 569 segment.elements.len(), 570 ) 571 .await?; 572 } 573 574 Ok(()) 575 } 576 577 fn get_index(val: &Val, ty: wasmtime_environ::IndexType) -> u64 { 578 match ty { 579 wasmtime_environ::IndexType::I32 => val.unwrap_i32().cast_unsigned().into(), 580 wasmtime_environ::IndexType::I64 => val.unwrap_i64().cast_unsigned(), 581 } 582 } 583 584 fn get_memory_init_start( 585 store: &mut StoreOpaque, 586 init: &MemoryInitializer, 587 instance: InstanceId, 588 ) -> Result<u64> { 589 let mut context = ConstEvalContext::new(instance); 590 let mut const_evaluator = ConstExprEvaluator::default(); 591 let mut store = OpaqueRootScope::new(store); 592 const_evaluator 593 .eval_int(&mut store, &mut context, &init.offset) 594 .map(|v| { 595 get_index( 596 v, 597 store.instance(instance).env_module().memories[init.memory_index].idx_type, 598 ) 599 }) 600 } 601 602 fn check_memory_init_bounds( 603 store: &mut StoreOpaque, 604 instance: InstanceId, 605 initializers: &[MemoryInitializer], 606 ) -> Result<()> { 607 for init in initializers { 608 let memory = store.instance_mut(instance).get_memory(init.memory_index); 609 let start = get_memory_init_start(store, init, instance)?; 610 let end = usize::try_from(start) 611 .ok() 612 .and_then(|start| start.checked_add(init.data.len())); 613 614 match end { 615 Some(end) if end <= memory.current_length() => { 616 // Initializer is in bounds 617 } 618 _ => { 619 bail!("memory out of bounds: data segment does not fit") 620 } 621 } 622 } 623 624 Ok(()) 625 } 626 627 fn initialize_memories( 628 store: &mut StoreOpaque, 629 context: &mut ConstEvalContext, 630 const_evaluator: &mut ConstExprEvaluator, 631 module: &Module, 632 ) -> Result<()> { 633 // Delegates to the `init_memory` method which is sort of a duplicate of 634 // `instance.memory_init_segment` but is used at compile-time in other 635 // contexts so is shared here to have only one method of memory 636 // initialization. 637 // 638 // This call to `init_memory` notably implements all the bells and whistles 639 // so errors only happen if an out-of-bounds segment is found, in which case 640 // a trap is returned. 641 642 struct InitMemoryAtInstantiation<'a> { 643 module: &'a Module, 644 store: &'a mut StoreOpaque, 645 context: &'a mut ConstEvalContext, 646 const_evaluator: &'a mut ConstExprEvaluator, 647 } 648 649 impl InitMemory for InitMemoryAtInstantiation<'_> { 650 fn memory_size_in_bytes( 651 &mut self, 652 memory: wasmtime_environ::MemoryIndex, 653 ) -> Result<u64, SizeOverflow> { 654 let len = self 655 .store 656 .instance(self.context.instance) 657 .get_memory(memory) 658 .current_length(); 659 let len = u64::try_from(len).unwrap(); 660 Ok(len) 661 } 662 663 fn eval_offset( 664 &mut self, 665 memory: wasmtime_environ::MemoryIndex, 666 expr: &wasmtime_environ::ConstExpr, 667 ) -> Option<u64> { 668 let mut store = OpaqueRootScope::new(&mut *self.store); 669 let val = self 670 .const_evaluator 671 .eval_int(&mut store, self.context, expr) 672 .expect("const expression should be valid"); 673 Some(get_index( 674 val, 675 store.instance(self.context.instance).env_module().memories[memory].idx_type, 676 )) 677 } 678 679 fn write( 680 &mut self, 681 memory_index: wasmtime_environ::MemoryIndex, 682 init: &wasmtime_environ::StaticMemoryInitializer, 683 ) -> bool { 684 // If this initializer applies to a defined memory but that memory 685 // doesn't need initialization, due to something like copy-on-write 686 // pre-initializing it via mmap magic, then this initializer can be 687 // skipped entirely. 688 let instance = self.store.instance_mut(self.context.instance); 689 if let Some(memory_index) = self.module.defined_memory_index(memory_index) { 690 if !instance.memories[memory_index].1.needs_init() { 691 return true; 692 } 693 } 694 let memory = instance.get_memory(memory_index); 695 696 unsafe { 697 let src = instance.wasm_data(init.data.clone()); 698 let offset = usize::try_from(init.offset).unwrap(); 699 let dst = memory.base.as_ptr().add(offset); 700 701 assert!(offset + src.len() <= memory.current_length()); 702 703 // FIXME audit whether this is safe in the presence of shared 704 // memory 705 // (https://github.com/bytecodealliance/wasmtime/issues/4203). 706 ptr::copy_nonoverlapping(src.as_ptr(), dst, src.len()) 707 } 708 true 709 } 710 } 711 712 let ok = module 713 .memory_initialization 714 .init_memory(&mut InitMemoryAtInstantiation { 715 module, 716 store, 717 context, 718 const_evaluator, 719 }); 720 if !ok { 721 return Err(Trap::MemoryOutOfBounds.into()); 722 } 723 724 Ok(()) 725 } 726 727 fn check_init_bounds(store: &mut StoreOpaque, instance: InstanceId, module: &Module) -> Result<()> { 728 check_table_init_bounds(store, instance, module)?; 729 730 match &module.memory_initialization { 731 MemoryInitialization::Segmented(initializers) => { 732 check_memory_init_bounds(store, instance, initializers)?; 733 } 734 // Statically validated already to have everything in-bounds. 735 MemoryInitialization::Static { .. } => {} 736 } 737 738 Ok(()) 739 } 740 741 async fn initialize_globals( 742 store: &mut StoreOpaque, 743 mut limiter: Option<&mut StoreResourceLimiter<'_>>, 744 context: &mut ConstEvalContext, 745 const_evaluator: &mut ConstExprEvaluator, 746 module: &Module, 747 ) -> Result<()> { 748 assert!(core::ptr::eq( 749 &**store.instance(context.instance).env_module(), 750 module 751 )); 752 753 let mut store = OpaqueRootScope::new(store); 754 755 for (index, init) in module.global_initializers.iter() { 756 // Attempt a simple, synchronous evaluation before hitting the 757 // general-purpose `.await` point below. This benchmarks ~15% faster in 758 // instantiation vs just falling through to `.await` below. 759 let val = if let Some(val) = const_evaluator.try_simple(init) { 760 val 761 } else { 762 const_evaluator 763 .eval(&mut store, limiter.as_deref_mut(), context, init) 764 .await 765 .expect("should be a valid const expr") 766 }; 767 768 let id = store.id(); 769 let index = module.global_index(index); 770 let mut instance = store.instance_mut(context.instance); 771 772 #[cfg(feature = "wmemcheck")] 773 if index.as_u32() == 0 774 && module.globals[index].wasm_ty == wasmtime_environ::WasmValType::I32 775 { 776 if let Some(wmemcheck) = instance.as_mut().wmemcheck_state_mut() { 777 let size = usize::try_from(val.unwrap_i32()).unwrap(); 778 wmemcheck.set_stack_size(size); 779 } 780 } 781 782 let global = instance.as_mut().get_exported_global(id, index); 783 784 // Note that mutability is bypassed here because this is, by definition, 785 // initialization of globals meaning that if it's an immutable global 786 // this is the one and only write. 787 // 788 // SAFETY: this is a valid module so `val` should have the correct type 789 // for this global, and it's safe to write to a global for the first 790 // time as-is happening here. 791 unsafe { 792 global.set_unchecked(&mut store, &val)?; 793 } 794 } 795 Ok(()) 796 } 797 798 pub async fn initialize_instance( 799 store: &mut StoreOpaque, 800 mut limiter: Option<&mut StoreResourceLimiter<'_>>, 801 instance: InstanceId, 802 module: &Module, 803 is_bulk_memory: bool, 804 ) -> Result<()> { 805 // If bulk memory is not enabled, bounds check the data and element segments before 806 // making any changes. With bulk memory enabled, initializers are processed 807 // in-order and side effects are observed up to the point of an out-of-bounds 808 // initializer, so the early checking is not desired. 809 if !is_bulk_memory { 810 check_init_bounds(store, instance, module)?; 811 } 812 813 let mut context = ConstEvalContext::new(instance); 814 let mut const_evaluator = ConstExprEvaluator::default(); 815 816 initialize_globals( 817 store, 818 limiter.as_deref_mut(), 819 &mut context, 820 &mut const_evaluator, 821 module, 822 ) 823 .await?; 824 initialize_tables( 825 store, 826 limiter.as_deref_mut(), 827 &mut context, 828 &mut const_evaluator, 829 module, 830 ) 831 .await?; 832 initialize_memories(store, &mut context, &mut const_evaluator, &module)?; 833 834 Ok(()) 835 } 836 837 #[cfg(test)] 838 mod tests { 839 use super::*; 840 841 #[test] 842 fn allocator_traits_are_object_safe() { 843 fn _instance_allocator(_: &dyn InstanceAllocator) {} 844 } 845 } 846