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