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