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::{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, PoolingAllocatorMetrics, 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 /// Returns `Some(&PoolingInstanceAllocator)` if this is one. 291 #[cfg(feature = "pooling-allocator")] 292 fn as_pooling(&self) -> Option<&PoolingInstanceAllocator> { 293 None 294 } 295 } 296 297 impl dyn InstanceAllocator + '_ { 298 /// Allocates a fresh `InstanceHandle` for the `req` given. 299 /// 300 /// This will allocate memories and tables internally from this allocator 301 /// and weave that altogether into a final and complete `InstanceHandle` 302 /// ready to be registered with a store. 303 /// 304 /// Note that the returned instance must still have `.initialize(..)` called 305 /// on it to complete the instantiation process. 306 /// 307 /// # Safety 308 /// 309 /// The `request` provided must be valid, e.g. the imports within are 310 /// correctly sized/typed for the instance being created. 311 pub(crate) async unsafe fn allocate_module( 312 &self, 313 mut request: InstanceAllocationRequest<'_, '_>, 314 ) -> Result<InstanceHandle> { 315 let module = request.runtime_info.env_module(); 316 317 if cfg!(debug_assertions) { 318 InstanceAllocator::validate_module(self, module, request.runtime_info.offsets()) 319 .expect("module should have already been validated before allocation"); 320 } 321 322 self.increment_core_instance_count()?; 323 324 let num_defined_memories = module.num_defined_memories(); 325 let num_defined_tables = module.num_defined_tables(); 326 327 let mut guard = DeallocateOnDrop { 328 run_deallocate: true, 329 memories: PrimaryMap::with_capacity(num_defined_memories), 330 tables: PrimaryMap::with_capacity(num_defined_tables), 331 allocator: self, 332 }; 333 334 self.allocate_memories(&mut request, &mut guard.memories) 335 .await?; 336 self.allocate_tables(&mut request, &mut guard.tables) 337 .await?; 338 guard.run_deallocate = false; 339 // SAFETY: memories/tables were just allocated from the store within 340 // `request` and this function's own contract requires that the 341 // imports are valid. 342 return unsafe { 343 Ok(Instance::new( 344 request, 345 mem::take(&mut guard.memories), 346 mem::take(&mut guard.tables), 347 &module.memories, 348 )) 349 }; 350 351 struct DeallocateOnDrop<'a> { 352 run_deallocate: bool, 353 memories: PrimaryMap<DefinedMemoryIndex, (MemoryAllocationIndex, Memory)>, 354 tables: PrimaryMap<DefinedTableIndex, (TableAllocationIndex, Table)>, 355 allocator: &'a (dyn InstanceAllocator + 'a), 356 } 357 358 impl Drop for DeallocateOnDrop<'_> { 359 fn drop(&mut self) { 360 if !self.run_deallocate { 361 return; 362 } 363 // SAFETY: these were previously allocated by this allocator 364 unsafe { 365 self.allocator.deallocate_memories(&mut self.memories); 366 self.allocator.deallocate_tables(&mut self.tables); 367 } 368 self.allocator.decrement_core_instance_count(); 369 } 370 } 371 } 372 373 /// Deallocates the provided instance. 374 /// 375 /// This will null-out the pointer within `handle` and otherwise reclaim 376 /// resources such as tables, memories, and the instance memory itself. 377 /// 378 /// # Unsafety 379 /// 380 /// The instance must have previously been allocated by `Self::allocate`. 381 pub(crate) unsafe fn deallocate_module(&self, handle: &mut InstanceHandle) { 382 // SAFETY: the contract of `deallocate_*` is itself a contract of this 383 // function, that the memories/tables were previously allocated from 384 // here. 385 unsafe { 386 self.deallocate_memories(handle.get_mut().memories_mut()); 387 self.deallocate_tables(handle.get_mut().tables_mut()); 388 } 389 390 self.decrement_core_instance_count(); 391 } 392 393 /// Allocate the memories for the given instance allocation request, pushing 394 /// them into `memories`. 395 async fn allocate_memories( 396 &self, 397 request: &mut InstanceAllocationRequest<'_, '_>, 398 memories: &mut PrimaryMap<DefinedMemoryIndex, (MemoryAllocationIndex, Memory)>, 399 ) -> Result<()> { 400 let module = request.runtime_info.env_module(); 401 402 if cfg!(debug_assertions) { 403 InstanceAllocator::validate_module(self, module, request.runtime_info.offsets()) 404 .expect("module should have already been validated before allocation"); 405 } 406 407 for (memory_index, ty) in module.memories.iter().skip(module.num_imported_memories) { 408 let memory_index = module 409 .defined_memory_index(memory_index) 410 .expect("should be a defined memory since we skipped imported ones"); 411 412 let memory = self 413 .allocate_memory(request, ty, Some(memory_index)) 414 .await?; 415 memories.push(memory); 416 } 417 418 Ok(()) 419 } 420 421 /// Deallocate all the memories in the given primary map. 422 /// 423 /// # Unsafety 424 /// 425 /// The memories must have previously been allocated by 426 /// `Self::allocate_memories`. 427 unsafe fn deallocate_memories( 428 &self, 429 memories: &mut PrimaryMap<DefinedMemoryIndex, (MemoryAllocationIndex, Memory)>, 430 ) { 431 for (memory_index, (allocation_index, memory)) in mem::take(memories) { 432 // Because deallocating memory is infallible, we don't need to worry 433 // about leaking subsequent memories if the first memory failed to 434 // deallocate. If deallocating memory ever becomes fallible, we will 435 // need to be careful here! 436 // 437 // SAFETY: the unsafe contract here is the same as the unsafe 438 // contract of this function, that the memories were previously 439 // allocated by this allocator. 440 unsafe { 441 self.deallocate_memory(Some(memory_index), allocation_index, memory); 442 } 443 } 444 } 445 446 /// Allocate tables for the given instance allocation request, pushing them 447 /// into `tables`. 448 async fn allocate_tables( 449 &self, 450 request: &mut InstanceAllocationRequest<'_, '_>, 451 tables: &mut PrimaryMap<DefinedTableIndex, (TableAllocationIndex, Table)>, 452 ) -> Result<()> { 453 let module = request.runtime_info.env_module(); 454 455 if cfg!(debug_assertions) { 456 InstanceAllocator::validate_module(self, module, request.runtime_info.offsets()) 457 .expect("module should have already been validated before allocation"); 458 } 459 460 for (index, table) in module.tables.iter().skip(module.num_imported_tables) { 461 let def_index = module 462 .defined_table_index(index) 463 .expect("should be a defined table since we skipped imported ones"); 464 465 let table = self.allocate_table(request, table, def_index).await?; 466 tables.push(table); 467 } 468 469 Ok(()) 470 } 471 472 /// Deallocate all the tables in the given primary map. 473 /// 474 /// # Unsafety 475 /// 476 /// The tables must have previously been allocated by 477 /// `Self::allocate_tables`. 478 unsafe fn deallocate_tables( 479 &self, 480 tables: &mut PrimaryMap<DefinedTableIndex, (TableAllocationIndex, Table)>, 481 ) { 482 for (table_index, (allocation_index, table)) in mem::take(tables) { 483 // SAFETY: the tables here were allocated from this allocator per 484 // the contract on this function itself. 485 unsafe { 486 self.deallocate_table(table_index, allocation_index, table); 487 } 488 } 489 } 490 } 491 492 fn check_table_init_bounds( 493 store: &mut StoreOpaque, 494 instance: InstanceId, 495 module: &Module, 496 context: &mut ConstEvalContext, 497 const_evaluator: &mut ConstExprEvaluator, 498 ) -> Result<()> { 499 let mut store = OpaqueRootScope::new(store); 500 501 for segment in module.table_initialization.segments.iter() { 502 let start = const_evaluator 503 .eval_int(&mut store, context, &segment.offset) 504 .expect("const expression should be valid"); 505 let start = usize::try_from(start.unwrap_i32().cast_unsigned()).unwrap(); 506 let end = start.checked_add(usize::try_from(segment.elements.len()).unwrap()); 507 508 let table = store.instance_mut(instance).get_table(segment.table_index); 509 match end { 510 Some(end) if end <= table.size() => { 511 // Initializer is in bounds 512 } 513 _ => { 514 bail!("table out of bounds: elements segment does not fit") 515 } 516 } 517 } 518 519 Ok(()) 520 } 521 522 async fn initialize_tables( 523 store: &mut StoreOpaque, 524 mut limiter: Option<&mut StoreResourceLimiter<'_>>, 525 context: &mut ConstEvalContext, 526 const_evaluator: &mut ConstExprEvaluator, 527 module: &Module, 528 ) -> Result<()> { 529 let mut store = OpaqueRootScope::new(store); 530 for (table, init) in module.table_initialization.initial_values.iter() { 531 match init { 532 // Tables are always initially null-initialized at this time 533 TableInitialValue::Null { precomputed: _ } => {} 534 535 TableInitialValue::Expr(expr) => { 536 let init = const_evaluator 537 .eval(&mut store, limiter.as_deref_mut(), context, expr) 538 .await?; 539 let idx = module.table_index(table); 540 let id = store.id(); 541 let table = store 542 .instance_mut(context.instance) 543 .get_exported_table(id, idx); 544 let size = table._size(&store); 545 table._fill(&mut store, 0, init.ref_().unwrap(), size)?; 546 } 547 } 548 } 549 550 // Note: if the module's table initializer state is in 551 // FuncTable mode, we will lazily initialize tables based on 552 // any statically-precomputed image of FuncIndexes, but there 553 // may still be "leftover segments" that could not be 554 // incorporated. So we have a unified handler here that 555 // iterates over all segments (Segments mode) or leftover 556 // segments (FuncTable mode) to initialize. 557 for segment in module.table_initialization.segments.iter() { 558 let start = const_evaluator 559 .eval_int(&mut store, context, &segment.offset) 560 .expect("const expression should be valid"); 561 let start = get_index( 562 start, 563 store.instance(context.instance).env_module().tables[segment.table_index].idx_type, 564 ); 565 Instance::table_init_segment( 566 &mut store, 567 limiter.as_deref_mut(), 568 context.asyncness, 569 context.instance, 570 const_evaluator, 571 segment.table_index, 572 &segment.elements, 573 start, 574 0, 575 segment.elements.len(), 576 ) 577 .await?; 578 } 579 580 Ok(()) 581 } 582 583 fn get_index(val: &Val, ty: wasmtime_environ::IndexType) -> u64 { 584 match ty { 585 wasmtime_environ::IndexType::I32 => val.unwrap_i32().cast_unsigned().into(), 586 wasmtime_environ::IndexType::I64 => val.unwrap_i64().cast_unsigned(), 587 } 588 } 589 590 fn get_memory_init_start( 591 store: &mut StoreOpaque, 592 init: &MemoryInitializer, 593 instance: InstanceId, 594 context: &mut ConstEvalContext, 595 const_evaluator: &mut ConstExprEvaluator, 596 ) -> Result<u64> { 597 let mut store = OpaqueRootScope::new(store); 598 const_evaluator 599 .eval_int(&mut store, context, &init.offset) 600 .map(|v| { 601 get_index( 602 v, 603 store.instance(instance).env_module().memories[init.memory_index].idx_type, 604 ) 605 }) 606 } 607 608 fn check_memory_init_bounds( 609 store: &mut StoreOpaque, 610 instance: InstanceId, 611 initializers: &[MemoryInitializer], 612 context: &mut ConstEvalContext, 613 const_evaluator: &mut ConstExprEvaluator, 614 ) -> Result<()> { 615 for init in initializers { 616 let memory = store.instance_mut(instance).get_memory(init.memory_index); 617 let start = get_memory_init_start(store, init, instance, context, const_evaluator)?; 618 let end = usize::try_from(start) 619 .ok() 620 .and_then(|start| start.checked_add(init.data.len())); 621 622 match end { 623 Some(end) if end <= memory.current_length() => { 624 // Initializer is in bounds 625 } 626 _ => { 627 bail!("memory out of bounds: data segment does not fit") 628 } 629 } 630 } 631 632 Ok(()) 633 } 634 635 fn initialize_memories( 636 store: &mut StoreOpaque, 637 context: &mut ConstEvalContext, 638 const_evaluator: &mut ConstExprEvaluator, 639 module: &Module, 640 ) -> Result<()> { 641 // Delegates to the `init_memory` method which is sort of a duplicate of 642 // `instance.memory_init_segment` but is used at compile-time in other 643 // contexts so is shared here to have only one method of memory 644 // initialization. 645 // 646 // This call to `init_memory` notably implements all the bells and whistles 647 // so errors only happen if an out-of-bounds segment is found, in which case 648 // a trap is returned. 649 650 struct InitMemoryAtInstantiation<'a> { 651 module: &'a Module, 652 store: &'a mut StoreOpaque, 653 context: &'a mut ConstEvalContext, 654 const_evaluator: &'a mut ConstExprEvaluator, 655 } 656 657 impl InitMemory for InitMemoryAtInstantiation<'_> { 658 fn memory_size_in_bytes( 659 &mut self, 660 memory: wasmtime_environ::MemoryIndex, 661 ) -> Result<u64, SizeOverflow> { 662 let len = self 663 .store 664 .instance(self.context.instance) 665 .get_memory(memory) 666 .current_length(); 667 let len = u64::try_from(len).unwrap(); 668 Ok(len) 669 } 670 671 fn eval_offset( 672 &mut self, 673 memory: wasmtime_environ::MemoryIndex, 674 expr: &wasmtime_environ::ConstExpr, 675 ) -> Option<u64> { 676 let mut store = OpaqueRootScope::new(&mut *self.store); 677 let val = self 678 .const_evaluator 679 .eval_int(&mut store, self.context, expr) 680 .expect("const expression should be valid"); 681 Some(get_index( 682 val, 683 store.instance(self.context.instance).env_module().memories[memory].idx_type, 684 )) 685 } 686 687 fn write( 688 &mut self, 689 memory_index: wasmtime_environ::MemoryIndex, 690 init: &wasmtime_environ::StaticMemoryInitializer, 691 ) -> bool { 692 // If this initializer applies to a defined memory but that memory 693 // doesn't need initialization, due to something like copy-on-write 694 // pre-initializing it via mmap magic, then this initializer can be 695 // skipped entirely. 696 let instance = self.store.instance_mut(self.context.instance); 697 if let Some(memory_index) = self.module.defined_memory_index(memory_index) { 698 if !instance.memories[memory_index].1.needs_init() { 699 return true; 700 } 701 } 702 let memory = instance.get_memory(memory_index); 703 704 unsafe { 705 let src = instance.wasm_data(init.data.clone()); 706 let offset = usize::try_from(init.offset).unwrap(); 707 let dst = memory.base.as_ptr().add(offset); 708 709 assert!(offset + src.len() <= memory.current_length()); 710 711 // FIXME audit whether this is safe in the presence of shared 712 // memory 713 // (https://github.com/bytecodealliance/wasmtime/issues/4203). 714 ptr::copy_nonoverlapping(src.as_ptr(), dst, src.len()) 715 } 716 true 717 } 718 } 719 720 let ok = module 721 .memory_initialization 722 .init_memory(&mut InitMemoryAtInstantiation { 723 module, 724 store, 725 context, 726 const_evaluator, 727 }); 728 if !ok { 729 return Err(Trap::MemoryOutOfBounds.into()); 730 } 731 732 Ok(()) 733 } 734 735 fn check_init_bounds( 736 store: &mut StoreOpaque, 737 instance: InstanceId, 738 context: &mut ConstEvalContext, 739 const_evaluator: &mut ConstExprEvaluator, 740 module: &Module, 741 ) -> Result<()> { 742 check_table_init_bounds(store, instance, module, context, const_evaluator)?; 743 744 match &module.memory_initialization { 745 MemoryInitialization::Segmented(initializers) => { 746 check_memory_init_bounds(store, instance, initializers, context, const_evaluator)?; 747 } 748 // Statically validated already to have everything in-bounds. 749 MemoryInitialization::Static { .. } => {} 750 } 751 752 Ok(()) 753 } 754 755 async fn initialize_globals( 756 store: &mut StoreOpaque, 757 mut limiter: Option<&mut StoreResourceLimiter<'_>>, 758 context: &mut ConstEvalContext, 759 const_evaluator: &mut ConstExprEvaluator, 760 module: &Module, 761 ) -> Result<()> { 762 assert!(core::ptr::eq( 763 &**store.instance(context.instance).env_module(), 764 module 765 )); 766 767 let mut store = OpaqueRootScope::new(store); 768 769 for (index, init) in module.global_initializers.iter() { 770 // Attempt a simple, synchronous evaluation before hitting the 771 // general-purpose `.await` point below. This benchmarks ~15% faster in 772 // instantiation vs just falling through to `.await` below. 773 let val = if let Some(val) = const_evaluator.try_simple(init) { 774 val 775 } else { 776 const_evaluator 777 .eval(&mut store, limiter.as_deref_mut(), context, init) 778 .await? 779 }; 780 781 let id = store.id(); 782 let index = module.global_index(index); 783 let mut instance = store.instance_mut(context.instance); 784 785 #[cfg(feature = "wmemcheck")] 786 if index.as_u32() == 0 787 && module.globals[index].wasm_ty == wasmtime_environ::WasmValType::I32 788 { 789 if let Some(wmemcheck) = instance.as_mut().wmemcheck_state_mut() { 790 let size = usize::try_from(val.unwrap_i32()).unwrap(); 791 wmemcheck.set_stack_size(size); 792 } 793 } 794 795 let global = instance.as_mut().get_exported_global(id, index); 796 797 // Note that mutability is bypassed here because this is, by definition, 798 // initialization of globals meaning that if it's an immutable global 799 // this is the one and only write. 800 // 801 // SAFETY: this is a valid module so `val` should have the correct type 802 // for this global, and it's safe to write to a global for the first 803 // time as-is happening here. 804 unsafe { 805 global.set_unchecked(&mut store, &val)?; 806 } 807 } 808 Ok(()) 809 } 810 811 pub async fn initialize_instance( 812 store: &mut StoreOpaque, 813 mut limiter: Option<&mut StoreResourceLimiter<'_>>, 814 instance: InstanceId, 815 module: &Module, 816 is_bulk_memory: bool, 817 asyncness: Asyncness, 818 ) -> Result<()> { 819 let mut context = ConstEvalContext::new(instance, asyncness); 820 let mut const_evaluator = ConstExprEvaluator::default(); 821 822 // If bulk memory is not enabled, bounds check the data and element segments before 823 // making any changes. With bulk memory enabled, initializers are processed 824 // in-order and side effects are observed up to the point of an out-of-bounds 825 // initializer, so the early checking is not desired. 826 if !is_bulk_memory { 827 check_init_bounds(store, instance, &mut context, &mut const_evaluator, module)?; 828 } 829 830 initialize_globals( 831 store, 832 limiter.as_deref_mut(), 833 &mut context, 834 &mut const_evaluator, 835 module, 836 ) 837 .await?; 838 initialize_tables( 839 store, 840 limiter.as_deref_mut(), 841 &mut context, 842 &mut const_evaluator, 843 module, 844 ) 845 .await?; 846 initialize_memories(store, &mut context, &mut const_evaluator, &module)?; 847 848 Ok(()) 849 } 850 851 #[cfg(test)] 852 mod tests { 853 use super::*; 854 855 #[test] 856 fn allocator_traits_are_object_safe() { 857 fn _instance_allocator(_: &dyn InstanceAllocator) {} 858 } 859 } 860