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