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