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