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