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