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