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