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, VMFuncRef, VMGcRef, VMStore}; 9 use crate::store::{AutoAssertNoGc, InstanceId, StoreOpaque}; 10 use crate::vm::VMGlobalDefinition; 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, WasmHeapTopType, 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_attr(not(feature = "wmemcheck"), allow(dead_code))] 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 = 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 InstanceAllocatorImpl { 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_impl<'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_impl(&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_impl(&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 `InstanceAllocatorImpl` 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 /// 257 /// # Unsafety 258 /// 259 /// The memory and its associated module must have already been validated by 260 /// `Self::validate_memory` (or transtively via 261 /// `Self::validate_{module,component}`) and passed that validation. 262 unsafe fn allocate_memory( 263 &self, 264 request: &mut InstanceAllocationRequest, 265 ty: &wasmtime_environ::Memory, 266 tunables: &Tunables, 267 memory_index: Option<DefinedMemoryIndex>, 268 ) -> Result<(MemoryAllocationIndex, Memory)>; 269 270 /// Deallocate an instance's previously allocated memory. 271 /// 272 /// # Unsafety 273 /// 274 /// The memory must have previously been allocated by 275 /// `Self::allocate_memory`, be at the given index, and must currently be 276 /// allocated. It must never be used again. 277 unsafe fn deallocate_memory( 278 &self, 279 memory_index: Option<DefinedMemoryIndex>, 280 allocation_index: MemoryAllocationIndex, 281 memory: Memory, 282 ); 283 284 /// Allocate a table for an instance. 285 /// 286 /// # Unsafety 287 /// 288 /// The table and its associated module must have already been validated by 289 /// `Self::validate_module` and passed that validation. 290 unsafe fn allocate_table( 291 &self, 292 req: &mut InstanceAllocationRequest, 293 table: &wasmtime_environ::Table, 294 tunables: &Tunables, 295 table_index: DefinedTableIndex, 296 ) -> Result<(TableAllocationIndex, Table)>; 297 298 /// Deallocate an instance's previously allocated table. 299 /// 300 /// # Unsafety 301 /// 302 /// The table must have previously been allocated by `Self::allocate_table`, 303 /// be at the given index, and must currently be allocated. It must never be 304 /// used again. 305 unsafe fn deallocate_table( 306 &self, 307 table_index: DefinedTableIndex, 308 allocation_index: TableAllocationIndex, 309 table: Table, 310 ); 311 312 /// Allocates a fiber stack for calling async functions on. 313 #[cfg(feature = "async")] 314 fn allocate_fiber_stack(&self) -> Result<wasmtime_fiber::FiberStack>; 315 316 /// Deallocates a fiber stack that was previously allocated with 317 /// `allocate_fiber_stack`. 318 /// 319 /// # Safety 320 /// 321 /// The provided stack is required to have been allocated with 322 /// `allocate_fiber_stack`. 323 #[cfg(feature = "async")] 324 unsafe fn deallocate_fiber_stack(&self, stack: wasmtime_fiber::FiberStack); 325 326 /// Allocate a GC heap for allocating Wasm GC objects within. 327 #[cfg(feature = "gc")] 328 fn allocate_gc_heap( 329 &self, 330 engine: &crate::Engine, 331 gc_runtime: &dyn GcRuntime, 332 memory_alloc_index: MemoryAllocationIndex, 333 memory: Memory, 334 ) -> Result<(GcHeapAllocationIndex, Box<dyn GcHeap>)>; 335 336 /// Deallocate a GC heap that was previously allocated with 337 /// `allocate_gc_heap`. 338 #[cfg(feature = "gc")] 339 #[must_use = "it is the caller's responsibility to deallocate the GC heap's underlying memory \ 340 storage after the GC heap is deallocated"] 341 fn deallocate_gc_heap( 342 &self, 343 allocation_index: GcHeapAllocationIndex, 344 gc_heap: Box<dyn GcHeap>, 345 ) -> (MemoryAllocationIndex, Memory); 346 347 /// Purges all lingering resources related to `module` from within this 348 /// allocator. 349 /// 350 /// Primarily present for the pooling allocator to remove mappings of 351 /// this module from slots in linear memory. 352 fn purge_module(&self, module: CompiledModuleId); 353 354 /// Use the next available protection key. 355 /// 356 /// The pooling allocator can use memory protection keys (MPK) for 357 /// compressing the guard regions protecting against OOB. Each 358 /// pool-allocated store needs its own key. 359 fn next_available_pkey(&self) -> Option<ProtectionKey>; 360 361 /// Restrict access to memory regions protected by `pkey`. 362 /// 363 /// This is useful for the pooling allocator, which can use memory 364 /// protection keys (MPK). Note: this may still allow access to other 365 /// protection keys, such as the default kernel key; see implementations of 366 /// this. 367 fn restrict_to_pkey(&self, pkey: ProtectionKey); 368 369 /// Allow access to memory regions protected by any protection key. 370 fn allow_all_pkeys(&self); 371 } 372 373 /// A thing that can allocate instances. 374 /// 375 /// Don't implement this trait directly, instead implement 376 /// `InstanceAllocatorImpl` and you'll get this trait for free via a blanket 377 /// impl. 378 pub trait InstanceAllocator: InstanceAllocatorImpl { 379 /// Validate whether a component (including all of its contained core 380 /// modules) is allocatable with this instance allocator. 381 #[cfg(feature = "component-model")] 382 fn validate_component<'a>( 383 &self, 384 component: &Component, 385 offsets: &VMComponentOffsets<HostPtr>, 386 get_module: &'a dyn Fn(StaticModuleIndex) -> &'a Module, 387 ) -> Result<()> { 388 InstanceAllocatorImpl::validate_component_impl(self, component, offsets, get_module) 389 } 390 391 /// Validate whether a core module is allocatable with this instance 392 /// allocator. 393 fn validate_module(&self, module: &Module, offsets: &VMOffsets<HostPtr>) -> Result<()> { 394 InstanceAllocatorImpl::validate_module_impl(self, module, offsets) 395 } 396 397 /// Validate whether a memory is allocatable with this instance allocator. 398 #[cfg(feature = "gc")] 399 fn validate_memory(&self, memory: &wasmtime_environ::Memory) -> Result<()> { 400 InstanceAllocatorImpl::validate_memory_impl(self, memory) 401 } 402 403 /// Allocates a fresh `InstanceHandle` for the `req` given. 404 /// 405 /// This will allocate memories and tables internally from this allocator 406 /// and weave that altogether into a final and complete `InstanceHandle` 407 /// ready to be registered with a store. 408 /// 409 /// Note that the returned instance must still have `.initialize(..)` called 410 /// on it to complete the instantiation process. 411 /// 412 /// # Unsafety 413 /// 414 /// The request's associated module, memories, tables, and vmctx must have 415 /// already have been validated by `Self::validate_module`. 416 unsafe fn allocate_module( 417 &self, 418 mut request: InstanceAllocationRequest, 419 ) -> Result<InstanceHandle> { 420 let module = request.runtime_info.env_module(); 421 422 #[cfg(debug_assertions)] 423 InstanceAllocatorImpl::validate_module_impl(self, module, request.runtime_info.offsets()) 424 .expect("module should have already been validated before allocation"); 425 426 self.increment_core_instance_count()?; 427 428 let num_defined_memories = module.num_defined_memories(); 429 let mut memories = PrimaryMap::with_capacity(num_defined_memories); 430 431 let num_defined_tables = module.num_defined_tables(); 432 let mut tables = PrimaryMap::with_capacity(num_defined_tables); 433 434 match (|| { 435 self.allocate_memories(&mut request, &mut memories)?; 436 self.allocate_tables(&mut request, &mut tables)?; 437 Ok(()) 438 })() { 439 Ok(_) => Ok(Instance::new(request, memories, tables, &module.memories)), 440 Err(e) => { 441 self.deallocate_memories(&mut memories); 442 self.deallocate_tables(&mut tables); 443 self.decrement_core_instance_count(); 444 Err(e) 445 } 446 } 447 } 448 449 /// Deallocates the provided instance. 450 /// 451 /// This will null-out the pointer within `handle` and otherwise reclaim 452 /// resources such as tables, memories, and the instance memory itself. 453 /// 454 /// # Unsafety 455 /// 456 /// The instance must have previously been allocated by `Self::allocate`. 457 unsafe fn deallocate_module(&self, handle: &mut InstanceHandle) { 458 self.deallocate_memories(handle.get_mut().memories_mut()); 459 self.deallocate_tables(handle.get_mut().tables_mut()); 460 461 self.decrement_core_instance_count(); 462 } 463 464 /// Allocate the memories for the given instance allocation request, pushing 465 /// them into `memories`. 466 /// 467 /// # Unsafety 468 /// 469 /// The request's associated module and memories must have previously been 470 /// validated by `Self::validate_module`. 471 unsafe fn allocate_memories( 472 &self, 473 request: &mut InstanceAllocationRequest, 474 memories: &mut PrimaryMap<DefinedMemoryIndex, (MemoryAllocationIndex, Memory)>, 475 ) -> Result<()> { 476 let module = request.runtime_info.env_module(); 477 478 #[cfg(debug_assertions)] 479 InstanceAllocatorImpl::validate_module_impl(self, module, request.runtime_info.offsets()) 480 .expect("module should have already been validated before allocation"); 481 482 for (memory_index, ty) in module.memories.iter().skip(module.num_imported_memories) { 483 let memory_index = module 484 .defined_memory_index(memory_index) 485 .expect("should be a defined memory since we skipped imported ones"); 486 487 memories.push(self.allocate_memory( 488 request, 489 ty, 490 request.tunables, 491 Some(memory_index), 492 )?); 493 } 494 495 Ok(()) 496 } 497 498 /// Deallocate all the memories in the given primary map. 499 /// 500 /// # Unsafety 501 /// 502 /// The memories must have previously been allocated by 503 /// `Self::allocate_memories`. 504 unsafe fn deallocate_memories( 505 &self, 506 memories: &mut PrimaryMap<DefinedMemoryIndex, (MemoryAllocationIndex, Memory)>, 507 ) { 508 for (memory_index, (allocation_index, memory)) in mem::take(memories) { 509 // Because deallocating memory is infallible, we don't need to worry 510 // about leaking subsequent memories if the first memory failed to 511 // deallocate. If deallocating memory ever becomes fallible, we will 512 // need to be careful here! 513 self.deallocate_memory(Some(memory_index), allocation_index, memory); 514 } 515 } 516 517 /// Allocate tables for the given instance allocation request, pushing them 518 /// into `tables`. 519 /// 520 /// # Unsafety 521 /// 522 /// The request's associated module and tables must have previously been 523 /// validated by `Self::validate_module`. 524 unsafe fn allocate_tables( 525 &self, 526 request: &mut InstanceAllocationRequest, 527 tables: &mut PrimaryMap<DefinedTableIndex, (TableAllocationIndex, Table)>, 528 ) -> Result<()> { 529 let module = request.runtime_info.env_module(); 530 531 #[cfg(debug_assertions)] 532 InstanceAllocatorImpl::validate_module_impl(self, module, request.runtime_info.offsets()) 533 .expect("module should have already been validated before allocation"); 534 535 for (index, table) in module.tables.iter().skip(module.num_imported_tables) { 536 let def_index = module 537 .defined_table_index(index) 538 .expect("should be a defined table since we skipped imported ones"); 539 540 tables.push(self.allocate_table(request, table, request.tunables, def_index)?); 541 } 542 543 Ok(()) 544 } 545 546 /// Deallocate all the tables in the given primary map. 547 /// 548 /// # Unsafety 549 /// 550 /// The tables must have previously been allocated by 551 /// `Self::allocate_tables`. 552 unsafe fn deallocate_tables( 553 &self, 554 tables: &mut PrimaryMap<DefinedTableIndex, (TableAllocationIndex, Table)>, 555 ) { 556 for (table_index, (allocation_index, table)) in mem::take(tables) { 557 self.deallocate_table(table_index, allocation_index, table); 558 } 559 } 560 } 561 562 // Every `InstanceAllocatorImpl` is an `InstanceAllocator` when used 563 // correctly. Also, no one is allowed to override this trait's methods, they 564 // must use the defaults. This blanket impl provides both of those things. 565 impl<T: InstanceAllocatorImpl> InstanceAllocator for T {} 566 567 fn check_table_init_bounds( 568 store: &mut StoreOpaque, 569 instance: InstanceId, 570 module: &Module, 571 ) -> Result<()> { 572 let mut const_evaluator = ConstExprEvaluator::default(); 573 574 for segment in module.table_initialization.segments.iter() { 575 let table = unsafe { &*store.instance_mut(instance).get_table(segment.table_index) }; 576 let mut context = ConstEvalContext::new(instance); 577 let start = unsafe { 578 const_evaluator 579 .eval(store, &mut context, &segment.offset) 580 .expect("const expression should be valid") 581 }; 582 let start = usize::try_from(start.get_u32()).unwrap(); 583 let end = start.checked_add(usize::try_from(segment.elements.len()).unwrap()); 584 585 match end { 586 Some(end) if end <= table.size() => { 587 // Initializer is in bounds 588 } 589 _ => { 590 bail!("table out of bounds: elements segment does not fit") 591 } 592 } 593 } 594 595 Ok(()) 596 } 597 598 fn initialize_tables( 599 store: &mut StoreOpaque, 600 context: &mut ConstEvalContext, 601 const_evaluator: &mut ConstExprEvaluator, 602 module: &Module, 603 ) -> Result<()> { 604 for (table, init) in module.table_initialization.initial_values.iter() { 605 match init { 606 // Tables are always initially null-initialized at this time 607 TableInitialValue::Null { precomputed: _ } => {} 608 609 TableInitialValue::Expr(expr) => { 610 let raw = unsafe { 611 const_evaluator 612 .eval(store, context, expr) 613 .expect("const expression should be valid") 614 }; 615 let idx = module.table_index(table); 616 let table = unsafe { 617 store 618 .instance_mut(context.instance) 619 .get_defined_table(table) 620 .as_mut() 621 .unwrap() 622 }; 623 match module.tables[idx].ref_type.heap_type.top() { 624 WasmHeapTopType::Extern => { 625 let gc_ref = VMGcRef::from_raw_u32(raw.get_externref()); 626 let gc_store = store.gc_store_mut()?; 627 let items = (0..table.size()) 628 .map(|_| gc_ref.as_ref().map(|r| gc_store.clone_gc_ref(r))); 629 table.init_gc_refs(0, items)?; 630 } 631 632 WasmHeapTopType::Any => { 633 let gc_ref = VMGcRef::from_raw_u32(raw.get_anyref()); 634 let gc_store = store.gc_store_mut()?; 635 let items = (0..table.size()) 636 .map(|_| gc_ref.as_ref().map(|r| gc_store.clone_gc_ref(r))); 637 table.init_gc_refs(0, items)?; 638 } 639 640 WasmHeapTopType::Func => { 641 let funcref = NonNull::new(raw.get_funcref().cast::<VMFuncRef>()); 642 let items = (0..table.size()).map(|_| funcref); 643 table.init_func(0, items)?; 644 } 645 646 WasmHeapTopType::Cont => todo!(), // FIXME: #10248 stack switching support. 647 } 648 } 649 } 650 } 651 652 // Note: if the module's table initializer state is in 653 // FuncTable mode, we will lazily initialize tables based on 654 // any statically-precomputed image of FuncIndexes, but there 655 // may still be "leftover segments" that could not be 656 // incorporated. So we have a unified handler here that 657 // iterates over all segments (Segments mode) or leftover 658 // segments (FuncTable mode) to initialize. 659 for segment in module.table_initialization.segments.iter() { 660 let start = unsafe { 661 const_evaluator 662 .eval(store, context, &segment.offset) 663 .expect("const expression should be valid") 664 }; 665 Instance::table_init_segment( 666 store, 667 context.instance, 668 const_evaluator, 669 segment.table_index, 670 &segment.elements, 671 start.get_u64(), 672 0, 673 segment.elements.len(), 674 )?; 675 } 676 677 Ok(()) 678 } 679 680 fn get_memory_init_start( 681 store: &mut StoreOpaque, 682 init: &MemoryInitializer, 683 instance: InstanceId, 684 ) -> Result<u64> { 685 let mut context = ConstEvalContext::new(instance); 686 let mut const_evaluator = ConstExprEvaluator::default(); 687 unsafe { const_evaluator.eval(store, &mut context, &init.offset) }.map(|v| { 688 match store.instance(instance).env_module().memories[init.memory_index].idx_type { 689 wasmtime_environ::IndexType::I32 => v.get_u32().into(), 690 wasmtime_environ::IndexType::I64 => v.get_u64(), 691 } 692 }) 693 } 694 695 fn check_memory_init_bounds( 696 store: &mut StoreOpaque, 697 instance: InstanceId, 698 initializers: &[MemoryInitializer], 699 ) -> Result<()> { 700 for init in initializers { 701 let memory = store.instance_mut(instance).get_memory(init.memory_index); 702 let start = get_memory_init_start(store, init, instance)?; 703 let end = usize::try_from(start) 704 .ok() 705 .and_then(|start| start.checked_add(init.data.len())); 706 707 match end { 708 Some(end) if end <= memory.current_length() => { 709 // Initializer is in bounds 710 } 711 _ => { 712 bail!("memory out of bounds: data segment does not fit") 713 } 714 } 715 } 716 717 Ok(()) 718 } 719 720 fn initialize_memories( 721 store: &mut StoreOpaque, 722 context: &mut ConstEvalContext, 723 const_evaluator: &mut ConstExprEvaluator, 724 module: &Module, 725 ) -> Result<()> { 726 // Delegates to the `init_memory` method which is sort of a duplicate of 727 // `instance.memory_init_segment` but is used at compile-time in other 728 // contexts so is shared here to have only one method of memory 729 // initialization. 730 // 731 // This call to `init_memory` notably implements all the bells and whistles 732 // so errors only happen if an out-of-bounds segment is found, in which case 733 // a trap is returned. 734 735 struct InitMemoryAtInstantiation<'a> { 736 module: &'a Module, 737 store: &'a mut StoreOpaque, 738 context: &'a mut ConstEvalContext, 739 const_evaluator: &'a mut ConstExprEvaluator, 740 } 741 742 impl InitMemory for InitMemoryAtInstantiation<'_> { 743 fn memory_size_in_bytes( 744 &mut self, 745 memory: wasmtime_environ::MemoryIndex, 746 ) -> Result<u64, SizeOverflow> { 747 let len = self 748 .store 749 .instance(self.context.instance) 750 .get_memory(memory) 751 .current_length(); 752 let len = u64::try_from(len).unwrap(); 753 Ok(len) 754 } 755 756 fn eval_offset( 757 &mut self, 758 memory: wasmtime_environ::MemoryIndex, 759 expr: &wasmtime_environ::ConstExpr, 760 ) -> Option<u64> { 761 let val = unsafe { self.const_evaluator.eval(self.store, self.context, expr) } 762 .expect("const expression should be valid"); 763 Some( 764 match self 765 .store 766 .instance(self.context.instance) 767 .env_module() 768 .memories[memory] 769 .idx_type 770 { 771 wasmtime_environ::IndexType::I32 => val.get_u32().into(), 772 wasmtime_environ::IndexType::I64 => val.get_u64(), 773 }, 774 ) 775 } 776 777 fn write( 778 &mut self, 779 memory_index: wasmtime_environ::MemoryIndex, 780 init: &wasmtime_environ::StaticMemoryInitializer, 781 ) -> bool { 782 // If this initializer applies to a defined memory but that memory 783 // doesn't need initialization, due to something like copy-on-write 784 // pre-initializing it via mmap magic, then this initializer can be 785 // skipped entirely. 786 let instance = self.store.instance_mut(self.context.instance); 787 if let Some(memory_index) = self.module.defined_memory_index(memory_index) { 788 if !instance.memories[memory_index].1.needs_init() { 789 return true; 790 } 791 } 792 let memory = instance.get_memory(memory_index); 793 794 unsafe { 795 let src = instance.wasm_data(init.data.clone()); 796 let offset = usize::try_from(init.offset).unwrap(); 797 let dst = memory.base.as_ptr().add(offset); 798 799 assert!(offset + src.len() <= memory.current_length()); 800 801 // FIXME audit whether this is safe in the presence of shared 802 // memory 803 // (https://github.com/bytecodealliance/wasmtime/issues/4203). 804 ptr::copy_nonoverlapping(src.as_ptr(), dst, src.len()) 805 } 806 true 807 } 808 } 809 810 let ok = module 811 .memory_initialization 812 .init_memory(&mut InitMemoryAtInstantiation { 813 module, 814 store, 815 context, 816 const_evaluator, 817 }); 818 if !ok { 819 return Err(Trap::MemoryOutOfBounds.into()); 820 } 821 822 Ok(()) 823 } 824 825 fn check_init_bounds(store: &mut StoreOpaque, instance: InstanceId, module: &Module) -> Result<()> { 826 check_table_init_bounds(store, instance, module)?; 827 828 match &module.memory_initialization { 829 MemoryInitialization::Segmented(initializers) => { 830 check_memory_init_bounds(store, instance, initializers)?; 831 } 832 // Statically validated already to have everything in-bounds. 833 MemoryInitialization::Static { .. } => {} 834 } 835 836 Ok(()) 837 } 838 839 fn initialize_globals( 840 store: &mut StoreOpaque, 841 context: &mut ConstEvalContext, 842 const_evaluator: &mut ConstExprEvaluator, 843 module: &Module, 844 ) -> Result<()> { 845 assert!(core::ptr::eq( 846 &**store.instance(context.instance).env_module(), 847 module 848 )); 849 850 let mut store = AutoAssertNoGc::new(store); 851 852 for (index, init) in module.global_initializers.iter() { 853 let raw = unsafe { 854 const_evaluator 855 .eval(&mut store, context, init) 856 .expect("should be a valid const expr") 857 }; 858 859 let instance = store.instance_mut(context.instance); 860 let to = instance.global_ptr(index); 861 let wasm_ty = module.globals[module.global_index(index)].wasm_ty; 862 863 #[cfg(feature = "wmemcheck")] 864 if index.as_u32() == 0 && wasm_ty == wasmtime_environ::WasmValType::I32 { 865 if let Some(wmemcheck) = instance.wmemcheck_state_mut() { 866 let size = usize::try_from(raw.get_i32()).unwrap(); 867 wmemcheck.set_stack_size(size); 868 } 869 } 870 871 // This write is safe because we know we have the correct module for 872 // this instance and its vmctx due to the assert above. 873 unsafe { 874 to.write(VMGlobalDefinition::from_val_raw(&mut store, wasm_ty, raw)?); 875 }; 876 } 877 Ok(()) 878 } 879 880 pub fn initialize_instance( 881 store: &mut StoreOpaque, 882 instance: InstanceId, 883 module: &Module, 884 is_bulk_memory: bool, 885 ) -> Result<()> { 886 // If bulk memory is not enabled, bounds check the data and element segments before 887 // making any changes. With bulk memory enabled, initializers are processed 888 // in-order and side effects are observed up to the point of an out-of-bounds 889 // initializer, so the early checking is not desired. 890 if !is_bulk_memory { 891 check_init_bounds(store, instance, module)?; 892 } 893 894 let mut context = ConstEvalContext::new(instance); 895 let mut const_evaluator = ConstExprEvaluator::default(); 896 897 initialize_globals(store, &mut context, &mut const_evaluator, module)?; 898 initialize_tables(store, &mut context, &mut const_evaluator, module)?; 899 initialize_memories(store, &mut context, &mut const_evaluator, &module)?; 900 901 Ok(()) 902 } 903 904 #[cfg(test)] 905 mod tests { 906 use super::*; 907 908 #[test] 909 fn allocator_traits_are_object_safe() { 910 fn _instance_allocator(_: &dyn InstanceAllocatorImpl) {} 911 fn _instance_allocator_ext(_: &dyn InstanceAllocator) {} 912 } 913 } 914