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