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