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