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