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