1 //! Wasmtime's "store" type 2 //! 3 //! This module, and its submodules, contain the `Store` type and various types 4 //! used to interact with it. At first glance this is a pretty confusing module 5 //! where you need to know the difference between: 6 //! 7 //! * `Store<T>` 8 //! * `StoreContext<T>` 9 //! * `StoreContextMut<T>` 10 //! * `AsContext` 11 //! * `AsContextMut` 12 //! * `StoreInner<T>` 13 //! * `StoreOpaque` 14 //! * `StoreData` 15 //! 16 //! There's... quite a lot going on here, and it's easy to be confused. This 17 //! comment is ideally going to serve the purpose of clarifying what all these 18 //! types are for and why they're motivated. 19 //! 20 //! First it's important to know what's "internal" and what's "external". Almost 21 //! everything above is defined as `pub`, but only some of the items are 22 //! reexported to the outside world to be usable from this crate. Otherwise all 23 //! items are `pub` within this `store` module, and the `store` module is 24 //! private to the `wasmtime` crate. Notably `Store<T>`, `StoreContext<T>`, 25 //! `StoreContextMut<T>`, `AsContext`, and `AsContextMut` are all public 26 //! interfaces to the `wasmtime` crate. You can think of these as: 27 //! 28 //! * `Store<T>` - an owned reference to a store, the "root of everything" 29 //! * `StoreContext<T>` - basically `&StoreInner<T>` 30 //! * `StoreContextMut<T>` - more-or-less `&mut StoreInner<T>` with caveats. 31 //! Explained later. 32 //! * `AsContext` - similar to `AsRef`, but produces `StoreContext<T>` 33 //! * `AsContextMut` - similar to `AsMut`, but produces `StoreContextMut<T>` 34 //! 35 //! Next comes the internal structure of the `Store<T>` itself. This looks like: 36 //! 37 //! * `Store<T>` - this type is just a pointer large. It's primarily just 38 //! intended to be consumed by the outside world. Note that the "just a 39 //! pointer large" is a load-bearing implementation detail in Wasmtime. This 40 //! enables it to store a pointer to its own trait object which doesn't need 41 //! to change over time. 42 //! 43 //! * `StoreInner<T>` - the first layer of the contents of a `Store<T>`, what's 44 //! stored inside the `Box`. This is the general Rust pattern when one struct 45 //! is a layer over another. The surprising part, though, is that this is 46 //! further subdivided. This structure only contains things which actually 47 //! need `T` itself. The downside of this structure is that it's always 48 //! generic and means that code is monomorphized into consumer crates. We 49 //! strive to have things be as monomorphic as possible in `wasmtime` so this 50 //! type is not heavily used. 51 //! 52 //! * `StoreOpaque` - this is the primary contents of the `StoreInner<T>` type. 53 //! Stored inline in the outer type the "opaque" here means that it's a 54 //! "store" but it doesn't have access to the `T`. This is the primary 55 //! "internal" reference that Wasmtime uses since `T` is rarely needed by the 56 //! internals of Wasmtime. 57 //! 58 //! * `StoreData` - this is a final helper struct stored within `StoreOpaque`. 59 //! All references of Wasm items into a `Store` are actually indices into a 60 //! table in this structure, and the `StoreData` being separate makes it a bit 61 //! easier to manage/define/work with. There's no real fundamental reason this 62 //! is split out, although sometimes it's useful to have separate borrows into 63 //! these tables than the `StoreOpaque`. 64 //! 65 //! A major caveat with these representations is that the internal `&mut 66 //! StoreInner<T>` is never handed out publicly to consumers of this crate, only 67 //! through a wrapper of `StoreContextMut<'_, T>`. The reason for this is that 68 //! we want to provide mutable, but not destructive, access to the contents of a 69 //! `Store`. For example if a `StoreInner<T>` were replaced with some other 70 //! `StoreInner<T>` then that would drop live instances, possibly those 71 //! currently executing beneath the current stack frame. This would not be a 72 //! safe operation. 73 //! 74 //! This means, though, that the `wasmtime` crate, which liberally uses `&mut 75 //! StoreOpaque` internally, has to be careful to never actually destroy the 76 //! contents of `StoreOpaque`. This is an invariant that we, as the authors of 77 //! `wasmtime`, must uphold for the public interface to be safe. 78 79 use crate::RootSet; 80 use crate::module::RegisteredModuleId; 81 use crate::prelude::*; 82 #[cfg(feature = "gc")] 83 use crate::runtime::vm::GcRootsList; 84 #[cfg(feature = "stack-switching")] 85 use crate::runtime::vm::VMContRef; 86 use crate::runtime::vm::mpk::ProtectionKey; 87 use crate::runtime::vm::{ 88 self, GcStore, Imports, InstanceAllocationRequest, InstanceAllocator, InstanceHandle, 89 Interpreter, InterpreterRef, ModuleRuntimeInfo, OnDemandInstanceAllocator, SendSyncPtr, 90 SignalHandler, StoreBox, StorePtr, Unwind, VMContext, VMFuncRef, VMGcRef, VMStoreContext, 91 }; 92 use crate::trampoline::VMHostGlobalContext; 93 use crate::{Engine, Module, Trap, Val, ValRaw, module::ModuleRegistry}; 94 use crate::{Global, Instance, Memory, Table, Uninhabited}; 95 use alloc::sync::Arc; 96 use core::fmt; 97 use core::marker; 98 use core::mem::{self, ManuallyDrop}; 99 use core::num::NonZeroU64; 100 use core::ops::{Deref, DerefMut}; 101 use core::pin::Pin; 102 use core::ptr::NonNull; 103 use wasmtime_environ::{DefinedGlobalIndex, DefinedTableIndex, EntityRef, PrimaryMap, TripleExt}; 104 105 mod context; 106 pub use self::context::*; 107 mod data; 108 pub use self::data::*; 109 mod func_refs; 110 use func_refs::FuncRefs; 111 #[cfg(feature = "async")] 112 mod async_; 113 #[cfg(all(feature = "async", feature = "call-hook"))] 114 pub use self::async_::CallHookHandler; 115 #[cfg(feature = "async")] 116 use self::async_::*; 117 #[cfg(feature = "gc")] 118 mod gc; 119 120 /// A [`Store`] is a collection of WebAssembly instances and host-defined state. 121 /// 122 /// All WebAssembly instances and items will be attached to and refer to a 123 /// [`Store`]. For example instances, functions, globals, and tables are all 124 /// attached to a [`Store`]. Instances are created by instantiating a 125 /// [`Module`](crate::Module) within a [`Store`]. 126 /// 127 /// A [`Store`] is intended to be a short-lived object in a program. No form 128 /// of GC is implemented at this time so once an instance is created within a 129 /// [`Store`] it will not be deallocated until the [`Store`] itself is dropped. 130 /// This makes [`Store`] unsuitable for creating an unbounded number of 131 /// instances in it because [`Store`] will never release this memory. It's 132 /// recommended to have a [`Store`] correspond roughly to the lifetime of a 133 /// "main instance" that an embedding is interested in executing. 134 /// 135 /// ## Type parameter `T` 136 /// 137 /// Each [`Store`] has a type parameter `T` associated with it. This `T` 138 /// represents state defined by the host. This state will be accessible through 139 /// the [`Caller`](crate::Caller) type that host-defined functions get access 140 /// to. This `T` is suitable for storing `Store`-specific information which 141 /// imported functions may want access to. 142 /// 143 /// The data `T` can be accessed through methods like [`Store::data`] and 144 /// [`Store::data_mut`]. 145 /// 146 /// ## Stores, contexts, oh my 147 /// 148 /// Most methods in Wasmtime take something of the form 149 /// [`AsContext`](crate::AsContext) or [`AsContextMut`](crate::AsContextMut) as 150 /// the first argument. These two traits allow ergonomically passing in the 151 /// context you currently have to any method. The primary two sources of 152 /// contexts are: 153 /// 154 /// * `Store<T>` 155 /// * `Caller<'_, T>` 156 /// 157 /// corresponding to what you create and what you have access to in a host 158 /// function. You can also explicitly acquire a [`StoreContext`] or 159 /// [`StoreContextMut`] and pass that around as well. 160 /// 161 /// Note that all methods on [`Store`] are mirrored onto [`StoreContext`], 162 /// [`StoreContextMut`], and [`Caller`](crate::Caller). This way no matter what 163 /// form of context you have you can call various methods, create objects, etc. 164 /// 165 /// ## Stores and `Default` 166 /// 167 /// You can create a store with default configuration settings using 168 /// `Store::default()`. This will create a brand new [`Engine`] with default 169 /// configuration (see [`Config`](crate::Config) for more information). 170 /// 171 /// ## Cross-store usage of items 172 /// 173 /// In `wasmtime` wasm items such as [`Global`] and [`Memory`] "belong" to a 174 /// [`Store`]. The store they belong to is the one they were created with 175 /// (passed in as a parameter) or instantiated with. This store is the only 176 /// store that can be used to interact with wasm items after they're created. 177 /// 178 /// The `wasmtime` crate will panic if the [`Store`] argument passed in to these 179 /// operations is incorrect. In other words it's considered a programmer error 180 /// rather than a recoverable error for the wrong [`Store`] to be used when 181 /// calling APIs. 182 pub struct Store<T: 'static> { 183 // for comments about `ManuallyDrop`, see `Store::into_data` 184 inner: ManuallyDrop<Box<StoreInner<T>>>, 185 } 186 187 #[derive(Copy, Clone, Debug)] 188 /// Passed to the argument of [`Store::call_hook`] to indicate a state transition in 189 /// the WebAssembly VM. 190 pub enum CallHook { 191 /// Indicates the VM is calling a WebAssembly function, from the host. 192 CallingWasm, 193 /// Indicates the VM is returning from a WebAssembly function, to the host. 194 ReturningFromWasm, 195 /// Indicates the VM is calling a host function, from WebAssembly. 196 CallingHost, 197 /// Indicates the VM is returning from a host function, to WebAssembly. 198 ReturningFromHost, 199 } 200 201 impl CallHook { 202 /// Indicates the VM is entering host code (exiting WebAssembly code) 203 pub fn entering_host(&self) -> bool { 204 match self { 205 CallHook::ReturningFromWasm | CallHook::CallingHost => true, 206 _ => false, 207 } 208 } 209 /// Indicates the VM is exiting host code (entering WebAssembly code) 210 pub fn exiting_host(&self) -> bool { 211 match self { 212 CallHook::ReturningFromHost | CallHook::CallingWasm => true, 213 _ => false, 214 } 215 } 216 } 217 218 /// Internal contents of a `Store<T>` that live on the heap. 219 /// 220 /// The members of this struct are those that need to be generic over `T`, the 221 /// store's internal type storage. Otherwise all things that don't rely on `T` 222 /// should go into `StoreOpaque`. 223 pub struct StoreInner<T: 'static> { 224 /// Generic metadata about the store that doesn't need access to `T`. 225 inner: StoreOpaque, 226 227 limiter: Option<ResourceLimiterInner<T>>, 228 call_hook: Option<CallHookInner<T>>, 229 #[cfg(target_has_atomic = "64")] 230 epoch_deadline_behavior: 231 Option<Box<dyn FnMut(StoreContextMut<T>) -> Result<UpdateDeadline> + Send + Sync>>, 232 // for comments about `ManuallyDrop`, see `Store::into_data` 233 data: ManuallyDrop<T>, 234 } 235 236 enum ResourceLimiterInner<T> { 237 Sync(Box<dyn FnMut(&mut T) -> &mut (dyn crate::ResourceLimiter) + Send + Sync>), 238 #[cfg(feature = "async")] 239 Async(Box<dyn FnMut(&mut T) -> &mut (dyn crate::ResourceLimiterAsync) + Send + Sync>), 240 } 241 242 enum CallHookInner<T: 'static> { 243 #[cfg(feature = "call-hook")] 244 Sync(Box<dyn FnMut(StoreContextMut<'_, T>, CallHook) -> Result<()> + Send + Sync>), 245 #[cfg(all(feature = "async", feature = "call-hook"))] 246 Async(Box<dyn CallHookHandler<T> + Send + Sync>), 247 #[allow(dead_code)] 248 ForceTypeParameterToBeUsed { 249 uninhabited: Uninhabited, 250 _marker: marker::PhantomData<T>, 251 }, 252 } 253 254 /// What to do after returning from a callback when the engine epoch reaches 255 /// the deadline for a Store during execution of a function using that store. 256 #[non_exhaustive] 257 pub enum UpdateDeadline { 258 /// Extend the deadline by the specified number of ticks. 259 Continue(u64), 260 /// Extend the deadline by the specified number of ticks after yielding to 261 /// the async executor loop. This can only be used with an async [`Store`] 262 /// configured via [`Config::async_support`](crate::Config::async_support). 263 #[cfg(feature = "async")] 264 Yield(u64), 265 /// Extend the deadline by the specified number of ticks after yielding to 266 /// the async executor loop. This can only be used with an async [`Store`] 267 /// configured via [`Config::async_support`](crate::Config::async_support). 268 /// 269 /// The yield will be performed by the future provided; when using `tokio` 270 /// it is recommended to provide [`tokio::task::yield_now`](https://docs.rs/tokio/latest/tokio/task/fn.yield_now.html) 271 /// here. 272 #[cfg(feature = "async")] 273 YieldCustom( 274 u64, 275 ::core::pin::Pin<Box<dyn ::core::future::Future<Output = ()> + Send>>, 276 ), 277 } 278 279 // Forward methods on `StoreOpaque` to also being on `StoreInner<T>` 280 impl<T> Deref for StoreInner<T> { 281 type Target = StoreOpaque; 282 fn deref(&self) -> &Self::Target { 283 &self.inner 284 } 285 } 286 287 impl<T> DerefMut for StoreInner<T> { 288 fn deref_mut(&mut self) -> &mut Self::Target { 289 &mut self.inner 290 } 291 } 292 293 /// Monomorphic storage for a `Store<T>`. 294 /// 295 /// This structure contains the bulk of the metadata about a `Store`. This is 296 /// used internally in Wasmtime when dependence on the `T` of `Store<T>` isn't 297 /// necessary, allowing code to be monomorphic and compiled into the `wasmtime` 298 /// crate itself. 299 pub struct StoreOpaque { 300 // This `StoreOpaque` structure has references to itself. These aren't 301 // immediately evident, however, so we need to tell the compiler that it 302 // contains self-references. This notably suppresses `noalias` annotations 303 // when this shows up in compiled code because types of this structure do 304 // indeed alias itself. An example of this is `default_callee` holds a 305 // `*mut dyn Store` to the address of this `StoreOpaque` itself, indeed 306 // aliasing! 307 // 308 // It's somewhat unclear to me at this time if this is 100% sufficient to 309 // get all the right codegen in all the right places. For example does 310 // `Store` need to internally contain a `Pin<Box<StoreInner<T>>>`? Do the 311 // contexts need to contain `Pin<&mut StoreInner<T>>`? I'm not familiar 312 // enough with `Pin` to understand if it's appropriate here (we do, for 313 // example want to allow movement in and out of `data: T`, just not movement 314 // of most of the other members). It's also not clear if using `Pin` in a 315 // few places buys us much other than a bunch of `unsafe` that we already 316 // sort of hand-wave away. 317 // 318 // In any case this seems like a good mid-ground for now where we're at 319 // least telling the compiler something about all the aliasing happening 320 // within a `Store`. 321 _marker: marker::PhantomPinned, 322 323 engine: Engine, 324 vm_store_context: VMStoreContext, 325 326 // Contains all continuations ever allocated throughout the lifetime of this 327 // store. 328 #[cfg(feature = "stack-switching")] 329 continuations: Vec<Box<VMContRef>>, 330 331 instances: PrimaryMap<InstanceId, StoreInstance>, 332 333 #[cfg(feature = "component-model")] 334 num_component_instances: usize, 335 signal_handler: Option<SignalHandler>, 336 modules: ModuleRegistry, 337 func_refs: FuncRefs, 338 host_globals: PrimaryMap<DefinedGlobalIndex, StoreBox<VMHostGlobalContext>>, 339 // GC-related fields. 340 gc_store: Option<GcStore>, 341 gc_roots: RootSet, 342 #[cfg(feature = "gc")] 343 gc_roots_list: GcRootsList, 344 // Types for which the embedder has created an allocator for. 345 #[cfg(feature = "gc")] 346 gc_host_alloc_types: crate::hash_set::HashSet<crate::type_registry::RegisteredType>, 347 348 // Numbers of resources instantiated in this store, and their limits 349 instance_count: usize, 350 instance_limit: usize, 351 memory_count: usize, 352 memory_limit: usize, 353 table_count: usize, 354 table_limit: usize, 355 #[cfg(feature = "async")] 356 async_state: AsyncState, 357 358 // If fuel_yield_interval is enabled, then we store the remaining fuel (that isn't in 359 // runtime_limits) here. The total amount of fuel is the runtime limits and reserve added 360 // together. Then when we run out of gas, we inject the yield amount from the reserve 361 // until the reserve is empty. 362 fuel_reserve: u64, 363 fuel_yield_interval: Option<NonZeroU64>, 364 /// Indexed data within this `Store`, used to store information about 365 /// globals, functions, memories, etc. 366 store_data: StoreData, 367 traitobj: StorePtr, 368 default_caller_vmctx: SendSyncPtr<VMContext>, 369 370 /// Used to optimized wasm->host calls when the host function is defined with 371 /// `Func::new` to avoid allocating a new vector each time a function is 372 /// called. 373 hostcall_val_storage: Vec<Val>, 374 /// Same as `hostcall_val_storage`, but for the direction of the host 375 /// calling wasm. 376 wasm_val_raw_storage: Vec<ValRaw>, 377 378 /// Keep track of what protection key is being used during allocation so 379 /// that the right memory pages can be enabled when entering WebAssembly 380 /// guest code. 381 pkey: Option<ProtectionKey>, 382 383 /// Runtime state for components used in the handling of resources, borrow, 384 /// and calls. These also interact with the `ResourceAny` type and its 385 /// internal representation. 386 #[cfg(feature = "component-model")] 387 component_host_table: vm::component::ResourceTable, 388 #[cfg(feature = "component-model")] 389 component_calls: vm::component::CallContexts, 390 #[cfg(feature = "component-model")] 391 host_resource_data: crate::component::HostResourceData, 392 393 /// State related to the executor of wasm code. 394 /// 395 /// For example if Pulley is enabled and configured then this will store a 396 /// Pulley interpreter. 397 executor: Executor, 398 } 399 400 /// Executor state within `StoreOpaque`. 401 /// 402 /// Effectively stores Pulley interpreter state and handles conditional support 403 /// for Cranelift at compile time. 404 enum Executor { 405 Interpreter(Interpreter), 406 #[cfg(has_host_compiler_backend)] 407 Native, 408 } 409 410 /// A borrowed reference to `Executor` above. 411 pub(crate) enum ExecutorRef<'a> { 412 Interpreter(InterpreterRef<'a>), 413 #[cfg(has_host_compiler_backend)] 414 Native, 415 } 416 417 /// An RAII type to automatically mark a region of code as unsafe for GC. 418 #[doc(hidden)] 419 pub struct AutoAssertNoGc<'a> { 420 store: &'a mut StoreOpaque, 421 entered: bool, 422 } 423 424 impl<'a> AutoAssertNoGc<'a> { 425 #[inline] 426 pub fn new(store: &'a mut StoreOpaque) -> Self { 427 let entered = if !cfg!(feature = "gc") { 428 false 429 } else if let Some(gc_store) = store.gc_store.as_mut() { 430 gc_store.gc_heap.enter_no_gc_scope(); 431 true 432 } else { 433 false 434 }; 435 436 AutoAssertNoGc { store, entered } 437 } 438 439 /// Creates an `AutoAssertNoGc` value which is forcibly "not entered" and 440 /// disables checks for no GC happening for the duration of this value. 441 /// 442 /// This is used when it is statically otherwise known that a GC doesn't 443 /// happen for the various types involved. 444 /// 445 /// # Unsafety 446 /// 447 /// This method is `unsafe` as it does not provide the same safety 448 /// guarantees as `AutoAssertNoGc::new`. It must be guaranteed by the 449 /// caller that a GC doesn't happen. 450 #[inline] 451 pub unsafe fn disabled(store: &'a mut StoreOpaque) -> Self { 452 if cfg!(debug_assertions) { 453 AutoAssertNoGc::new(store) 454 } else { 455 AutoAssertNoGc { 456 store, 457 entered: false, 458 } 459 } 460 } 461 } 462 463 impl core::ops::Deref for AutoAssertNoGc<'_> { 464 type Target = StoreOpaque; 465 466 #[inline] 467 fn deref(&self) -> &Self::Target { 468 &*self.store 469 } 470 } 471 472 impl core::ops::DerefMut for AutoAssertNoGc<'_> { 473 #[inline] 474 fn deref_mut(&mut self) -> &mut Self::Target { 475 &mut *self.store 476 } 477 } 478 479 impl Drop for AutoAssertNoGc<'_> { 480 #[inline] 481 fn drop(&mut self) { 482 if self.entered { 483 self.store.unwrap_gc_store_mut().gc_heap.exit_no_gc_scope(); 484 } 485 } 486 } 487 488 /// Used to associate instances with the store. 489 /// 490 /// This is needed to track if the instance was allocated explicitly with the on-demand 491 /// instance allocator. 492 struct StoreInstance { 493 handle: InstanceHandle, 494 kind: StoreInstanceKind, 495 } 496 497 enum StoreInstanceKind { 498 /// An actual, non-dummy instance. 499 Real { 500 /// The id of this instance's module inside our owning store's 501 /// `ModuleRegistry`. 502 module_id: RegisteredModuleId, 503 }, 504 505 /// This is a dummy instance that is just an implementation detail for 506 /// something else. For example, host-created memories internally create a 507 /// dummy instance. 508 /// 509 /// Regardless of the configured instance allocator for the engine, dummy 510 /// instances always use the on-demand allocator to deallocate the instance. 511 Dummy, 512 } 513 514 impl<T> Store<T> { 515 /// Creates a new [`Store`] to be associated with the given [`Engine`] and 516 /// `data` provided. 517 /// 518 /// The created [`Store`] will place no additional limits on the size of 519 /// linear memories or tables at runtime. Linear memories and tables will 520 /// be allowed to grow to any upper limit specified in their definitions. 521 /// The store will limit the number of instances, linear memories, and 522 /// tables created to 10,000. This can be overridden with the 523 /// [`Store::limiter`] configuration method. 524 pub fn new(engine: &Engine, data: T) -> Self { 525 let store_data = StoreData::new(); 526 log::trace!("creating new store {:?}", store_data.id()); 527 528 let pkey = engine.allocator().next_available_pkey(); 529 530 let inner = StoreOpaque { 531 _marker: marker::PhantomPinned, 532 engine: engine.clone(), 533 vm_store_context: Default::default(), 534 #[cfg(feature = "stack-switching")] 535 continuations: Vec::new(), 536 instances: PrimaryMap::new(), 537 #[cfg(feature = "component-model")] 538 num_component_instances: 0, 539 signal_handler: None, 540 gc_store: None, 541 gc_roots: RootSet::default(), 542 #[cfg(feature = "gc")] 543 gc_roots_list: GcRootsList::default(), 544 #[cfg(feature = "gc")] 545 gc_host_alloc_types: Default::default(), 546 modules: ModuleRegistry::default(), 547 func_refs: FuncRefs::default(), 548 host_globals: PrimaryMap::new(), 549 instance_count: 0, 550 instance_limit: crate::DEFAULT_INSTANCE_LIMIT, 551 memory_count: 0, 552 memory_limit: crate::DEFAULT_MEMORY_LIMIT, 553 table_count: 0, 554 table_limit: crate::DEFAULT_TABLE_LIMIT, 555 #[cfg(feature = "async")] 556 async_state: AsyncState::default(), 557 fuel_reserve: 0, 558 fuel_yield_interval: None, 559 store_data, 560 traitobj: StorePtr::empty(), 561 default_caller_vmctx: SendSyncPtr::new(NonNull::dangling()), 562 hostcall_val_storage: Vec::new(), 563 wasm_val_raw_storage: Vec::new(), 564 pkey, 565 #[cfg(feature = "component-model")] 566 component_host_table: Default::default(), 567 #[cfg(feature = "component-model")] 568 component_calls: Default::default(), 569 #[cfg(feature = "component-model")] 570 host_resource_data: Default::default(), 571 #[cfg(has_host_compiler_backend)] 572 executor: if cfg!(feature = "pulley") && engine.target().is_pulley() { 573 Executor::Interpreter(Interpreter::new(engine)) 574 } else { 575 Executor::Native 576 }, 577 #[cfg(not(has_host_compiler_backend))] 578 executor: { 579 debug_assert!(engine.target().is_pulley()); 580 Executor::Interpreter(Interpreter::new(engine)) 581 }, 582 }; 583 let mut inner = Box::new(StoreInner { 584 inner, 585 limiter: None, 586 call_hook: None, 587 #[cfg(target_has_atomic = "64")] 588 epoch_deadline_behavior: None, 589 data: ManuallyDrop::new(data), 590 }); 591 592 inner.traitobj = StorePtr::new(NonNull::from(&mut *inner)); 593 594 // Wasmtime uses the callee argument to host functions to learn about 595 // the original pointer to the `Store` itself, allowing it to 596 // reconstruct a `StoreContextMut<T>`. When we initially call a `Func`, 597 // however, there's no "callee" to provide. To fix this we allocate a 598 // single "default callee" for the entire `Store`. This is then used as 599 // part of `Func::call` to guarantee that the `callee: *mut VMContext` 600 // is never null. 601 let module = Arc::new(wasmtime_environ::Module::default()); 602 let shim = ModuleRuntimeInfo::bare(module); 603 let allocator = OnDemandInstanceAllocator::default(); 604 605 allocator 606 .validate_module(shim.env_module(), shim.offsets()) 607 .unwrap(); 608 609 unsafe { 610 let id = inner 611 .allocate_instance( 612 AllocateInstanceKind::Dummy { 613 allocator: &allocator, 614 }, 615 &shim, 616 Default::default(), 617 ) 618 .expect("failed to allocate default callee"); 619 let default_caller_vmctx = inner.instance(id).vmctx(); 620 inner.default_caller_vmctx = default_caller_vmctx.into(); 621 } 622 623 Self { 624 inner: ManuallyDrop::new(inner), 625 } 626 } 627 628 /// Access the underlying data owned by this `Store`. 629 #[inline] 630 pub fn data(&self) -> &T { 631 self.inner.data() 632 } 633 634 /// Access the underlying data owned by this `Store`. 635 #[inline] 636 pub fn data_mut(&mut self) -> &mut T { 637 self.inner.data_mut() 638 } 639 640 /// Consumes this [`Store`], destroying it, and returns the underlying data. 641 pub fn into_data(mut self) -> T { 642 self.inner.flush_fiber_stack(); 643 644 // This is an unsafe operation because we want to avoid having a runtime 645 // check or boolean for whether the data is actually contained within a 646 // `Store`. The data itself is stored as `ManuallyDrop` since we're 647 // manually managing the memory here, and there's also a `ManuallyDrop` 648 // around the `Box<StoreInner<T>>`. The way this works though is a bit 649 // tricky, so here's how things get dropped appropriately: 650 // 651 // * When a `Store<T>` is normally dropped, the custom destructor for 652 // `Store<T>` will drop `T`, then the `self.inner` field. The 653 // rustc-glue destructor runs for `Box<StoreInner<T>>` which drops 654 // `StoreInner<T>`. This cleans up all internal fields and doesn't 655 // touch `T` because it's wrapped in `ManuallyDrop`. 656 // 657 // * When calling this method we skip the top-level destructor for 658 // `Store<T>` with `mem::forget`. This skips both the destructor for 659 // `T` and the destructor for `StoreInner<T>`. We do, however, run the 660 // destructor for `Box<StoreInner<T>>` which, like above, will skip 661 // the destructor for `T` since it's `ManuallyDrop`. 662 // 663 // In both cases all the other fields of `StoreInner<T>` should all get 664 // dropped, and the manual management of destructors is basically 665 // between this method and `Drop for Store<T>`. Note that this also 666 // means that `Drop for StoreInner<T>` cannot access `self.data`, so 667 // there is a comment indicating this as well. 668 unsafe { 669 let mut inner = ManuallyDrop::take(&mut self.inner); 670 core::mem::forget(self); 671 ManuallyDrop::take(&mut inner.data) 672 } 673 } 674 675 /// Configures the [`ResourceLimiter`] used to limit resource creation 676 /// within this [`Store`]. 677 /// 678 /// Whenever resources such as linear memory, tables, or instances are 679 /// allocated the `limiter` specified here is invoked with the store's data 680 /// `T` and the returned [`ResourceLimiter`] is used to limit the operation 681 /// being allocated. The returned [`ResourceLimiter`] is intended to live 682 /// within the `T` itself, for example by storing a 683 /// [`StoreLimits`](crate::StoreLimits). 684 /// 685 /// Note that this limiter is only used to limit the creation/growth of 686 /// resources in the future, this does not retroactively attempt to apply 687 /// limits to the [`Store`]. 688 /// 689 /// # Examples 690 /// 691 /// ``` 692 /// use wasmtime::*; 693 /// 694 /// struct MyApplicationState { 695 /// my_state: u32, 696 /// limits: StoreLimits, 697 /// } 698 /// 699 /// let engine = Engine::default(); 700 /// let my_state = MyApplicationState { 701 /// my_state: 42, 702 /// limits: StoreLimitsBuilder::new() 703 /// .memory_size(1 << 20 /* 1 MB */) 704 /// .instances(2) 705 /// .build(), 706 /// }; 707 /// let mut store = Store::new(&engine, my_state); 708 /// store.limiter(|state| &mut state.limits); 709 /// 710 /// // Creation of smaller memories is allowed 711 /// Memory::new(&mut store, MemoryType::new(1, None)).unwrap(); 712 /// 713 /// // Creation of a larger memory, however, will exceed the 1MB limit we've 714 /// // configured 715 /// assert!(Memory::new(&mut store, MemoryType::new(1000, None)).is_err()); 716 /// 717 /// // The number of instances in this store is limited to 2, so the third 718 /// // instance here should fail. 719 /// let module = Module::new(&engine, "(module)").unwrap(); 720 /// assert!(Instance::new(&mut store, &module, &[]).is_ok()); 721 /// assert!(Instance::new(&mut store, &module, &[]).is_ok()); 722 /// assert!(Instance::new(&mut store, &module, &[]).is_err()); 723 /// ``` 724 /// 725 /// [`ResourceLimiter`]: crate::ResourceLimiter 726 pub fn limiter( 727 &mut self, 728 mut limiter: impl FnMut(&mut T) -> &mut (dyn crate::ResourceLimiter) + Send + Sync + 'static, 729 ) { 730 // Apply the limits on instances, tables, and memory given by the limiter: 731 let inner = &mut self.inner; 732 let (instance_limit, table_limit, memory_limit) = { 733 let l = limiter(&mut inner.data); 734 (l.instances(), l.tables(), l.memories()) 735 }; 736 let innermost = &mut inner.inner; 737 innermost.instance_limit = instance_limit; 738 innermost.table_limit = table_limit; 739 innermost.memory_limit = memory_limit; 740 741 // Save the limiter accessor function: 742 inner.limiter = Some(ResourceLimiterInner::Sync(Box::new(limiter))); 743 } 744 745 /// Configure a function that runs on calls and returns between WebAssembly 746 /// and host code. 747 /// 748 /// The function is passed a [`CallHook`] argument, which indicates which 749 /// state transition the VM is making. 750 /// 751 /// This function may return a [`Trap`]. If a trap is returned when an 752 /// import was called, it is immediately raised as-if the host import had 753 /// returned the trap. If a trap is returned after wasm returns to the host 754 /// then the wasm function's result is ignored and this trap is returned 755 /// instead. 756 /// 757 /// After this function returns a trap, it may be called for subsequent returns 758 /// to host or wasm code as the trap propagates to the root call. 759 #[cfg(feature = "call-hook")] 760 pub fn call_hook( 761 &mut self, 762 hook: impl FnMut(StoreContextMut<'_, T>, CallHook) -> Result<()> + Send + Sync + 'static, 763 ) { 764 self.inner.call_hook = Some(CallHookInner::Sync(Box::new(hook))); 765 } 766 767 /// Returns the [`Engine`] that this store is associated with. 768 pub fn engine(&self) -> &Engine { 769 self.inner.engine() 770 } 771 772 /// Perform garbage collection. 773 /// 774 /// Note that it is not required to actively call this function. GC will 775 /// automatically happen according to various internal heuristics. This is 776 /// provided if fine-grained control over the GC is desired. 777 /// 778 /// If you are calling this method after an attempted allocation failed, you 779 /// may pass in the [`GcHeapOutOfMemory`][crate::GcHeapOutOfMemory] error. 780 /// When you do so, this method will attempt to create enough space in the 781 /// GC heap for that allocation, so that it will succeed on the next 782 /// attempt. 783 /// 784 /// This method is only available when the `gc` Cargo feature is enabled. 785 #[cfg(feature = "gc")] 786 pub fn gc(&mut self, why: Option<&crate::GcHeapOutOfMemory<()>>) { 787 assert!(!self.inner.async_support()); 788 self.inner.gc(why); 789 } 790 791 /// Returns the amount fuel in this [`Store`]. When fuel is enabled, it must 792 /// be configured via [`Store::set_fuel`]. 793 /// 794 /// # Errors 795 /// 796 /// This function will return an error if fuel consumption is not enabled 797 /// via [`Config::consume_fuel`](crate::Config::consume_fuel). 798 pub fn get_fuel(&self) -> Result<u64> { 799 self.inner.get_fuel() 800 } 801 802 /// Set the fuel to this [`Store`] for wasm to consume while executing. 803 /// 804 /// For this method to work fuel consumption must be enabled via 805 /// [`Config::consume_fuel`](crate::Config::consume_fuel). By default a 806 /// [`Store`] starts with 0 fuel for wasm to execute with (meaning it will 807 /// immediately trap). This function must be called for the store to have 808 /// some fuel to allow WebAssembly to execute. 809 /// 810 /// Most WebAssembly instructions consume 1 unit of fuel. Some 811 /// instructions, such as `nop`, `drop`, `block`, and `loop`, consume 0 812 /// units, as any execution cost associated with them involves other 813 /// instructions which do consume fuel. 814 /// 815 /// Note that when fuel is entirely consumed it will cause wasm to trap. 816 /// 817 /// # Errors 818 /// 819 /// This function will return an error if fuel consumption is not enabled via 820 /// [`Config::consume_fuel`](crate::Config::consume_fuel). 821 pub fn set_fuel(&mut self, fuel: u64) -> Result<()> { 822 self.inner.set_fuel(fuel) 823 } 824 825 /// Configures a [`Store`] to yield execution of async WebAssembly code 826 /// periodically. 827 /// 828 /// When a [`Store`] is configured to consume fuel with 829 /// [`Config::consume_fuel`](crate::Config::consume_fuel) this method will 830 /// configure WebAssembly to be suspended and control will be yielded back to the 831 /// caller every `interval` units of fuel consumed. This is only suitable with use of 832 /// a store associated with an [async config](crate::Config::async_support) because 833 /// only then are futures used and yields are possible. 834 /// 835 /// The purpose of this behavior is to ensure that futures which represent 836 /// execution of WebAssembly do not execute too long inside their 837 /// `Future::poll` method. This allows for some form of cooperative 838 /// multitasking where WebAssembly will voluntarily yield control 839 /// periodically (based on fuel consumption) back to the running thread. 840 /// 841 /// Note that futures returned by this crate will automatically flag 842 /// themselves to get re-polled if a yield happens. This means that 843 /// WebAssembly will continue to execute, just after giving the host an 844 /// opportunity to do something else. 845 /// 846 /// The `interval` parameter indicates how much fuel should be 847 /// consumed between yields of an async future. When fuel runs out wasm will trap. 848 /// 849 /// # Error 850 /// 851 /// This method will error if it is not called on a store associated with an [async 852 /// config](crate::Config::async_support). 853 pub fn fuel_async_yield_interval(&mut self, interval: Option<u64>) -> Result<()> { 854 self.inner.fuel_async_yield_interval(interval) 855 } 856 857 /// Sets the epoch deadline to a certain number of ticks in the future. 858 /// 859 /// When the Wasm guest code is compiled with epoch-interruption 860 /// instrumentation 861 /// ([`Config::epoch_interruption()`](crate::Config::epoch_interruption)), 862 /// and when the `Engine`'s epoch is incremented 863 /// ([`Engine::increment_epoch()`](crate::Engine::increment_epoch)) 864 /// past a deadline, execution can be configured to either trap or 865 /// yield and then continue. 866 /// 867 /// This deadline is always set relative to the current epoch: 868 /// `ticks_beyond_current` ticks in the future. The deadline can 869 /// be set explicitly via this method, or refilled automatically 870 /// on a yield if configured via 871 /// [`epoch_deadline_async_yield_and_update()`](Store::epoch_deadline_async_yield_and_update). After 872 /// this method is invoked, the deadline is reached when 873 /// [`Engine::increment_epoch()`] has been invoked at least 874 /// `ticks_beyond_current` times. 875 /// 876 /// By default a store will trap immediately with an epoch deadline of 0 877 /// (which has always "elapsed"). This method is required to be configured 878 /// for stores with epochs enabled to some future epoch deadline. 879 /// 880 /// See documentation on 881 /// [`Config::epoch_interruption()`](crate::Config::epoch_interruption) 882 /// for an introduction to epoch-based interruption. 883 #[cfg(target_has_atomic = "64")] 884 pub fn set_epoch_deadline(&mut self, ticks_beyond_current: u64) { 885 self.inner.set_epoch_deadline(ticks_beyond_current); 886 } 887 888 /// Configures epoch-deadline expiration to trap. 889 /// 890 /// When epoch-interruption-instrumented code is executed on this 891 /// store and the epoch deadline is reached before completion, 892 /// with the store configured in this way, execution will 893 /// terminate with a trap as soon as an epoch check in the 894 /// instrumented code is reached. 895 /// 896 /// This behavior is the default if the store is not otherwise 897 /// configured via 898 /// [`epoch_deadline_trap()`](Store::epoch_deadline_trap), 899 /// [`epoch_deadline_callback()`](Store::epoch_deadline_callback) or 900 /// [`epoch_deadline_async_yield_and_update()`](Store::epoch_deadline_async_yield_and_update). 901 /// 902 /// This setting is intended to allow for coarse-grained 903 /// interruption, but not a deterministic deadline of a fixed, 904 /// finite interval. For deterministic interruption, see the 905 /// "fuel" mechanism instead. 906 /// 907 /// Note that when this is used it's required to call 908 /// [`Store::set_epoch_deadline`] or otherwise wasm will always immediately 909 /// trap. 910 /// 911 /// See documentation on 912 /// [`Config::epoch_interruption()`](crate::Config::epoch_interruption) 913 /// for an introduction to epoch-based interruption. 914 #[cfg(target_has_atomic = "64")] 915 pub fn epoch_deadline_trap(&mut self) { 916 self.inner.epoch_deadline_trap(); 917 } 918 919 /// Configures epoch-deadline expiration to invoke a custom callback 920 /// function. 921 /// 922 /// When epoch-interruption-instrumented code is executed on this 923 /// store and the epoch deadline is reached before completion, the 924 /// provided callback function is invoked. 925 /// 926 /// This callback should either return an [`UpdateDeadline`], or 927 /// return an error, which will terminate execution with a trap. 928 /// 929 /// The [`UpdateDeadline`] is a positive number of ticks to 930 /// add to the epoch deadline, as well as indicating what 931 /// to do after the callback returns. If the [`Store`] is 932 /// configured with async support, then the callback may return 933 /// [`UpdateDeadline::Yield`] or [`UpdateDeadline::YieldCustom`] 934 /// to yield to the async executor before updating the epoch deadline. 935 /// Alternatively, the callback may return [`UpdateDeadline::Continue`] to 936 /// update the epoch deadline immediately. 937 /// 938 /// This setting is intended to allow for coarse-grained 939 /// interruption, but not a deterministic deadline of a fixed, 940 /// finite interval. For deterministic interruption, see the 941 /// "fuel" mechanism instead. 942 /// 943 /// See documentation on 944 /// [`Config::epoch_interruption()`](crate::Config::epoch_interruption) 945 /// for an introduction to epoch-based interruption. 946 #[cfg(target_has_atomic = "64")] 947 pub fn epoch_deadline_callback( 948 &mut self, 949 callback: impl FnMut(StoreContextMut<T>) -> Result<UpdateDeadline> + Send + Sync + 'static, 950 ) { 951 self.inner.epoch_deadline_callback(Box::new(callback)); 952 } 953 } 954 955 impl<'a, T> StoreContext<'a, T> { 956 pub(crate) fn async_support(&self) -> bool { 957 self.0.async_support() 958 } 959 960 /// Returns the underlying [`Engine`] this store is connected to. 961 pub fn engine(&self) -> &Engine { 962 self.0.engine() 963 } 964 965 /// Access the underlying data owned by this `Store`. 966 /// 967 /// Same as [`Store::data`]. 968 pub fn data(&self) -> &'a T { 969 self.0.data() 970 } 971 972 /// Returns the remaining fuel in this store. 973 /// 974 /// For more information see [`Store::get_fuel`]. 975 pub fn get_fuel(&self) -> Result<u64> { 976 self.0.get_fuel() 977 } 978 } 979 980 impl<'a, T> StoreContextMut<'a, T> { 981 /// Access the underlying data owned by this `Store`. 982 /// 983 /// Same as [`Store::data`]. 984 pub fn data(&self) -> &T { 985 self.0.data() 986 } 987 988 /// Access the underlying data owned by this `Store`. 989 /// 990 /// Same as [`Store::data_mut`]. 991 pub fn data_mut(&mut self) -> &mut T { 992 self.0.data_mut() 993 } 994 995 /// Returns the underlying [`Engine`] this store is connected to. 996 pub fn engine(&self) -> &Engine { 997 self.0.engine() 998 } 999 1000 /// Perform garbage collection of `ExternRef`s. 1001 /// 1002 /// Same as [`Store::gc`]. 1003 /// 1004 /// This method is only available when the `gc` Cargo feature is enabled. 1005 #[cfg(feature = "gc")] 1006 pub fn gc(&mut self, why: Option<&crate::GcHeapOutOfMemory<()>>) { 1007 self.0.gc(why); 1008 } 1009 1010 /// Returns remaining fuel in this store. 1011 /// 1012 /// For more information see [`Store::get_fuel`] 1013 pub fn get_fuel(&self) -> Result<u64> { 1014 self.0.get_fuel() 1015 } 1016 1017 /// Set the amount of fuel in this store. 1018 /// 1019 /// For more information see [`Store::set_fuel`] 1020 pub fn set_fuel(&mut self, fuel: u64) -> Result<()> { 1021 self.0.set_fuel(fuel) 1022 } 1023 1024 /// Configures this `Store` to periodically yield while executing futures. 1025 /// 1026 /// For more information see [`Store::fuel_async_yield_interval`] 1027 pub fn fuel_async_yield_interval(&mut self, interval: Option<u64>) -> Result<()> { 1028 self.0.fuel_async_yield_interval(interval) 1029 } 1030 1031 /// Sets the epoch deadline to a certain number of ticks in the future. 1032 /// 1033 /// For more information see [`Store::set_epoch_deadline`]. 1034 #[cfg(target_has_atomic = "64")] 1035 pub fn set_epoch_deadline(&mut self, ticks_beyond_current: u64) { 1036 self.0.set_epoch_deadline(ticks_beyond_current); 1037 } 1038 1039 /// Configures epoch-deadline expiration to trap. 1040 /// 1041 /// For more information see [`Store::epoch_deadline_trap`]. 1042 #[cfg(target_has_atomic = "64")] 1043 pub fn epoch_deadline_trap(&mut self) { 1044 self.0.epoch_deadline_trap(); 1045 } 1046 } 1047 1048 impl<T> StoreInner<T> { 1049 #[inline] 1050 fn data(&self) -> &T { 1051 &self.data 1052 } 1053 1054 #[inline] 1055 fn data_mut(&mut self) -> &mut T { 1056 &mut self.data 1057 } 1058 1059 #[inline] 1060 pub fn call_hook(&mut self, s: CallHook) -> Result<()> { 1061 if self.inner.pkey.is_none() && self.call_hook.is_none() { 1062 Ok(()) 1063 } else { 1064 self.call_hook_slow_path(s) 1065 } 1066 } 1067 1068 fn call_hook_slow_path(&mut self, s: CallHook) -> Result<()> { 1069 if let Some(pkey) = &self.inner.pkey { 1070 let allocator = self.engine().allocator(); 1071 match s { 1072 CallHook::CallingWasm | CallHook::ReturningFromHost => { 1073 allocator.restrict_to_pkey(*pkey) 1074 } 1075 CallHook::ReturningFromWasm | CallHook::CallingHost => allocator.allow_all_pkeys(), 1076 } 1077 } 1078 1079 // Temporarily take the configured behavior to avoid mutably borrowing 1080 // multiple times. 1081 #[cfg_attr(not(feature = "call-hook"), allow(unreachable_patterns))] 1082 if let Some(mut call_hook) = self.call_hook.take() { 1083 let result = self.invoke_call_hook(&mut call_hook, s); 1084 self.call_hook = Some(call_hook); 1085 return result; 1086 } 1087 1088 Ok(()) 1089 } 1090 1091 fn invoke_call_hook(&mut self, call_hook: &mut CallHookInner<T>, s: CallHook) -> Result<()> { 1092 match call_hook { 1093 #[cfg(feature = "call-hook")] 1094 CallHookInner::Sync(hook) => hook((&mut *self).as_context_mut(), s), 1095 1096 #[cfg(all(feature = "async", feature = "call-hook"))] 1097 CallHookInner::Async(handler) => unsafe { 1098 self.inner 1099 .async_cx() 1100 .ok_or_else(|| anyhow!("couldn't grab async_cx for call hook"))? 1101 .block_on( 1102 handler 1103 .handle_call_event((&mut *self).as_context_mut(), s) 1104 .as_mut(), 1105 )? 1106 }, 1107 1108 CallHookInner::ForceTypeParameterToBeUsed { uninhabited, .. } => { 1109 let _ = s; 1110 match *uninhabited {} 1111 } 1112 } 1113 } 1114 1115 #[cfg(not(feature = "async"))] 1116 fn flush_fiber_stack(&mut self) { 1117 // noop shim so code can assume this always exists. 1118 } 1119 } 1120 1121 fn get_fuel(injected_fuel: i64, fuel_reserve: u64) -> u64 { 1122 fuel_reserve.saturating_add_signed(-injected_fuel) 1123 } 1124 1125 // Add remaining fuel from the reserve into the active fuel if there is any left. 1126 fn refuel( 1127 injected_fuel: &mut i64, 1128 fuel_reserve: &mut u64, 1129 yield_interval: Option<NonZeroU64>, 1130 ) -> bool { 1131 let fuel = get_fuel(*injected_fuel, *fuel_reserve); 1132 if fuel > 0 { 1133 set_fuel(injected_fuel, fuel_reserve, yield_interval, fuel); 1134 true 1135 } else { 1136 false 1137 } 1138 } 1139 1140 fn set_fuel( 1141 injected_fuel: &mut i64, 1142 fuel_reserve: &mut u64, 1143 yield_interval: Option<NonZeroU64>, 1144 new_fuel_amount: u64, 1145 ) { 1146 let interval = yield_interval.unwrap_or(NonZeroU64::MAX).get(); 1147 // If we're yielding periodically we only store the "active" amount of fuel into consumed_ptr 1148 // for the VM to use. 1149 let injected = core::cmp::min(interval, new_fuel_amount); 1150 // Fuel in the VM is stored as an i64, so we have to cap the amount of fuel we inject into the 1151 // VM at once to be i64 range. 1152 let injected = core::cmp::min(injected, i64::MAX as u64); 1153 // Add whatever is left over after injection to the reserve for later use. 1154 *fuel_reserve = new_fuel_amount - injected; 1155 // Within the VM we increment to count fuel, so inject a negative amount. The VM will halt when 1156 // this counter is positive. 1157 *injected_fuel = -(injected as i64); 1158 } 1159 1160 #[doc(hidden)] 1161 impl StoreOpaque { 1162 pub fn id(&self) -> StoreId { 1163 self.store_data.id() 1164 } 1165 1166 pub fn bump_resource_counts(&mut self, module: &Module) -> Result<()> { 1167 fn bump(slot: &mut usize, max: usize, amt: usize, desc: &str) -> Result<()> { 1168 let new = slot.saturating_add(amt); 1169 if new > max { 1170 bail!( 1171 "resource limit exceeded: {} count too high at {}", 1172 desc, 1173 new 1174 ); 1175 } 1176 *slot = new; 1177 Ok(()) 1178 } 1179 1180 let module = module.env_module(); 1181 let memories = module.num_defined_memories(); 1182 let tables = module.num_defined_tables(); 1183 1184 bump(&mut self.instance_count, self.instance_limit, 1, "instance")?; 1185 bump( 1186 &mut self.memory_count, 1187 self.memory_limit, 1188 memories, 1189 "memory", 1190 )?; 1191 bump(&mut self.table_count, self.table_limit, tables, "table")?; 1192 1193 Ok(()) 1194 } 1195 1196 #[inline] 1197 pub fn async_support(&self) -> bool { 1198 cfg!(feature = "async") && self.engine().config().async_support 1199 } 1200 1201 #[inline] 1202 pub fn engine(&self) -> &Engine { 1203 &self.engine 1204 } 1205 1206 #[inline] 1207 pub fn store_data(&self) -> &StoreData { 1208 &self.store_data 1209 } 1210 1211 #[inline] 1212 pub fn store_data_mut(&mut self) -> &mut StoreData { 1213 &mut self.store_data 1214 } 1215 1216 #[inline] 1217 pub(crate) fn modules(&self) -> &ModuleRegistry { 1218 &self.modules 1219 } 1220 1221 #[inline] 1222 pub(crate) fn modules_mut(&mut self) -> &mut ModuleRegistry { 1223 &mut self.modules 1224 } 1225 1226 pub(crate) fn func_refs_and_modules(&mut self) -> (&mut FuncRefs, &ModuleRegistry) { 1227 (&mut self.func_refs, &self.modules) 1228 } 1229 1230 pub(crate) fn host_globals( 1231 &self, 1232 ) -> &PrimaryMap<DefinedGlobalIndex, StoreBox<VMHostGlobalContext>> { 1233 &self.host_globals 1234 } 1235 1236 pub(crate) fn host_globals_mut( 1237 &mut self, 1238 ) -> &mut PrimaryMap<DefinedGlobalIndex, StoreBox<VMHostGlobalContext>> { 1239 &mut self.host_globals 1240 } 1241 1242 pub fn module_for_instance(&self, instance: StoreInstanceId) -> Option<&'_ Module> { 1243 instance.store_id().assert_belongs_to(self.id()); 1244 match self.instances[instance.instance()].kind { 1245 StoreInstanceKind::Dummy => None, 1246 StoreInstanceKind::Real { module_id } => { 1247 let module = self 1248 .modules() 1249 .lookup_module_by_id(module_id) 1250 .expect("should always have a registered module for real instances"); 1251 Some(module) 1252 } 1253 } 1254 } 1255 1256 /// Accessor from `InstanceId` to `&vm::Instance`. 1257 /// 1258 /// Note that if you have a `StoreInstanceId` you should use 1259 /// `StoreInstanceId::get` instead. This assumes that `id` has been 1260 /// validated to already belong to this store. 1261 pub fn instance(&self, id: InstanceId) -> &vm::Instance { 1262 self.instances[id].handle.get() 1263 } 1264 1265 /// Accessor from `InstanceId` to `Pin<&mut vm::Instance>`. 1266 /// 1267 /// Note that if you have a `StoreInstanceId` you should use 1268 /// `StoreInstanceId::get_mut` instead. This assumes that `id` has been 1269 /// validated to already belong to this store. 1270 pub fn instance_mut(&mut self, id: InstanceId) -> Pin<&mut vm::Instance> { 1271 self.instances[id].handle.get_mut() 1272 } 1273 1274 /// Get all instances (ignoring dummy instances) within this store. 1275 pub fn all_instances<'a>(&'a mut self) -> impl ExactSizeIterator<Item = Instance> + 'a { 1276 let instances = self 1277 .instances 1278 .iter() 1279 .filter_map(|(id, inst)| { 1280 if let StoreInstanceKind::Dummy = inst.kind { 1281 None 1282 } else { 1283 Some(id) 1284 } 1285 }) 1286 .collect::<Vec<_>>(); 1287 instances 1288 .into_iter() 1289 .map(|i| Instance::from_wasmtime(i, self)) 1290 } 1291 1292 /// Get all memories (host- or Wasm-defined) within this store. 1293 pub fn all_memories<'a>(&'a mut self) -> impl Iterator<Item = Memory> + 'a { 1294 // NB: Host-created memories have dummy instances. Therefore, we can get 1295 // all memories in the store by iterating over all instances (including 1296 // dummy instances) and getting each of their defined memories. 1297 let mems = self 1298 .instances 1299 .iter_mut() 1300 .flat_map(|(_, instance)| instance.handle.get().defined_memories()) 1301 .collect::<Vec<_>>(); 1302 mems.into_iter() 1303 .map(|memory| unsafe { Memory::from_wasmtime_memory(memory, self) }) 1304 } 1305 1306 /// Iterate over all tables (host- or Wasm-defined) within this store. 1307 pub fn for_each_table(&mut self, mut f: impl FnMut(&mut Self, Table)) { 1308 // NB: Host-created tables have dummy instances. Therefore, we can get 1309 // all tables in the store by iterating over all instances (including 1310 // dummy instances) and getting each of their defined memories. 1311 for id in self.instances.keys() { 1312 let instance = StoreInstanceId::new(self.id(), id); 1313 for table in 0..self.instance(id).env_module().num_defined_tables() { 1314 let table = DefinedTableIndex::new(table); 1315 f(self, Table::from_raw(instance, table)); 1316 } 1317 } 1318 } 1319 1320 /// Iterate over all globals (host- or Wasm-defined) within this store. 1321 pub fn for_each_global(&mut self, mut f: impl FnMut(&mut Self, Global)) { 1322 // First enumerate all the host-created globals. 1323 for global in self.host_globals.keys() { 1324 let global = Global::new_host(self, global); 1325 f(self, global); 1326 } 1327 1328 // Then enumerate all instances' defined globals. 1329 for id in self.instances.keys() { 1330 for index in 0..self.instance(id).env_module().num_defined_globals() { 1331 let index = DefinedGlobalIndex::new(index); 1332 let global = Global::new_instance(self, id, index); 1333 f(self, global); 1334 } 1335 } 1336 } 1337 1338 #[cfg_attr(not(target_os = "linux"), allow(dead_code))] // not used on all platforms 1339 pub fn set_signal_handler(&mut self, handler: Option<SignalHandler>) { 1340 self.signal_handler = handler; 1341 } 1342 1343 #[inline] 1344 pub fn vm_store_context(&self) -> &VMStoreContext { 1345 &self.vm_store_context 1346 } 1347 1348 #[inline(never)] 1349 pub(crate) fn allocate_gc_heap(&mut self) -> Result<()> { 1350 log::trace!("allocating GC heap for store {:?}", self.id()); 1351 1352 assert!(self.gc_store.is_none()); 1353 assert_eq!( 1354 self.vm_store_context.gc_heap.base.as_non_null(), 1355 NonNull::dangling(), 1356 ); 1357 assert_eq!(self.vm_store_context.gc_heap.current_length(), 0); 1358 1359 let vmstore = self.traitobj(); 1360 let gc_store = allocate_gc_store(self.engine(), vmstore, self.get_pkey())?; 1361 self.vm_store_context.gc_heap = gc_store.vmmemory_definition(); 1362 self.gc_store = Some(gc_store); 1363 return Ok(()); 1364 1365 #[cfg(feature = "gc")] 1366 fn allocate_gc_store( 1367 engine: &Engine, 1368 vmstore: NonNull<dyn vm::VMStore>, 1369 pkey: Option<ProtectionKey>, 1370 ) -> Result<GcStore> { 1371 use wasmtime_environ::packed_option::ReservedValue; 1372 1373 ensure!( 1374 engine.features().gc_types(), 1375 "cannot allocate a GC store when GC is disabled at configuration time" 1376 ); 1377 1378 // First, allocate the memory that will be our GC heap's storage. 1379 let mut request = InstanceAllocationRequest { 1380 id: InstanceId::reserved_value(), 1381 runtime_info: &ModuleRuntimeInfo::bare(Arc::new( 1382 wasmtime_environ::Module::default(), 1383 )), 1384 imports: vm::Imports::default(), 1385 store: StorePtr::new(vmstore), 1386 wmemcheck: false, 1387 pkey, 1388 tunables: engine.tunables(), 1389 }; 1390 let mem_ty = engine.tunables().gc_heap_memory_type(); 1391 let tunables = engine.tunables(); 1392 1393 // SAFETY: We validated the GC heap's memory type during engine creation. 1394 let (mem_alloc_index, mem) = unsafe { 1395 engine 1396 .allocator() 1397 .allocate_memory(&mut request, &mem_ty, tunables, None)? 1398 }; 1399 1400 // Then, allocate the actual GC heap, passing in that memory 1401 // storage. 1402 let (index, heap) = engine.allocator().allocate_gc_heap( 1403 engine, 1404 &**engine.gc_runtime()?, 1405 mem_alloc_index, 1406 mem, 1407 )?; 1408 1409 Ok(GcStore::new(index, heap)) 1410 } 1411 1412 #[cfg(not(feature = "gc"))] 1413 fn allocate_gc_store( 1414 _engine: &Engine, 1415 _vmstore: NonNull<dyn vm::VMStore>, 1416 _pkey: Option<ProtectionKey>, 1417 ) -> Result<GcStore> { 1418 bail!("cannot allocate a GC store: the `gc` feature was disabled at compile time") 1419 } 1420 } 1421 1422 #[inline] 1423 pub(crate) fn gc_store(&self) -> Result<&GcStore> { 1424 match &self.gc_store { 1425 Some(gc_store) => Ok(gc_store), 1426 None => bail!("GC heap not initialized yet"), 1427 } 1428 } 1429 1430 #[inline] 1431 pub(crate) fn gc_store_mut(&mut self) -> Result<&mut GcStore> { 1432 if self.gc_store.is_none() { 1433 self.allocate_gc_heap()?; 1434 } 1435 Ok(self.unwrap_gc_store_mut()) 1436 } 1437 1438 /// If this store is configured with a GC heap, return a mutable reference 1439 /// to it. Otherwise, return `None`. 1440 #[inline] 1441 pub(crate) fn optional_gc_store_mut(&mut self) -> Option<&mut GcStore> { 1442 if cfg!(not(feature = "gc")) || !self.engine.features().gc_types() { 1443 debug_assert!(self.gc_store.is_none()); 1444 None 1445 } else { 1446 self.gc_store.as_mut() 1447 } 1448 } 1449 1450 /// If this store is configured with a GC heap, return a shared reference to 1451 /// it. Otherwise, return `None`. 1452 #[inline] 1453 #[cfg(feature = "gc")] 1454 pub(crate) fn optional_gc_store(&self) -> Option<&GcStore> { 1455 if cfg!(not(feature = "gc")) || !self.engine.features().gc_types() { 1456 debug_assert!(self.gc_store.is_none()); 1457 None 1458 } else { 1459 self.gc_store.as_ref() 1460 } 1461 } 1462 1463 #[inline] 1464 #[track_caller] 1465 #[cfg(feature = "gc")] 1466 pub(crate) fn unwrap_gc_store(&self) -> &GcStore { 1467 self.gc_store 1468 .as_ref() 1469 .expect("attempted to access the store's GC heap before it has been allocated") 1470 } 1471 1472 #[inline] 1473 #[track_caller] 1474 pub(crate) fn unwrap_gc_store_mut(&mut self) -> &mut GcStore { 1475 self.gc_store 1476 .as_mut() 1477 .expect("attempted to access the store's GC heap before it has been allocated") 1478 } 1479 1480 #[inline] 1481 pub(crate) fn gc_roots(&self) -> &RootSet { 1482 &self.gc_roots 1483 } 1484 1485 #[inline] 1486 #[cfg(feature = "gc")] 1487 pub(crate) fn gc_roots_mut(&mut self) -> &mut RootSet { 1488 &mut self.gc_roots 1489 } 1490 1491 #[inline] 1492 pub(crate) fn exit_gc_lifo_scope(&mut self, scope: usize) { 1493 self.gc_roots.exit_lifo_scope(self.gc_store.as_mut(), scope); 1494 } 1495 1496 #[cfg(feature = "gc")] 1497 fn do_gc(&mut self) { 1498 assert!( 1499 !self.async_support(), 1500 "must use `store.gc_async()` instead of `store.gc()` for async stores" 1501 ); 1502 1503 // If the GC heap hasn't been initialized, there is nothing to collect. 1504 if self.gc_store.is_none() { 1505 return; 1506 } 1507 1508 log::trace!("============ Begin GC ==========="); 1509 1510 // Take the GC roots out of `self` so we can borrow it mutably but still 1511 // call mutable methods on `self`. 1512 let mut roots = core::mem::take(&mut self.gc_roots_list); 1513 1514 self.trace_roots(&mut roots); 1515 self.unwrap_gc_store_mut().gc(unsafe { roots.iter() }); 1516 1517 // Restore the GC roots for the next GC. 1518 roots.clear(); 1519 self.gc_roots_list = roots; 1520 1521 log::trace!("============ End GC ==========="); 1522 } 1523 1524 #[cfg(feature = "gc")] 1525 fn trace_roots(&mut self, gc_roots_list: &mut GcRootsList) { 1526 log::trace!("Begin trace GC roots"); 1527 1528 // We shouldn't have any leftover, stale GC roots. 1529 assert!(gc_roots_list.is_empty()); 1530 1531 self.trace_wasm_stack_roots(gc_roots_list); 1532 #[cfg(feature = "stack-switching")] 1533 self.trace_wasm_continuation_roots(gc_roots_list); 1534 self.trace_vmctx_roots(gc_roots_list); 1535 self.trace_user_roots(gc_roots_list); 1536 1537 log::trace!("End trace GC roots") 1538 } 1539 1540 #[cfg(feature = "gc")] 1541 fn trace_wasm_stack_frame( 1542 &self, 1543 gc_roots_list: &mut GcRootsList, 1544 frame: crate::runtime::vm::Frame, 1545 ) { 1546 use crate::runtime::vm::SendSyncPtr; 1547 use core::ptr::NonNull; 1548 1549 let pc = frame.pc(); 1550 debug_assert!(pc != 0, "we should always get a valid PC for Wasm frames"); 1551 1552 let fp = frame.fp() as *mut usize; 1553 debug_assert!( 1554 !fp.is_null(), 1555 "we should always get a valid frame pointer for Wasm frames" 1556 ); 1557 1558 let module_info = self 1559 .modules() 1560 .lookup_module_by_pc(pc) 1561 .expect("should have module info for Wasm frame"); 1562 1563 let stack_map = match module_info.lookup_stack_map(pc) { 1564 Some(sm) => sm, 1565 None => { 1566 log::trace!("No stack map for this Wasm frame"); 1567 return; 1568 } 1569 }; 1570 log::trace!( 1571 "We have a stack map that maps {} bytes in this Wasm frame", 1572 stack_map.frame_size() 1573 ); 1574 1575 let sp = unsafe { stack_map.sp(fp) }; 1576 for stack_slot in unsafe { stack_map.live_gc_refs(sp) } { 1577 let raw: u32 = unsafe { core::ptr::read(stack_slot) }; 1578 log::trace!("Stack slot @ {stack_slot:p} = {raw:#x}"); 1579 1580 let gc_ref = VMGcRef::from_raw_u32(raw); 1581 if gc_ref.is_some() { 1582 unsafe { 1583 gc_roots_list 1584 .add_wasm_stack_root(SendSyncPtr::new(NonNull::new(stack_slot).unwrap())); 1585 } 1586 } 1587 } 1588 } 1589 1590 #[cfg(feature = "gc")] 1591 fn trace_wasm_stack_roots(&mut self, gc_roots_list: &mut GcRootsList) { 1592 use crate::runtime::vm::Backtrace; 1593 log::trace!("Begin trace GC roots :: Wasm stack"); 1594 1595 Backtrace::trace(self, |frame| { 1596 self.trace_wasm_stack_frame(gc_roots_list, frame); 1597 core::ops::ControlFlow::Continue(()) 1598 }); 1599 1600 log::trace!("End trace GC roots :: Wasm stack"); 1601 } 1602 1603 #[cfg(all(feature = "gc", feature = "stack-switching"))] 1604 fn trace_wasm_continuation_roots(&mut self, gc_roots_list: &mut GcRootsList) { 1605 use crate::{runtime::vm::Backtrace, vm::VMStackState}; 1606 log::trace!("Begin trace GC roots :: continuations"); 1607 1608 for continuation in &self.continuations { 1609 let state = continuation.common_stack_information.state; 1610 1611 // FIXME(frank-emrich) In general, it is not enough to just trace 1612 // through the stacks of continuations; we also need to look through 1613 // their `cont.bind` arguments. However, we don't currently have 1614 // enough RTTI information to check if any of the values in the 1615 // buffers used by `cont.bind` are GC values. As a workaround, note 1616 // that we currently disallow cont.bind-ing GC values altogether. 1617 // This way, it is okay not to check them here. 1618 match state { 1619 VMStackState::Suspended => { 1620 Backtrace::trace_suspended_continuation(self, continuation.deref(), |frame| { 1621 self.trace_wasm_stack_frame(gc_roots_list, frame); 1622 core::ops::ControlFlow::Continue(()) 1623 }); 1624 } 1625 VMStackState::Running => { 1626 // Handled by `trace_wasm_stack_roots`. 1627 } 1628 VMStackState::Parent => { 1629 // We don't know whether our child is suspended or running, but in 1630 // either case things should be hanlded correctly when traversing 1631 // further along in the chain, nothing required at this point. 1632 } 1633 VMStackState::Fresh | VMStackState::Returned => { 1634 // Fresh/Returned continuations have no gc values on their stack. 1635 } 1636 } 1637 } 1638 1639 log::trace!("End trace GC roots :: continuations"); 1640 } 1641 1642 #[cfg(feature = "gc")] 1643 fn trace_vmctx_roots(&mut self, gc_roots_list: &mut GcRootsList) { 1644 log::trace!("Begin trace GC roots :: vmctx"); 1645 self.for_each_global(|store, global| global.trace_root(store, gc_roots_list)); 1646 self.for_each_table(|store, table| table.trace_roots(store, gc_roots_list)); 1647 log::trace!("End trace GC roots :: vmctx"); 1648 } 1649 1650 #[cfg(feature = "gc")] 1651 fn trace_user_roots(&mut self, gc_roots_list: &mut GcRootsList) { 1652 log::trace!("Begin trace GC roots :: user"); 1653 self.gc_roots.trace_roots(gc_roots_list); 1654 log::trace!("End trace GC roots :: user"); 1655 } 1656 1657 /// Insert a host-allocated GC type into this store. 1658 /// 1659 /// This makes it suitable for the embedder to allocate instances of this 1660 /// type in this store, and we don't have to worry about the type being 1661 /// reclaimed (since it is possible that none of the Wasm modules in this 1662 /// store are holding it alive). 1663 #[cfg(feature = "gc")] 1664 pub(crate) fn insert_gc_host_alloc_type(&mut self, ty: crate::type_registry::RegisteredType) { 1665 self.gc_host_alloc_types.insert(ty); 1666 } 1667 1668 pub fn get_fuel(&self) -> Result<u64> { 1669 anyhow::ensure!( 1670 self.engine().tunables().consume_fuel, 1671 "fuel is not configured in this store" 1672 ); 1673 let injected_fuel = unsafe { *self.vm_store_context.fuel_consumed.get() }; 1674 Ok(get_fuel(injected_fuel, self.fuel_reserve)) 1675 } 1676 1677 fn refuel(&mut self) -> bool { 1678 let injected_fuel = unsafe { &mut *self.vm_store_context.fuel_consumed.get() }; 1679 refuel( 1680 injected_fuel, 1681 &mut self.fuel_reserve, 1682 self.fuel_yield_interval, 1683 ) 1684 } 1685 1686 pub fn set_fuel(&mut self, fuel: u64) -> Result<()> { 1687 anyhow::ensure!( 1688 self.engine().tunables().consume_fuel, 1689 "fuel is not configured in this store" 1690 ); 1691 let injected_fuel = unsafe { &mut *self.vm_store_context.fuel_consumed.get() }; 1692 set_fuel( 1693 injected_fuel, 1694 &mut self.fuel_reserve, 1695 self.fuel_yield_interval, 1696 fuel, 1697 ); 1698 Ok(()) 1699 } 1700 1701 pub fn fuel_async_yield_interval(&mut self, interval: Option<u64>) -> Result<()> { 1702 anyhow::ensure!( 1703 self.engine().tunables().consume_fuel, 1704 "fuel is not configured in this store" 1705 ); 1706 anyhow::ensure!( 1707 self.engine().config().async_support, 1708 "async support is not configured in this store" 1709 ); 1710 anyhow::ensure!( 1711 interval != Some(0), 1712 "fuel_async_yield_interval must not be 0" 1713 ); 1714 self.fuel_yield_interval = interval.and_then(|i| NonZeroU64::new(i)); 1715 // Reset the fuel active + reserve states by resetting the amount. 1716 self.set_fuel(self.get_fuel()?) 1717 } 1718 1719 #[inline] 1720 pub fn signal_handler(&self) -> Option<*const SignalHandler> { 1721 let handler = self.signal_handler.as_ref()?; 1722 Some(handler) 1723 } 1724 1725 #[inline] 1726 pub fn vm_store_context_ptr(&self) -> NonNull<VMStoreContext> { 1727 NonNull::from(&self.vm_store_context) 1728 } 1729 1730 #[inline] 1731 pub fn default_caller(&self) -> NonNull<VMContext> { 1732 self.default_caller_vmctx.as_non_null() 1733 } 1734 1735 #[inline] 1736 pub fn traitobj(&self) -> NonNull<dyn vm::VMStore> { 1737 self.traitobj.as_raw().unwrap() 1738 } 1739 1740 #[inline] 1741 pub fn traitobj_mut(&mut self) -> &mut dyn vm::VMStore { 1742 unsafe { self.traitobj().as_mut() } 1743 } 1744 1745 /// Takes the cached `Vec<Val>` stored internally across hostcalls to get 1746 /// used as part of calling the host in a `Func::new` method invocation. 1747 #[inline] 1748 pub fn take_hostcall_val_storage(&mut self) -> Vec<Val> { 1749 mem::take(&mut self.hostcall_val_storage) 1750 } 1751 1752 /// Restores the vector previously taken by `take_hostcall_val_storage` 1753 /// above back into the store, allowing it to be used in the future for the 1754 /// next wasm->host call. 1755 #[inline] 1756 pub fn save_hostcall_val_storage(&mut self, storage: Vec<Val>) { 1757 if storage.capacity() > self.hostcall_val_storage.capacity() { 1758 self.hostcall_val_storage = storage; 1759 } 1760 } 1761 1762 /// Same as `take_hostcall_val_storage`, but for the direction of the host 1763 /// calling wasm. 1764 #[inline] 1765 pub fn take_wasm_val_raw_storage(&mut self) -> Vec<ValRaw> { 1766 mem::take(&mut self.wasm_val_raw_storage) 1767 } 1768 1769 /// Same as `save_hostcall_val_storage`, but for the direction of the host 1770 /// calling wasm. 1771 #[inline] 1772 pub fn save_wasm_val_raw_storage(&mut self, storage: Vec<ValRaw>) { 1773 if storage.capacity() > self.wasm_val_raw_storage.capacity() { 1774 self.wasm_val_raw_storage = storage; 1775 } 1776 } 1777 1778 /// Translates a WebAssembly fault at the native `pc` and native `addr` to a 1779 /// WebAssembly-relative fault. 1780 /// 1781 /// This function may abort the process if `addr` is not found to actually 1782 /// reside in any linear memory. In such a situation it means that the 1783 /// segfault was erroneously caught by Wasmtime and is possibly indicative 1784 /// of a code generator bug. 1785 /// 1786 /// This function returns `None` for dynamically-bounds-checked-memories 1787 /// with spectre mitigations enabled since the hardware fault address is 1788 /// always zero in these situations which means that the trapping context 1789 /// doesn't have enough information to report the fault address. 1790 pub(crate) fn wasm_fault(&self, pc: usize, addr: usize) -> Option<vm::WasmFault> { 1791 // There are a few instances where a "close to zero" pointer is loaded 1792 // and we expect that to happen: 1793 // 1794 // * Explicitly bounds-checked memories with spectre-guards enabled will 1795 // cause out-of-bounds accesses to get routed to address 0, so allow 1796 // wasm instructions to fault on the null address. 1797 // * `call_indirect` when invoking a null function pointer may load data 1798 // from the a `VMFuncRef` whose address is null, meaning any field of 1799 // `VMFuncRef` could be the address of the fault. 1800 // 1801 // In these situations where the address is so small it won't be in any 1802 // instance, so skip the checks below. 1803 if addr <= mem::size_of::<VMFuncRef>() { 1804 const _: () = { 1805 // static-assert that `VMFuncRef` isn't too big to ensure that 1806 // it lives solely within the first page as we currently only 1807 // have the guarantee that the first page of memory is unmapped, 1808 // no more. 1809 assert!(mem::size_of::<VMFuncRef>() <= 512); 1810 }; 1811 return None; 1812 } 1813 1814 // Search all known instances in this store for this address. Note that 1815 // this is probably not the speediest way to do this. Traps, however, 1816 // are generally not expected to be super fast and additionally stores 1817 // probably don't have all that many instances or memories. 1818 // 1819 // If this loop becomes hot in the future, however, it should be 1820 // possible to precompute maps about linear memories in a store and have 1821 // a quicker lookup. 1822 let mut fault = None; 1823 for (_, instance) in self.instances.iter() { 1824 if let Some(f) = instance.handle.get().wasm_fault(addr) { 1825 assert!(fault.is_none()); 1826 fault = Some(f); 1827 } 1828 } 1829 if fault.is_some() { 1830 return fault; 1831 } 1832 1833 cfg_if::cfg_if! { 1834 if #[cfg(any(feature = "std", unix, windows))] { 1835 // With the standard library a rich error can be printed here 1836 // to stderr and the native abort path is used. 1837 eprintln!( 1838 "\ 1839 Wasmtime caught a segfault for a wasm program because the faulting instruction 1840 is allowed to segfault due to how linear memories are implemented. The address 1841 that was accessed, however, is not known to any linear memory in use within this 1842 Store. This may be indicative of a critical bug in Wasmtime's code generation 1843 because all addresses which are known to be reachable from wasm won't reach this 1844 message. 1845 1846 pc: 0x{pc:x} 1847 address: 0x{addr:x} 1848 1849 This is a possible security issue because WebAssembly has accessed something it 1850 shouldn't have been able to. Other accesses may have succeeded and this one just 1851 happened to be caught. The process will now be aborted to prevent this damage 1852 from going any further and to alert what's going on. If this is a security 1853 issue please reach out to the Wasmtime team via its security policy 1854 at https://bytecodealliance.org/security. 1855 " 1856 ); 1857 std::process::abort(); 1858 } else if #[cfg(panic = "abort")] { 1859 // Without the standard library but with `panic=abort` then 1860 // it's safe to panic as that's known to halt execution. For 1861 // now avoid the above error message as well since without 1862 // `std` it's probably best to be a bit more size-conscious. 1863 let _ = pc; 1864 panic!("invalid fault"); 1865 } else { 1866 // Without `std` and with `panic = "unwind"` there's no way to 1867 // abort the process portably, so flag a compile time error. 1868 // 1869 // NB: if this becomes a problem in the future one option would 1870 // be to extend the `capi.rs` module for no_std platforms, but 1871 // it remains yet to be seen at this time if this is hit much. 1872 compile_error!("either `std` or `panic=abort` must be enabled"); 1873 None 1874 } 1875 } 1876 } 1877 1878 /// Retrieve the store's protection key. 1879 #[inline] 1880 pub(crate) fn get_pkey(&self) -> Option<ProtectionKey> { 1881 self.pkey 1882 } 1883 1884 #[inline] 1885 #[cfg(feature = "component-model")] 1886 pub(crate) fn component_resource_state( 1887 &mut self, 1888 ) -> ( 1889 &mut vm::component::CallContexts, 1890 &mut vm::component::ResourceTable, 1891 &mut crate::component::HostResourceData, 1892 ) { 1893 ( 1894 &mut self.component_calls, 1895 &mut self.component_host_table, 1896 &mut self.host_resource_data, 1897 ) 1898 } 1899 1900 #[cfg(feature = "component-model")] 1901 pub(crate) fn push_component_instance(&mut self, instance: crate::component::Instance) { 1902 // We don't actually need the instance itself right now, but it seems 1903 // like something we will almost certainly eventually want to keep 1904 // around, so force callers to provide it. 1905 let _ = instance; 1906 1907 self.num_component_instances += 1; 1908 } 1909 1910 #[cfg(feature = "component-model")] 1911 pub(crate) fn component_resource_state_with_instance( 1912 &mut self, 1913 instance: crate::component::Instance, 1914 ) -> ( 1915 &mut vm::component::CallContexts, 1916 &mut vm::component::ResourceTable, 1917 &mut crate::component::HostResourceData, 1918 Pin<&mut vm::component::ComponentInstance>, 1919 ) { 1920 ( 1921 &mut self.component_calls, 1922 &mut self.component_host_table, 1923 &mut self.host_resource_data, 1924 instance.id().from_data_get_mut(&mut self.store_data), 1925 ) 1926 } 1927 1928 #[cfg(not(feature = "async"))] 1929 pub(crate) fn async_guard_range(&self) -> core::ops::Range<*mut u8> { 1930 core::ptr::null_mut()..core::ptr::null_mut() 1931 } 1932 1933 pub(crate) fn executor(&mut self) -> ExecutorRef<'_> { 1934 match &mut self.executor { 1935 Executor::Interpreter(i) => ExecutorRef::Interpreter(i.as_interpreter_ref()), 1936 #[cfg(has_host_compiler_backend)] 1937 Executor::Native => ExecutorRef::Native, 1938 } 1939 } 1940 1941 pub(crate) fn unwinder(&self) -> &'static dyn Unwind { 1942 match &self.executor { 1943 Executor::Interpreter(i) => i.unwinder(), 1944 #[cfg(has_host_compiler_backend)] 1945 Executor::Native => &vm::UnwindHost, 1946 } 1947 } 1948 1949 /// Allocates a new continuation. Note that we currently don't support 1950 /// deallocating them. Instead, all continuations remain allocated 1951 /// throughout the store's lifetime. 1952 #[cfg(feature = "stack-switching")] 1953 pub fn allocate_continuation(&mut self) -> Result<*mut VMContRef> { 1954 // FIXME(frank-emrich) Do we need to pin this? 1955 let mut continuation = Box::new(VMContRef::empty()); 1956 let stack_size = self.engine.config().async_stack_size; 1957 let stack = crate::vm::VMContinuationStack::new(stack_size)?; 1958 continuation.stack = stack; 1959 let ptr = continuation.deref_mut() as *mut VMContRef; 1960 self.continuations.push(continuation); 1961 Ok(ptr) 1962 } 1963 1964 /// Constructs and executes an `InstanceAllocationRequest` and pushes the 1965 /// returned instance into the store. 1966 /// 1967 /// This is a helper method for invoking 1968 /// `InstanceAllocator::allocate_module` with the appropriate parameters 1969 /// from this store's own configuration. The `kind` provided is used to 1970 /// distinguish between "real" modules and dummy ones that are synthesized 1971 /// for embedder-created memories, globals, tables, etc. The `kind` will 1972 /// also use a different instance allocator by default, the one passed in, 1973 /// rather than the engine's default allocator. 1974 /// 1975 /// This method will push the instance within `StoreOpaque` onto the 1976 /// `instances` array and return the `InstanceId` which can be use to look 1977 /// it up within the store. 1978 /// 1979 /// # Safety 1980 /// 1981 /// The request's associated module, memories, tables, and vmctx must have 1982 /// already have been validated by `validate_module` for the allocator 1983 /// configured. This is typically done during module construction for 1984 /// example. 1985 pub(crate) unsafe fn allocate_instance( 1986 &mut self, 1987 kind: AllocateInstanceKind<'_>, 1988 runtime_info: &ModuleRuntimeInfo, 1989 imports: Imports<'_>, 1990 ) -> Result<InstanceId> { 1991 let id = self.instances.next_key(); 1992 1993 let allocator = match kind { 1994 AllocateInstanceKind::Module(_) => self.engine().allocator(), 1995 AllocateInstanceKind::Dummy { allocator } => allocator, 1996 }; 1997 let handle = allocator.allocate_module(InstanceAllocationRequest { 1998 id, 1999 runtime_info, 2000 imports, 2001 store: StorePtr::new(self.traitobj()), 2002 wmemcheck: self.engine().config().wmemcheck, 2003 pkey: self.get_pkey(), 2004 tunables: self.engine().tunables(), 2005 })?; 2006 2007 let actual = match kind { 2008 AllocateInstanceKind::Module(module_id) => { 2009 log::trace!( 2010 "Adding instance to store: store={:?}, module={module_id:?}, instance={id:?}", 2011 self.id() 2012 ); 2013 self.instances.push(StoreInstance { 2014 handle, 2015 kind: StoreInstanceKind::Real { module_id }, 2016 }) 2017 } 2018 AllocateInstanceKind::Dummy { .. } => { 2019 log::trace!( 2020 "Adding dummy instance to store: store={:?}, instance={id:?}", 2021 self.id() 2022 ); 2023 self.instances.push(StoreInstance { 2024 handle, 2025 kind: StoreInstanceKind::Dummy, 2026 }) 2027 } 2028 }; 2029 2030 // double-check we didn't accidentally allocate two instances and our 2031 // prediction of what the id would be is indeed the id it should be. 2032 assert_eq!(id, actual); 2033 2034 Ok(id) 2035 } 2036 2037 /// Returns the `StoreInstanceId` that can be used to re-acquire access to 2038 /// `vmctx` from a store later on. 2039 /// 2040 /// # Safety 2041 /// 2042 /// This method is unsafe as it cannot validate that `vmctx` is a valid 2043 /// allocation that lives within this store. 2044 pub(crate) unsafe fn vmctx_id(&self, vmctx: NonNull<VMContext>) -> StoreInstanceId { 2045 let instance_id = vm::Instance::from_vmctx(vmctx, |i| i.id()); 2046 StoreInstanceId::new(self.id(), instance_id) 2047 } 2048 } 2049 2050 /// Helper parameter to [`StoreOpaque::allocate_instance`]. 2051 pub(crate) enum AllocateInstanceKind<'a> { 2052 /// An embedder-provided module is being allocated meaning that the default 2053 /// engine's allocator will be used. 2054 Module(RegisteredModuleId), 2055 2056 /// Add a dummy instance that to the store. 2057 /// 2058 /// These are instances that are just implementation details of something 2059 /// else (e.g. host-created memories that are not actually defined in any 2060 /// Wasm module) and therefore shouldn't show up in things like core dumps. 2061 /// 2062 /// A custom, typically OnDemand-flavored, allocator is provided to execute 2063 /// the allocation. 2064 Dummy { 2065 allocator: &'a dyn InstanceAllocator, 2066 }, 2067 } 2068 2069 unsafe impl<T> vm::VMStore for StoreInner<T> { 2070 #[cfg(feature = "component-model-async")] 2071 fn component_async_store( 2072 &mut self, 2073 ) -> &mut dyn crate::runtime::component::VMComponentAsyncStore { 2074 self 2075 } 2076 2077 fn store_opaque(&self) -> &StoreOpaque { 2078 &self.inner 2079 } 2080 2081 fn store_opaque_mut(&mut self) -> &mut StoreOpaque { 2082 &mut self.inner 2083 } 2084 2085 fn memory_growing( 2086 &mut self, 2087 current: usize, 2088 desired: usize, 2089 maximum: Option<usize>, 2090 ) -> Result<bool, anyhow::Error> { 2091 match self.limiter { 2092 Some(ResourceLimiterInner::Sync(ref mut limiter)) => { 2093 limiter(&mut self.data).memory_growing(current, desired, maximum) 2094 } 2095 #[cfg(feature = "async")] 2096 Some(ResourceLimiterInner::Async(ref mut limiter)) => unsafe { 2097 self.inner 2098 .async_cx() 2099 .expect("ResourceLimiterAsync requires async Store") 2100 .block_on( 2101 limiter(&mut self.data) 2102 .memory_growing(current, desired, maximum) 2103 .as_mut(), 2104 )? 2105 }, 2106 None => Ok(true), 2107 } 2108 } 2109 2110 fn memory_grow_failed(&mut self, error: anyhow::Error) -> Result<()> { 2111 match self.limiter { 2112 Some(ResourceLimiterInner::Sync(ref mut limiter)) => { 2113 limiter(&mut self.data).memory_grow_failed(error) 2114 } 2115 #[cfg(feature = "async")] 2116 Some(ResourceLimiterInner::Async(ref mut limiter)) => { 2117 limiter(&mut self.data).memory_grow_failed(error) 2118 } 2119 None => { 2120 log::debug!("ignoring memory growth failure error: {error:?}"); 2121 Ok(()) 2122 } 2123 } 2124 } 2125 2126 fn table_growing( 2127 &mut self, 2128 current: usize, 2129 desired: usize, 2130 maximum: Option<usize>, 2131 ) -> Result<bool, anyhow::Error> { 2132 // Need to borrow async_cx before the mut borrow of the limiter. 2133 // self.async_cx() panicks when used with a non-async store, so 2134 // wrap this in an option. 2135 #[cfg(feature = "async")] 2136 let async_cx = if self.async_support() 2137 && matches!(self.limiter, Some(ResourceLimiterInner::Async(_))) 2138 { 2139 Some(self.async_cx().unwrap()) 2140 } else { 2141 None 2142 }; 2143 2144 match self.limiter { 2145 Some(ResourceLimiterInner::Sync(ref mut limiter)) => { 2146 limiter(&mut self.data).table_growing(current, desired, maximum) 2147 } 2148 #[cfg(feature = "async")] 2149 Some(ResourceLimiterInner::Async(ref mut limiter)) => unsafe { 2150 async_cx 2151 .expect("ResourceLimiterAsync requires async Store") 2152 .block_on(limiter(&mut self.data).table_growing(current, desired, maximum))? 2153 }, 2154 None => Ok(true), 2155 } 2156 } 2157 2158 fn table_grow_failed(&mut self, error: anyhow::Error) -> Result<()> { 2159 match self.limiter { 2160 Some(ResourceLimiterInner::Sync(ref mut limiter)) => { 2161 limiter(&mut self.data).table_grow_failed(error) 2162 } 2163 #[cfg(feature = "async")] 2164 Some(ResourceLimiterInner::Async(ref mut limiter)) => { 2165 limiter(&mut self.data).table_grow_failed(error) 2166 } 2167 None => { 2168 log::debug!("ignoring table growth failure: {error:?}"); 2169 Ok(()) 2170 } 2171 } 2172 } 2173 2174 fn out_of_gas(&mut self) -> Result<()> { 2175 if !self.refuel() { 2176 return Err(Trap::OutOfFuel.into()); 2177 } 2178 #[cfg(feature = "async")] 2179 if self.fuel_yield_interval.is_some() { 2180 self.async_yield_impl()?; 2181 } 2182 Ok(()) 2183 } 2184 2185 #[cfg(target_has_atomic = "64")] 2186 fn new_epoch(&mut self) -> Result<u64, anyhow::Error> { 2187 // Temporarily take the configured behavior to avoid mutably borrowing 2188 // multiple times. 2189 let mut behavior = self.epoch_deadline_behavior.take(); 2190 let delta_result = match &mut behavior { 2191 None => Err(Trap::Interrupt.into()), 2192 Some(callback) => callback((&mut *self).as_context_mut()).and_then(|update| { 2193 let delta = match update { 2194 UpdateDeadline::Continue(delta) => delta, 2195 #[cfg(feature = "async")] 2196 UpdateDeadline::Yield(delta) => { 2197 assert!( 2198 self.async_support(), 2199 "cannot use `UpdateDeadline::Yield` without enabling async support in the config" 2200 ); 2201 // Do the async yield. May return a trap if future was 2202 // canceled while we're yielded. 2203 self.async_yield_impl()?; 2204 delta 2205 } 2206 #[cfg(feature = "async")] 2207 UpdateDeadline::YieldCustom(delta, future) => { 2208 assert!( 2209 self.async_support(), 2210 "cannot use `UpdateDeadline::YieldCustom` without enabling async support in the config" 2211 ); 2212 2213 // When control returns, we have a `Result<()>` passed 2214 // in from the host fiber. If this finished successfully then 2215 // we were resumed normally via a `poll`, so keep going. If 2216 // the future was dropped while we were yielded, then we need 2217 // to clean up this fiber. Do so by raising a trap which will 2218 // abort all wasm and get caught on the other side to clean 2219 // things up. 2220 unsafe { 2221 self.async_cx() 2222 .expect("attempted to pull async context during shutdown") 2223 .block_on(future)? 2224 } 2225 delta 2226 } 2227 }; 2228 2229 // Set a new deadline and return the new epoch deadline so 2230 // the Wasm code doesn't have to reload it. 2231 self.set_epoch_deadline(delta); 2232 Ok(self.get_epoch_deadline()) 2233 }) 2234 }; 2235 2236 // Put back the original behavior which was replaced by `take`. 2237 self.epoch_deadline_behavior = behavior; 2238 delta_result 2239 } 2240 2241 #[cfg(feature = "gc")] 2242 unsafe fn maybe_async_grow_or_collect_gc_heap( 2243 &mut self, 2244 root: Option<VMGcRef>, 2245 bytes_needed: Option<u64>, 2246 ) -> Result<Option<VMGcRef>> { 2247 self.inner.maybe_async_gc(root, bytes_needed) 2248 } 2249 2250 #[cfg(not(feature = "gc"))] 2251 unsafe fn maybe_async_grow_or_collect_gc_heap( 2252 &mut self, 2253 root: Option<VMGcRef>, 2254 _bytes_needed: Option<u64>, 2255 ) -> Result<Option<VMGcRef>> { 2256 Ok(root) 2257 } 2258 2259 #[cfg(feature = "component-model")] 2260 fn component_calls(&mut self) -> &mut vm::component::CallContexts { 2261 &mut self.component_calls 2262 } 2263 } 2264 2265 impl<T> StoreInner<T> { 2266 #[cfg(target_has_atomic = "64")] 2267 pub(crate) fn set_epoch_deadline(&mut self, delta: u64) { 2268 // Set a new deadline based on the "epoch deadline delta". 2269 // 2270 // Also, note that when this update is performed while Wasm is 2271 // on the stack, the Wasm will reload the new value once we 2272 // return into it. 2273 let current_epoch = self.engine().current_epoch(); 2274 let epoch_deadline = self.vm_store_context.epoch_deadline.get_mut(); 2275 *epoch_deadline = current_epoch + delta; 2276 } 2277 2278 #[cfg(target_has_atomic = "64")] 2279 fn epoch_deadline_trap(&mut self) { 2280 self.epoch_deadline_behavior = None; 2281 } 2282 2283 #[cfg(target_has_atomic = "64")] 2284 fn epoch_deadline_callback( 2285 &mut self, 2286 callback: Box<dyn FnMut(StoreContextMut<T>) -> Result<UpdateDeadline> + Send + Sync>, 2287 ) { 2288 self.epoch_deadline_behavior = Some(callback); 2289 } 2290 2291 fn get_epoch_deadline(&mut self) -> u64 { 2292 *self.vm_store_context.epoch_deadline.get_mut() 2293 } 2294 } 2295 2296 impl<T: Default> Default for Store<T> { 2297 fn default() -> Store<T> { 2298 Store::new(&Engine::default(), T::default()) 2299 } 2300 } 2301 2302 impl<T: fmt::Debug> fmt::Debug for Store<T> { 2303 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 2304 let inner = &**self.inner as *const StoreInner<T>; 2305 f.debug_struct("Store") 2306 .field("inner", &inner) 2307 .field("data", &self.inner.data) 2308 .finish() 2309 } 2310 } 2311 2312 impl<T> Drop for Store<T> { 2313 fn drop(&mut self) { 2314 self.inner.flush_fiber_stack(); 2315 2316 // for documentation on this `unsafe`, see `into_data`. 2317 unsafe { 2318 ManuallyDrop::drop(&mut self.inner.data); 2319 ManuallyDrop::drop(&mut self.inner); 2320 } 2321 } 2322 } 2323 2324 impl Drop for StoreOpaque { 2325 fn drop(&mut self) { 2326 // NB it's important that this destructor does not access `self.data`. 2327 // That is deallocated by `Drop for Store<T>` above. 2328 2329 unsafe { 2330 let allocator = self.engine.allocator(); 2331 let ondemand = OnDemandInstanceAllocator::default(); 2332 let store_id = self.id(); 2333 2334 #[cfg(feature = "gc")] 2335 if let Some(gc_store) = self.gc_store.take() { 2336 let gc_alloc_index = gc_store.allocation_index; 2337 log::trace!("store {store_id:?} is deallocating GC heap {gc_alloc_index:?}"); 2338 debug_assert!(self.engine.features().gc_types()); 2339 let (mem_alloc_index, mem) = 2340 allocator.deallocate_gc_heap(gc_alloc_index, gc_store.gc_heap); 2341 allocator.deallocate_memory(None, mem_alloc_index, mem); 2342 } 2343 2344 for (id, instance) in self.instances.iter_mut() { 2345 log::trace!("store {store_id:?} is deallocating {id:?}"); 2346 if let StoreInstanceKind::Dummy = instance.kind { 2347 ondemand.deallocate_module(&mut instance.handle); 2348 } else { 2349 allocator.deallocate_module(&mut instance.handle); 2350 } 2351 } 2352 2353 #[cfg(feature = "component-model")] 2354 { 2355 for _ in 0..self.num_component_instances { 2356 allocator.decrement_component_instance_count(); 2357 } 2358 } 2359 } 2360 } 2361 } 2362 2363 #[cfg(test)] 2364 mod tests { 2365 use super::{get_fuel, refuel, set_fuel}; 2366 use std::num::NonZeroU64; 2367 2368 struct FuelTank { 2369 pub consumed_fuel: i64, 2370 pub reserve_fuel: u64, 2371 pub yield_interval: Option<NonZeroU64>, 2372 } 2373 2374 impl FuelTank { 2375 fn new() -> Self { 2376 FuelTank { 2377 consumed_fuel: 0, 2378 reserve_fuel: 0, 2379 yield_interval: None, 2380 } 2381 } 2382 fn get_fuel(&self) -> u64 { 2383 get_fuel(self.consumed_fuel, self.reserve_fuel) 2384 } 2385 fn refuel(&mut self) -> bool { 2386 refuel( 2387 &mut self.consumed_fuel, 2388 &mut self.reserve_fuel, 2389 self.yield_interval, 2390 ) 2391 } 2392 fn set_fuel(&mut self, fuel: u64) { 2393 set_fuel( 2394 &mut self.consumed_fuel, 2395 &mut self.reserve_fuel, 2396 self.yield_interval, 2397 fuel, 2398 ); 2399 } 2400 } 2401 2402 #[test] 2403 fn smoke() { 2404 let mut tank = FuelTank::new(); 2405 tank.set_fuel(10); 2406 assert_eq!(tank.consumed_fuel, -10); 2407 assert_eq!(tank.reserve_fuel, 0); 2408 2409 tank.yield_interval = NonZeroU64::new(10); 2410 tank.set_fuel(25); 2411 assert_eq!(tank.consumed_fuel, -10); 2412 assert_eq!(tank.reserve_fuel, 15); 2413 } 2414 2415 #[test] 2416 fn does_not_lose_precision() { 2417 let mut tank = FuelTank::new(); 2418 tank.set_fuel(u64::MAX); 2419 assert_eq!(tank.get_fuel(), u64::MAX); 2420 2421 tank.set_fuel(i64::MAX as u64); 2422 assert_eq!(tank.get_fuel(), i64::MAX as u64); 2423 2424 tank.set_fuel(i64::MAX as u64 + 1); 2425 assert_eq!(tank.get_fuel(), i64::MAX as u64 + 1); 2426 } 2427 2428 #[test] 2429 fn yielding_does_not_lose_precision() { 2430 let mut tank = FuelTank::new(); 2431 2432 tank.yield_interval = NonZeroU64::new(10); 2433 tank.set_fuel(u64::MAX); 2434 assert_eq!(tank.get_fuel(), u64::MAX); 2435 assert_eq!(tank.consumed_fuel, -10); 2436 assert_eq!(tank.reserve_fuel, u64::MAX - 10); 2437 2438 tank.yield_interval = NonZeroU64::new(u64::MAX); 2439 tank.set_fuel(u64::MAX); 2440 assert_eq!(tank.get_fuel(), u64::MAX); 2441 assert_eq!(tank.consumed_fuel, -i64::MAX); 2442 assert_eq!(tank.reserve_fuel, u64::MAX - (i64::MAX as u64)); 2443 2444 tank.yield_interval = NonZeroU64::new((i64::MAX as u64) + 1); 2445 tank.set_fuel(u64::MAX); 2446 assert_eq!(tank.get_fuel(), u64::MAX); 2447 assert_eq!(tank.consumed_fuel, -i64::MAX); 2448 assert_eq!(tank.reserve_fuel, u64::MAX - (i64::MAX as u64)); 2449 } 2450 2451 #[test] 2452 fn refueling() { 2453 // It's possible to fuel to have consumed over the limit as some instructions can consume 2454 // multiple units of fuel at once. Refueling should be strict in it's consumption and not 2455 // add more fuel than there is. 2456 let mut tank = FuelTank::new(); 2457 2458 tank.yield_interval = NonZeroU64::new(10); 2459 tank.reserve_fuel = 42; 2460 tank.consumed_fuel = 4; 2461 assert!(tank.refuel()); 2462 assert_eq!(tank.reserve_fuel, 28); 2463 assert_eq!(tank.consumed_fuel, -10); 2464 2465 tank.yield_interval = NonZeroU64::new(1); 2466 tank.reserve_fuel = 8; 2467 tank.consumed_fuel = 4; 2468 assert_eq!(tank.get_fuel(), 4); 2469 assert!(tank.refuel()); 2470 assert_eq!(tank.reserve_fuel, 3); 2471 assert_eq!(tank.consumed_fuel, -1); 2472 assert_eq!(tank.get_fuel(), 4); 2473 2474 tank.yield_interval = NonZeroU64::new(10); 2475 tank.reserve_fuel = 3; 2476 tank.consumed_fuel = 4; 2477 assert_eq!(tank.get_fuel(), 0); 2478 assert!(!tank.refuel()); 2479 assert_eq!(tank.reserve_fuel, 3); 2480 assert_eq!(tank.consumed_fuel, 4); 2481 assert_eq!(tank.get_fuel(), 0); 2482 } 2483 } 2484