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