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