wasmtime/runtime/vm/traphandlers.rs
1//! WebAssembly trap handling, which is built on top of the lower-level
2//! signalhandling mechanisms.
3
4#![cfg_attr(
5 all(not(has_native_signals), not(feature = "pulley")),
6 expect(unused, reason = "easier to not #[cfg] methods and all related types")
7)]
8
9mod backtrace;
10
11#[cfg(feature = "coredump")]
12#[path = "traphandlers/coredump_enabled.rs"]
13mod coredump;
14#[cfg(not(feature = "coredump"))]
15#[path = "traphandlers/coredump_disabled.rs"]
16mod coredump;
17
18#[cfg(all(has_native_signals))]
19mod signals;
20#[cfg(all(has_native_signals))]
21pub use self::signals::*;
22
23#[cfg(feature = "gc")]
24use crate::ThrownException;
25use crate::runtime::module::lookup_code;
26use crate::runtime::store::{ExecutorRef, StoreOpaque};
27use crate::runtime::vm::sys::traphandlers;
28use crate::runtime::vm::{InterpreterRef, VMContext, VMStore, VMStoreContext, f32x4, f64x2, i8x16};
29use crate::{EntryStoreContext, prelude::*};
30use crate::{StoreContextMut, WasmBacktrace};
31use core::cell::Cell;
32use core::num::NonZeroU32;
33use core::ptr::{self, NonNull};
34use wasmtime_unwinder::Handler;
35
36#[cfg(feature = "debug")]
37pub(crate) use self::backtrace::Activation;
38pub use self::backtrace::Backtrace;
39#[cfg(feature = "gc")]
40pub use wasmtime_unwinder::Frame;
41
42pub use self::coredump::CoreDumpStack;
43pub use self::tls::tls_eager_initialize;
44#[cfg(feature = "async")]
45pub use self::tls::{AsyncWasmCallState, PreviousAsyncWasmCallState};
46
47pub use traphandlers::SignalHandler;
48
49pub(crate) struct TrapRegisters {
50 pub pc: usize,
51 pub fp: usize,
52}
53
54/// Return value from `test_if_trap`.
55pub(crate) enum TrapTest {
56 /// Not a wasm trap, need to delegate to whatever process handler is next.
57 NotWasm,
58 /// This trap was handled by the embedder via custom embedding APIs.
59 #[cfg(all(has_native_signals, not(miri)))]
60 HandledByEmbedder,
61 /// This is a wasm trap, it needs to be handled.
62 Trap(Handler),
63}
64
65fn lazy_per_thread_init() {
66 traphandlers::lazy_per_thread_init();
67}
68
69/// Raises a preexisting trap or exception and unwinds.
70///
71/// If the preexisting state has registered a trap, this function will execute
72/// the `Handler::resume` to make its way back to the original exception
73/// handler created when Wasm was entered. If the state has registered an
74/// exception, this function will perform the unwind action registered: either
75/// resetting PC, FP, and SP to the handler in the middle of the Wasm
76/// activation on the stack, or the entry trampoline back to the the host, if
77/// the exception is uncaught.
78///
79/// This is currently only called from the `raise` builtin of
80/// Wasmtime. This builtin is only used when the host returns back to
81/// wasm and indicates that a trap or exception should be raised. In
82/// this situation the host has already stored trap or exception
83/// information within the `CallThreadState` and this is the low-level
84/// operation to actually perform an unwind.
85///
86/// Note that this function is used both for Pulley and for native execution.
87/// For Pulley this function will return and the interpreter will be
88/// responsible for handling the control-flow transfer. For native this
89/// function will not return as the control flow transfer will be handled
90/// internally.
91///
92/// # Safety
93///
94/// Only safe to call when wasm code is on the stack, aka `catch_traps` must
95/// have been previously called. Additionally no Rust destructors can be on the
96/// stack. They will be skipped and not executed.
97pub(super) unsafe fn raise_preexisting_trap(store: &mut dyn VMStore) {
98 tls::with(|info| unsafe { info.unwrap().unwind(store) })
99}
100
101/// Invokes the closure `f` and handles any error/panic/trap that happens
102/// within.
103///
104/// This will invoke the closure `f` with the provided `store` and the closure
105/// will return a value that implements `HostResult`. This trait abstracts over
106/// how host values are translated to ABI values when going back into wasm.
107/// Some examples are:
108///
109/// * `T` - bare return types (not results) are simply returned as-is. No
110/// `catch_unwind` happens as if a trap can't happen then the host shouldn't
111/// be panicking or invoking user code.
112///
113/// * `Result<(), E>` - this represents an ABI return value of `bool` which
114/// indicates whether the call succeeded. This return value will catch panics
115/// and record trap information as `E`.
116///
117/// * `Result<u32, E>` - the ABI return value here is `u64` where on success
118/// the 32-bit result is zero-extended and `u64::MAX` as a return value
119/// indicates that a trap or panic happened.
120///
121/// This is primarily used in conjunction with the Cranelift-and-host boundary.
122/// This function acts as a bridge between the two to appropriately handle
123/// encoding host values to Cranelift-understood ABIs via the `HostResult`
124/// trait.
125pub fn catch_unwind_and_record_trap<R>(
126 store: &mut dyn VMStore,
127 f: impl FnOnce(&mut dyn VMStore) -> R,
128) -> R::Abi
129where
130 R: HostResult,
131{
132 // Invoke the closure `f`, optionally catching unwinds depending on `R`. The
133 // return value is always provided and if unwind information is provided
134 // (e.g. `ret` is a "false"-y value) then it's recorded in TLS for the
135 // unwind operation that's about to happen from Cranelift-generated code.
136 let (ret, unwind) = R::maybe_catch_unwind(store, |store| f(store));
137 if let Some(unwind) = unwind {
138 tls::with(|info| info.unwrap().record_unwind(unwind));
139 }
140 ret
141}
142
143/// A trait used in conjunction with `catch_unwind_and_record_trap` to convert a
144/// Rust-based type to a specific ABI while handling traps/unwinds.
145///
146/// This type is implemented for return values from host function calls and
147/// libcalls. The `Abi` value of this trait represents either a successful
148/// execution with some payload state or that a failed execution happened. In
149/// the event of a failed execution the state of the failure itself is stored
150/// within `CallThreadState::unwind`. Cranelift-compiled code is expected to
151/// test for this failure sentinel and process it accordingly.
152///
153/// See `catch_unwind_and_record_trap` for some more information as well.
154pub trait HostResult {
155 /// The type of the value that's returned to Cranelift-compiled code. Needs
156 /// to be ABI-safe to pass through an `extern "C"` return value.
157 type Abi: Copy;
158
159 /// Executes `f` and returns the ABI/unwind information as a result.
160 ///
161 /// This may optionally catch unwinds during execution depending on this
162 /// implementation. The ABI return value is unconditionally provided. If an
163 /// unwind was detected (e.g. a host panic or a wasm trap) then that's
164 /// additionally returned as well.
165 ///
166 /// If an unwind is returned then it's expected that when the host returns
167 /// back to wasm (which should be soon after calling this through
168 /// `catch_unwind_and_record_trap`) then wasm will very quickly turn around
169 /// and initiate an unwind (currently through `raise_preexisting_trap`).
170 fn maybe_catch_unwind(
171 store: &mut dyn VMStore,
172 f: impl FnOnce(&mut dyn VMStore) -> Self,
173 ) -> (Self::Abi, Option<UnwindReason>);
174}
175
176// Base case implementations that do not catch unwinds. These are for libcalls
177// that neither trap nor execute user code. The raw value is the ABI itself.
178//
179// Panics in these libcalls will result in a process abort as unwinding is not
180// allowed via Rust through `extern "C"` function boundaries.
181macro_rules! host_result_no_catch {
182 ($($t:ty,)*) => {
183 $(
184 impl HostResult for $t {
185 type Abi = $t;
186 #[allow(unreachable_code, reason = "some types uninhabited on some platforms")]
187 fn maybe_catch_unwind(
188 store: &mut dyn VMStore,
189 f: impl FnOnce(&mut dyn VMStore) -> $t,
190 ) -> ($t, Option<UnwindReason>) {
191 (f(store), None)
192 }
193 }
194 )*
195 }
196}
197
198host_result_no_catch! {
199 (),
200 bool,
201 u32,
202 *mut u8,
203 u64,
204 f32,
205 f64,
206 usize,
207 i8x16,
208 f32x4,
209 f64x2,
210}
211
212impl HostResult for NonNull<u8> {
213 type Abi = *mut u8;
214 fn maybe_catch_unwind(
215 store: &mut dyn VMStore,
216 f: impl FnOnce(&mut dyn VMStore) -> Self,
217 ) -> (*mut u8, Option<UnwindReason>) {
218 (f(store).as_ptr(), None)
219 }
220}
221
222/// Implementation of `HostResult` for `Result<T, E>`.
223///
224/// This is where things get interesting for `HostResult`. This is generically
225/// defined to allow many shapes of the `Result` type to be returned from host
226/// calls or libcalls. To do this an extra trait requirement is placed on the
227/// successful result `T`: `HostResultHasUnwindSentinel`.
228///
229/// The general requirement is that `T` says what ABI it has, and the ABI must
230/// have a sentinel value which indicates that an unwind in wasm should happen.
231/// For example if `T = ()` then `true` means that the call succeeded and
232/// `false` means that an unwind happened. Here the sentinel is `false` and the
233/// ABI is `bool`.
234///
235/// This is the only implementation of `HostResult` which actually catches
236/// unwinds as there's a sentinel to encode.
237impl<T, E> HostResult for Result<T, E>
238where
239 T: HostResultHasUnwindSentinel,
240 E: Into<TrapReason>,
241{
242 type Abi = T::Abi;
243
244 fn maybe_catch_unwind(
245 store: &mut dyn VMStore,
246 f: impl FnOnce(&mut dyn VMStore) -> Result<T, E>,
247 ) -> (T::Abi, Option<UnwindReason>) {
248 // First wrap `f` in call hooks if that feature is enabled. This is used
249 // as a "pretty far down in the stack" mechanism of ensuring that hooks
250 // aren't forgotten.
251 //
252 // Note that by being placed here this is handling:
253 //
254 // * libcalls
255 // * host functions
256 // * component versions of the above
257 //
258 // This specifically is NOT handling libcalls where the result can't
259 // carry a result. This should be safe as anything which doesn't return
260 // a `Result` sort of has to be simple enough to not allow recursion so
261 // it's just a brief exit from the guest to the host.
262 //
263 // Also note that this only happens with the `call-hook` feature because
264 // this otherwise imposes a dynamic dispatch on the `store` trait object
265 // which otherwise can't be optimized away.
266 #[cfg(feature = "call-hook")]
267 let f = move |store: &mut dyn VMStore| {
268 store.call_hook(crate::CallHook::CallingHost)?;
269
270 let res = f(store);
271
272 // Note that if this returns a trap then `ret` is discarded
273 // entirely.
274 store.call_hook(crate::CallHook::ReturningFromHost)?;
275
276 res.map_err(|e| e.into())
277 };
278
279 // Next prepare the closure `f` as something that'll be invoked to
280 // generate the return value of this function. This is the
281 // conditionally, below, passed to `catch_unwind`.
282 let f = move || match f(store) {
283 Ok(ret) => {
284 let abi = ret.into_abi();
285 debug_assert!(abi != T::SENTINEL);
286 (abi, None)
287 }
288 Err(reason) => (T::SENTINEL, Some(UnwindReason::from(reason))),
289 };
290
291 // With `panic=unwind` use `std::panic::catch_unwind` to catch possible
292 // panics to rethrow.
293 #[cfg(all(feature = "std", panic = "unwind"))]
294 {
295 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)) {
296 Ok(result) => result,
297 Err(err) => (T::SENTINEL, Some(UnwindReason::Panic(err))),
298 }
299 }
300
301 // With `panic=abort` there's no use in using `std::panic::catch_unwind`
302 // since it won't actually catch anything. Note that
303 // `std::panic::catch_unwind` will technically optimize to this but having
304 // this branch avoids using the `std::panic` module entirely.
305 #[cfg(not(all(feature = "std", panic = "unwind")))]
306 {
307 f()
308 }
309 }
310}
311
312/// Trait used in conjunction with `HostResult for Result<T, E>` where this is
313/// the trait bound on `T`.
314///
315/// This is for values in the "ok" position of a `Result` return value. Each
316/// value can have a separate ABI from itself (e.g. `type Abi`) and must be
317/// convertible to the ABI. Additionally all implementations of this trait have
318/// a "sentinel value" which indicates that an unwind happened. This means that
319/// no valid instance of `Self` should generate the `SENTINEL` via the
320/// `into_abi` function.
321pub unsafe trait HostResultHasUnwindSentinel {
322 /// The Cranelift-understood ABI of this value (should not be `Self`).
323 type Abi: Copy + PartialEq;
324
325 /// A value that indicates that an unwind should happen and is tested for in
326 /// Cranelift-generated code.
327 const SENTINEL: Self::Abi;
328
329 /// Converts this value into the ABI representation. Should never returned
330 /// the `SENTINEL` value.
331 fn into_abi(self) -> Self::Abi;
332}
333
334/// No return value from the host is represented as a `bool` in the ABI. Here
335/// `true` means that execution succeeded while `false` is the sentinel used to
336/// indicate an unwind.
337unsafe impl HostResultHasUnwindSentinel for () {
338 type Abi = bool;
339 const SENTINEL: bool = false;
340 fn into_abi(self) -> bool {
341 true
342 }
343}
344
345unsafe impl HostResultHasUnwindSentinel for NonZeroU32 {
346 type Abi = u32;
347 const SENTINEL: Self::Abi = 0;
348 fn into_abi(self) -> Self::Abi {
349 self.get()
350 }
351}
352
353/// A 32-bit return value can be inflated to a 64-bit return value in the ABI.
354/// In this manner a successful result is a zero-extended 32-bit value and the
355/// failure sentinel is `u64::MAX` or -1 as a signed integer.
356unsafe impl HostResultHasUnwindSentinel for u32 {
357 type Abi = u64;
358 const SENTINEL: u64 = u64::MAX;
359 fn into_abi(self) -> u64 {
360 self.into()
361 }
362}
363
364/// If there is not actual successful result (e.g. an empty enum) then the ABI
365/// can be `()`, or nothing, because there's no successful result and it's
366/// always a failure.
367unsafe impl HostResultHasUnwindSentinel for core::convert::Infallible {
368 type Abi = ();
369 const SENTINEL: () = ();
370 fn into_abi(self) {
371 match self {}
372 }
373}
374
375unsafe impl HostResultHasUnwindSentinel for bool {
376 type Abi = u32;
377 const SENTINEL: Self::Abi = u32::MAX;
378 fn into_abi(self) -> Self::Abi {
379 u32::from(self)
380 }
381}
382
383unsafe impl HostResultHasUnwindSentinel for *mut u8 {
384 type Abi = *mut u8;
385 const SENTINEL: Self::Abi = ptr::without_provenance_mut(usize::MAX);
386 fn into_abi(self) -> Self::Abi {
387 self
388 }
389}
390
391/// A helper structure to schlep from this module to the
392/// `crate::trap::from_runtime_box` function.
393///
394/// This is boxed up on the heap to keep movement around optimized. This is
395/// mutated after creation to fill in `backtrace` in some situations as well.
396#[derive(Debug)]
397pub struct Trap {
398 /// Original reason from where this trap originated.
399 pub reason: TrapReason,
400 /// Wasm backtrace of the trap, if any.
401 pub backtrace: Option<Backtrace>,
402 /// The Wasm Coredump, if any.
403 pub coredumpstack: Option<CoreDumpStack>,
404}
405
406/// Enumeration of different methods of raising a trap (or a sentinel
407/// for an exception).
408#[derive(Debug)]
409pub enum TrapReason {
410 /// A user-defined error has been raised, such as through a host function
411 /// call.
412 ///
413 /// This is constructed naturally through various `From` conversions leading
414 /// into a `TrapReason`. For example host functions returning `Result<()>`
415 /// will have any errors put here.
416 ///
417 /// Note that this variant can also represent an embedder-thrown exception.
418 /// Embedder-thrown exceptions are encoded as `ThrownException.into()` which
419 /// then looks for various handlers on the stack.
420 User(Error),
421
422 /// A trap raised from Cranelift-generated code.
423 Jit {
424 /// The program counter where this trap originated.
425 ///
426 /// This is later used with side tables from compilation to translate
427 /// the trapping address to a trap code.
428 pc: usize,
429
430 /// If the trap was a memory-related trap such as SIGSEGV then this
431 /// field will contain the address of the inaccessible data.
432 ///
433 /// Note that wasm loads/stores are not guaranteed to fill in this
434 /// information. Dynamically-bounds-checked memories, for example, will
435 /// not access an invalid address but may instead load from NULL or may
436 /// explicitly jump to a `ud2` instruction. This is only available for
437 /// fault-based traps which are one of the main ways, but not the only
438 /// way, to run wasm.
439 faulting_addr: Option<usize>,
440
441 /// The trap code associated with this trap.
442 trap: wasmtime_environ::CompiledTrap,
443 },
444}
445
446impl<E> From<E> for TrapReason
447where
448 E: Into<Error>,
449{
450 fn from(error: E) -> Self {
451 TrapReason::User(error.into())
452 }
453}
454
455/// Catches any wasm traps that happen within the execution of `closure`,
456/// returning them as a `Result`.
457pub fn catch_traps<T, F>(
458 store: &mut StoreContextMut<'_, T>,
459 old_state: &mut EntryStoreContext,
460 mut closure: F,
461) -> Result<()>
462where
463 F: FnMut(NonNull<VMContext>, Option<InterpreterRef<'_>>) -> bool,
464{
465 let caller = store.0.default_caller();
466
467 let result = CallThreadState::new(store.0, old_state).with(|_cx| match store.0.executor() {
468 ExecutorRef::Interpreter(r) => closure(caller, Some(r)),
469 #[cfg(has_host_compiler_backend)]
470 ExecutorRef::Native => closure(caller, None),
471 });
472
473 match result {
474 Ok(x) => Ok(x),
475 Err(UnwindReason::Trap(reason)) => Err(crate::trap::from_runtime_box(store.0, reason?)),
476 #[cfg(all(feature = "std", panic = "unwind"))]
477 Err(UnwindReason::Panic(panic)) => std::panic::resume_unwind(panic),
478 }
479}
480
481// Module to hide visibility of the `CallThreadState::prev` field and force
482// usage of its accessor methods.
483mod call_thread_state {
484 use super::*;
485 use crate::EntryStoreContext;
486 use crate::runtime::vm::{Unwind, VMStackChain};
487
488 /// Temporary state stored on the stack which is registered in the `tls`
489 /// module below for calls into wasm.
490 ///
491 /// This structure is stored on the stack and allocated during the
492 /// `catch_traps` function above. The purpose of this structure is to track
493 /// the state of an "activation" or a sequence of 0-or-more contiguous
494 /// WebAssembly call frames. A `CallThreadState` always lives on the stack
495 /// and additionally maintains pointers to previous states to form a linked
496 /// list of activations.
497 ///
498 /// One of the primary goals of `CallThreadState` is to store the state of
499 /// various fields in `VMStoreContext` when it was created. This is done
500 /// because calling WebAssembly will clobber these fields otherwise.
501 ///
502 /// Another major purpose of `CallThreadState` is to assist with unwinding
503 /// and track state necessary when an unwind happens for the original
504 /// creator of `CallThreadState` to determine why the unwind happened.
505 ///
506 /// Note that this structure is pointed-to from TLS, hence liberal usage of
507 /// interior mutability here since that only gives access to
508 /// `&CallThreadState`.
509 pub struct CallThreadState {
510 /// Unwind state set when initiating an unwind and read when
511 /// the control transfer occurs (after the `raise` point is
512 /// reached for host-code destinations and right when
513 /// performing the jump for Wasm-code destinations).
514 pub(super) unwind: Cell<Option<UnwindReason>>,
515 #[cfg(all(has_native_signals))]
516 pub(super) signal_handler: Option<*const SignalHandler>,
517 pub(super) capture_backtrace: bool,
518 #[cfg(feature = "coredump")]
519 pub(super) capture_coredump: bool,
520
521 pub(crate) vm_store_context: Cell<NonNull<VMStoreContext>>,
522 pub(crate) unwinder: &'static dyn Unwind,
523
524 pub(super) prev: Cell<tls::Ptr>,
525
526 // The state of the runtime for the *previous* `CallThreadState` for
527 // this same store. Our *current* state is saved in `self.vm_store_context`,
528 // etc. We need access to the old values of these
529 // fields because the `VMStoreContext` typically doesn't change across
530 // nested calls into Wasm (i.e. they are typically calls back into the
531 // same store and `self.vm_store_context == self.prev.vm_store_context`) and we must to
532 // maintain the list of contiguous-Wasm-frames stack regions for
533 // backtracing purposes.
534 old_state: *mut EntryStoreContext,
535 }
536
537 impl Drop for CallThreadState {
538 fn drop(&mut self) {
539 // Unwind information should not be present as it should have
540 // already been processed.
541 debug_assert!(self.unwind.replace(None).is_none());
542 }
543 }
544
545 impl CallThreadState {
546 #[inline]
547 pub(super) fn new(
548 store: &mut StoreOpaque,
549 old_state: *mut EntryStoreContext,
550 ) -> CallThreadState {
551 CallThreadState {
552 unwind: Cell::new(None),
553 unwinder: store.unwinder(),
554 #[cfg(all(has_native_signals))]
555 signal_handler: store.signal_handler(),
556 capture_backtrace: store.engine().config().wasm_backtrace_max_frames.is_some(),
557 #[cfg(feature = "coredump")]
558 capture_coredump: store.engine().config().coredump_on_trap,
559 vm_store_context: Cell::new(store.vm_store_context_ptr()),
560 prev: Cell::new(ptr::null()),
561 old_state,
562 }
563 }
564
565 /// Get the saved FP upon exit from Wasm for the previous `CallThreadState`.
566 ///
567 /// # Safety
568 ///
569 /// Requires that the saved last Wasm trampoline FP points to
570 /// a valid trampoline frame, or is null.
571 pub unsafe fn old_last_wasm_exit_fp(&self) -> usize {
572 let trampoline_fp = unsafe { (&*self.old_state).last_wasm_exit_trampoline_fp };
573 // SAFETY: `trampoline_fp` is either a valid FP from an
574 // active trampoline frame or is null.
575 unsafe { VMStoreContext::wasm_exit_fp_from_trampoline_fp(trampoline_fp) }
576 }
577
578 /// Get the saved PC upon exit from Wasm for the previous `CallThreadState`.
579 pub unsafe fn old_last_wasm_exit_pc(&self) -> usize {
580 unsafe { (&*self.old_state).last_wasm_exit_pc }
581 }
582
583 /// Get the saved FP upon entry into Wasm for the previous `CallThreadState`.
584 pub unsafe fn old_last_wasm_entry_fp(&self) -> usize {
585 unsafe { (&*self.old_state).last_wasm_entry_fp }
586 }
587
588 /// Get the saved `VMStackChain` for the previous `CallThreadState`.
589 pub unsafe fn old_stack_chain(&self) -> VMStackChain {
590 unsafe { (&*self.old_state).stack_chain.clone() }
591 }
592
593 /// Get the previous `CallThreadState`.
594 pub fn prev(&self) -> tls::Ptr {
595 self.prev.get()
596 }
597
598 /// Pushes this `CallThreadState` activation on to the linked list
599 /// stored in TLS.
600 ///
601 /// This method will take the current head of the linked list, stored in
602 /// our TLS pointer, and move it into `prev`. The TLS pointer is then
603 /// updated to `self`.
604 ///
605 /// # Panics
606 ///
607 /// Panics if this activation is already in a linked list (e.g.
608 /// `self.prev` is set).
609 #[inline]
610 pub(crate) unsafe fn push(&self) {
611 assert!(self.prev.get().is_null());
612 self.prev.set(tls::raw::replace(self));
613 }
614
615 /// Pops this `CallThreadState` from the linked list stored in TLS.
616 ///
617 /// This method will restore `self.prev` into the head of the linked
618 /// list stored in TLS and will additionally null-out `self.prev`.
619 ///
620 /// # Panics
621 ///
622 /// Panics if this activation isn't the head of the list.
623 #[inline]
624 pub(crate) unsafe fn pop(&self) {
625 let prev = self.prev.replace(ptr::null());
626 let head = tls::raw::replace(prev);
627 assert!(core::ptr::eq(head, self));
628 }
629
630 /// Swaps the state in this `CallThreadState`'s `VMStoreContext` with
631 /// the state in `EntryStoreContext` that was saved when this
632 /// activation was created.
633 ///
634 /// This method is using during suspension of a fiber to restore the
635 /// store back to what it originally was and prepare it to be resumed
636 /// later on. This takes various fields of `VMStoreContext` and swaps
637 /// them with what was saved in `EntryStoreContext`. That restores
638 /// a store to just before this activation was called but saves off the
639 /// fields of this activation to get restored/resumed at a later time.
640 #[cfg(feature = "async")]
641 pub(super) unsafe fn swap(&self) {
642 unsafe fn swap<T>(a: &core::cell::UnsafeCell<T>, b: &mut T) {
643 unsafe { core::mem::swap(&mut *a.get(), b) }
644 }
645
646 unsafe {
647 let cx = self.vm_store_context.get().as_ref();
648 swap(
649 &cx.last_wasm_exit_trampoline_fp,
650 &mut (*self.old_state).last_wasm_exit_trampoline_fp,
651 );
652 swap(
653 &cx.last_wasm_exit_pc,
654 &mut (*self.old_state).last_wasm_exit_pc,
655 );
656 swap(
657 &cx.last_wasm_entry_fp,
658 &mut (*self.old_state).last_wasm_entry_fp,
659 );
660 swap(
661 &cx.last_wasm_entry_sp,
662 &mut (*self.old_state).last_wasm_entry_sp,
663 );
664 swap(
665 &cx.last_wasm_entry_trap_handler,
666 &mut (*self.old_state).last_wasm_entry_trap_handler,
667 );
668 swap(&cx.stack_chain, &mut (*self.old_state).stack_chain);
669 }
670 }
671 }
672}
673pub use call_thread_state::*;
674
675#[cfg(feature = "gc")]
676use super::compute_handler;
677
678/// The reasons why Wasmtime might unwind, stored within `CallThreadState`.
679pub enum UnwindReason {
680 /// The host panicked.
681 ///
682 /// In this situation Wasmtime must transfer the panic payload across Wasm
683 /// code since the native unwinder isn't guaranteed to be able to unwind
684 /// wasm. Once wasm is unwound, however, the panic is re-thrown on the
685 /// other side to propagate like usual.
686 #[cfg(all(feature = "std", panic = "unwind"))]
687 Panic(Box<dyn std::any::Any + Send>),
688
689 /// Wasm or the host raised a trap for some reason.
690 ///
691 /// This is specifically stored as a `Result` to carry the `OutOfMemory`
692 /// error from when this is allocated to the catch-site of the error.
693 /// Otherwise keeping this in a `Box` means that moving this value in and
694 /// out of `CallThreadState` is optimized. Specifically the "hot function"
695 /// doesn't need a slow path which is much larger with lots of memcpy's and
696 /// such.
697 Trap(Result<Box<Trap>, OutOfMemory>),
698}
699
700impl<E> From<E> for UnwindReason
701where
702 E: Into<TrapReason>,
703{
704 fn from(value: E) -> UnwindReason {
705 UnwindReason::Trap(try_new::<Box<_>>(Trap {
706 reason: value.into(),
707 backtrace: None,
708 coredumpstack: None,
709 }))
710 }
711}
712
713impl CallThreadState {
714 #[inline]
715 fn with(mut self, closure: impl FnOnce(&CallThreadState) -> bool) -> Result<(), UnwindReason> {
716 let succeeded = tls::set(&mut self, |me| closure(me));
717 if succeeded {
718 Ok(())
719 } else {
720 Err(self.read_unwind())
721 }
722 }
723
724 #[cold]
725 fn read_unwind(&self) -> UnwindReason {
726 self.unwind.replace(None).unwrap()
727 }
728
729 /// Records the unwind information provided within this `CallThreadState`.
730 ///
731 /// This function is used to stash metadata for why an unwind is about to
732 /// happen. The actual unwind is expected to happen after this function is
733 /// called using the `unwind` function below. This function is expected to
734 /// be called from the host or a signal handler the moment a trap happens.
735 /// Signal handlers then immediately unwind to the entry state, and host
736 /// functions will return back to the entry trampoline which will
737 /// immediately turn around and call the `unwind` function below.
738 ///
739 /// Note that this is a relatively low-level function and will panic if
740 /// misused.
741 ///
742 /// # Panics
743 ///
744 /// Panics if unwind information has already been recorded as that should
745 /// have been processed first.
746 fn record_unwind(&self, reason: UnwindReason) {
747 if cfg!(debug_assertions) {
748 let prev = self.unwind.replace(None);
749 assert!(prev.is_none());
750 }
751
752 self.unwind.set(Some(reason));
753 }
754
755 /// Helper function to perform an actual unwinding operation.
756 ///
757 /// This must be preceded by a `record_unwind` operation above to be
758 /// processed correctly on the other side.
759 ///
760 /// This is not used for signals-based-traps. When a signal is caught the
761 /// thread's register state is updated to the entrypoint handler. This is
762 /// only used for host-initiated traps. Note that this includes the host
763 /// implementation of throwing a wasm exception.
764 ///
765 /// Note that this function is expected to be called with the wasm backtrace
766 /// in such a state that it represents the unwinding condition.
767 /// Effectively, if a wasm backtrace is captured here, it reflects why the
768 /// unwind happened.
769 ///
770 /// # Unsafety
771 ///
772 /// This function is not safe if a corresponding handler wasn't already
773 /// setup in the entry trampoline. Additionally this isn't safe as it may
774 /// skip all Rust destructors on the stack, if there are any, for native
775 /// executors as `Handler::resume` will be used.
776 unsafe fn unwind(&self, store: &mut dyn VMStore) {
777 #[allow(unused_mut, reason = "only mutated in `debug` configuration")]
778 let mut unwind = self.unwind.replace(None);
779
780 // If configured, fire a debug event for the cause of unwinding here.
781 //
782 // Note that by firing a debug event the trap being handled can be
783 // subtly different. For example in the event of a thrown exception the
784 // debug handler might take the exception from the store and put
785 // another one there, changing the type of the exception being thrown.
786 // This notably means that the calculation for what to do about `unwind`
787 // happens after this block, down below.
788 //
789 // Also note that this can execute arbitrary WebAssembly code within
790 // this block due to the store's debug handler. That means that we
791 // might be paused here for quite some time.
792 #[cfg(feature = "debug")]
793 if let Some(UnwindReason::Trap(Ok(trap))) = &unwind {
794 #[cfg(feature = "gc")]
795 use wasmtime_core::alloc::PanicOnOom;
796
797 let result = match &trap.reason {
798 TrapReason::User(err) => {
799 let mut event = crate::DebugEvent::HostcallError(err);
800 if let Some(trap) = err.downcast_ref() {
801 event = crate::DebugEvent::Trap(*trap);
802 }
803
804 if let Some(trap) = err.downcast_ref() {
805 event = crate::DebugEvent::Trap(*trap);
806 }
807
808 // For `ThrownException` errors that indicates that we should
809 // look at the store to see if there's a pending exception
810 // there, and if so then that's a different debug event than the
811 // `HostcallError`.
812 //
813 // TODO(#12069): handle allocation failure here
814 #[cfg(feature = "gc")]
815 if err.is::<ThrownException>()
816 && let Some(exn) = store.pending_exception_owned_rooted().panic_on_oom()
817 {
818 event = crate::DebugEvent::Exception(exn.clone());
819 }
820
821 store.block_on_debug_handler(event)
822 }
823
824 TrapReason::Jit { .. } => {
825 // Not handled here. JIT traps only show up via signal
826 // handlers, and the debugger isn't invoked from signal
827 // handlers at this time.
828 Ok(())
829 }
830 };
831
832 // If the debugger invocation itself resulted in an `Err`
833 // (which can only come from the `block_on` hitting a
834 // failure mode), we need to override our unwind as-if
835 // were handling a host error.
836 if let Err(err) = result {
837 unwind = Some(UnwindReason::from(err));
838 }
839 }
840
841 let handler;
842 let payload1;
843 let payload2;
844
845 // Determine, from `unwind`, the `handler` and payloads that will be
846 // used to resume execution to. Note that the entry trampoline into wasm
847 // setup an entrypoint handler meaning we're guaranteed *something* to
848 // unwind to. In the case of a wasm exception, however, we may want to
849 // unwind to a different landing pad on the stack which is in-wasm.
850 'done: {
851 if let Some(UnwindReason::Trap(Ok(trap))) = &mut unwind {
852 let mut has_backtrace = trap.backtrace.is_some();
853
854 if let TrapReason::User(err) = &trap.reason {
855 // If this trap indicates an exception is being thrown, aka
856 // `ThrownException`, and there's a stored exception within the
857 // store to lookup tag information for, then do so here. If
858 // there's a wasm handler for this exception on the stack then
859 // that's the handler to resume to.
860 //
861 // Note that `unwind` is intentionally dropped on the floor
862 // here. We're resuming back into wasm with a normal state
863 // meaning we're no longer in an exceptional state. By doing
864 // this the internal `self.unwind` state reflects `None`.
865 //
866 // SAFETY: we are invoking `compute_handler()` while Wasm is
867 // on the stack and we have re-entered via a trampoline, as
868 // required by its stack-walking logic.
869 #[cfg(feature = "gc")]
870 if err.is::<ThrownException>()
871 && let Some((instance, tag)) = store.pending_exception_tag_and_instance()
872 && let Some(catch) = unsafe { compute_handler(store, instance, tag) }
873 {
874 handler = catch;
875 // Take the pending exception at this time and use it as
876 // payload.
877 payload1 = usize::try_from(
878 store.expose_pending_exception_to_wasm().unwrap().get(),
879 )
880 .expect("GC ref does not fit in usize");
881 payload2 = 0;
882 drop(unwind);
883 break 'done;
884 }
885
886 has_backtrace = has_backtrace || err.is::<WasmBacktrace>();
887 }
888
889 // If doesn't yet already have a backtrace, and one's not been
890 // captured yet, then assign one now.
891 if !has_backtrace {
892 trap.backtrace = self.capture_backtrace(store.vm_store_context_mut(), None);
893 trap.coredumpstack = self.capture_coredump(store.vm_store_context_mut(), None);
894 }
895 }
896
897 // If this wasn't a wasm-caught exception, then catch the exception
898 // within the original entrypoint into wasm. Note that in this
899 // situation the `unwind` value is replaced within `self` to ensure
900 // that it's picked up on the other side of the trampoline catching
901 // this error.
902 handler = entry_trap_handler(store.vm_store_context());
903 payload1 = 0;
904 payload2 = 0;
905 self.unwind.set(unwind);
906 break 'done; // be sure `'done` is considered used
907 }
908
909 unsafe {
910 self.resume_to_exception_handler(store.executor(), &handler, payload1, payload2);
911 }
912 }
913
914 pub(crate) fn entry_trap_handler(&self) -> Handler {
915 unsafe { entry_trap_handler(self.vm_store_context.get().as_ref()) }
916 }
917
918 unsafe fn resume_to_exception_handler(
919 &self,
920 executor: ExecutorRef<'_>,
921 handler: &Handler,
922 payload1: usize,
923 payload2: usize,
924 ) {
925 unsafe {
926 match executor {
927 ExecutorRef::Interpreter(mut r) => {
928 r.resume_to_exception_handler(handler, payload1, payload2)
929 }
930 #[cfg(has_host_compiler_backend)]
931 ExecutorRef::Native => handler.resume_tailcc(payload1, payload2),
932 }
933 }
934 }
935
936 fn capture_backtrace(
937 &self,
938 limits: *const VMStoreContext,
939 trap_pc_and_fp: Option<(usize, usize)>,
940 ) -> Option<Backtrace> {
941 if !self.capture_backtrace {
942 return None;
943 }
944
945 Some(unsafe { Backtrace::new_with_trap_state(limits, self.unwinder, self, trap_pc_and_fp) })
946 }
947
948 pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = &'a Self> + 'a {
949 let mut state = Some(self);
950 core::iter::from_fn(move || {
951 let this = state?;
952 state = unsafe { this.prev().as_ref() };
953 Some(this)
954 })
955 }
956
957 /// Trap handler using our thread-local state.
958 ///
959 /// * `regs` - some special program registers at the time that the trap
960 /// happened, for example `pc`.
961 /// * `faulting_addr` - the system-provided address that the a fault, if
962 /// any, happened at. This is used when debug-asserting that all segfaults
963 /// are known to live within a `Store<T>` in a valid range.
964 /// * `call_handler` - a closure used to invoke the platform-specific
965 /// signal handler for each instance, if available.
966 ///
967 /// Attempts to handle the trap if it's a wasm trap. Returns a `TrapTest`
968 /// which indicates what this could be, such as:
969 ///
970 /// * `TrapTest::NotWasm` - not a wasm fault, this should get forwarded to
971 /// the next platform-specific fault handler.
972 /// * `TrapTest::HandledByEmbedder` - the embedder `call_handler` handled
973 /// this signal, nothing else to do.
974 /// * `TrapTest::Trap` - this is a wasm trap an the stack needs to be
975 /// unwound now.
976 pub(crate) fn test_if_trap(
977 &self,
978 regs: TrapRegisters,
979 faulting_addr: Option<usize>,
980 call_handler: impl FnOnce(&SignalHandler) -> bool,
981 ) -> TrapTest {
982 // First up see if any instance registered has a custom trap handler,
983 // in which case run them all. If anything handles the trap then we
984 // return that the trap was handled.
985 let _ = &call_handler;
986 #[cfg(all(has_native_signals, not(miri)))]
987 if let Some(handler) = self.signal_handler {
988 if unsafe { call_handler(&*handler) } {
989 return TrapTest::HandledByEmbedder;
990 }
991 }
992
993 // If this fault wasn't in wasm code, then it's not our problem
994 let Some((code, text_offset)) = lookup_code(regs.pc) else {
995 return TrapTest::NotWasm;
996 };
997
998 // If the fault was at a location that was not marked as potentially
999 // trapping, then that's a bug in Cranelift/Winch/etc. Don't try to
1000 // catch the trap and pretend this isn't wasm so the program likely
1001 // aborts.
1002 let Some(trap) = code.lookup_trap_code(text_offset) else {
1003 return TrapTest::NotWasm;
1004 };
1005
1006 // If all that passed then this is indeed a wasm trap, so return the
1007 // `Handler` setup in the original wasm frame.
1008 self.set_jit_trap(regs, faulting_addr, trap);
1009 let entry_handler = self.entry_trap_handler();
1010 TrapTest::Trap(entry_handler)
1011 }
1012
1013 pub(crate) fn set_jit_trap(
1014 &self,
1015 TrapRegisters { pc, fp, .. }: TrapRegisters,
1016 faulting_addr: Option<usize>,
1017 trap: wasmtime_environ::CompiledTrap,
1018 ) {
1019 let mut unwind = UnwindReason::from(TrapReason::Jit {
1020 pc,
1021 faulting_addr,
1022 trap,
1023 });
1024 if let UnwindReason::Trap(Ok(trap)) = &mut unwind {
1025 trap.backtrace =
1026 self.capture_backtrace(self.vm_store_context.get().as_ptr(), Some((pc, fp)));
1027 trap.coredumpstack =
1028 self.capture_coredump(self.vm_store_context.get().as_ptr(), Some((pc, fp)));
1029 }
1030 self.record_unwind(unwind);
1031 }
1032}
1033
1034fn entry_trap_handler(vm_store_context: &VMStoreContext) -> Handler {
1035 unsafe {
1036 let fp = *vm_store_context.last_wasm_entry_fp.get();
1037 let sp = *vm_store_context.last_wasm_entry_sp.get();
1038 let pc = *vm_store_context.last_wasm_entry_trap_handler.get();
1039 Handler { pc, sp, fp }
1040 }
1041}
1042
1043/// A private inner module managing the state of Wasmtime's thread-local storage
1044/// (TLS) state.
1045///
1046/// Wasmtime at this time has a single pointer of TLS. This single pointer of
1047/// TLS is the totality of all TLS required by Wasmtime. By keeping this as
1048/// small as possible it generally makes it easier to integrate with external
1049/// systems and implement features such as fiber context switches. This single
1050/// TLS pointer is declared in platform-specific modules to handle platform
1051/// differences, so this module here uses getters/setters which delegate to
1052/// platform-specific implementations.
1053///
1054/// The single TLS pointer used by Wasmtime is morally
1055/// `Option<&CallThreadState>` meaning that it's a possibly-present pointer to
1056/// some state. This pointer is a pointer to the most recent (youngest)
1057/// `CallThreadState` activation, or the most recent call into WebAssembly.
1058///
1059/// This TLS pointer is additionally the head of a linked list of activations
1060/// that are all stored on the stack for the current thread. Each time
1061/// WebAssembly is recursively invoked by an embedder will push a new entry into
1062/// this linked list. This singly-linked list is maintained with its head in TLS
1063/// node pointers are stored in `CallThreadState::prev`.
1064///
1065/// An example stack might look like this:
1066///
1067/// ```text
1068/// ┌─────────────────────┐◄───── highest, or oldest, stack address
1069/// │ native stack frames │
1070/// │ ... │
1071/// │ ┌───────────────┐◄─┼──┐
1072/// │ │CallThreadState│ │ │
1073/// │ └───────────────┘ │ p
1074/// ├─────────────────────┤ r
1075/// │ wasm stack frames │ e
1076/// │ ... │ v
1077/// ├─────────────────────┤ │
1078/// │ native stack frames │ │
1079/// │ ... │ │
1080/// │ ┌───────────────┐◄─┼──┼── TLS pointer
1081/// │ │CallThreadState├──┼──┘
1082/// │ └───────────────┘ │
1083/// ├─────────────────────┤
1084/// │ wasm stack frames │
1085/// │ ... │
1086/// ├─────────────────────┤
1087/// │ native stack frames │
1088/// │ ... │
1089/// └─────────────────────┘◄───── smallest, or youngest, stack address
1090/// ```
1091///
1092/// # Fibers and async
1093///
1094/// Wasmtime supports stack-switching with fibers to implement async. This means
1095/// that Wasmtime will temporarily execute code on a separate stack and then
1096/// suspend from this stack back to the embedder for async operations. Doing
1097/// this safely requires manual management of the TLS pointer updated by
1098/// Wasmtime.
1099///
1100/// For example when a fiber is suspended that means that the TLS pointer needs
1101/// to be restored to whatever it was when the fiber was resumed. Additionally
1102/// this may need to pop multiple `CallThreadState` activations, one for each
1103/// one located on the fiber stack itself.
1104///
1105/// The `AsyncWasmCallState` and `PreviousAsyncWasmCallState` structures in this
1106/// module are used to manage this state, namely:
1107///
1108/// * The `AsyncWasmCallState` structure represents the state of a suspended
1109/// fiber. This is a linked list, in reverse order, from oldest activation on
1110/// the fiber to youngest activation on the fiber.
1111///
1112/// * The `PreviousAsyncWasmCallState` structure represents a pointer within our
1113/// thread's TLS linked list of activations when a fiber was resumed. This
1114/// pointer is used during fiber suspension to know when to stop popping
1115/// activations from the thread's linked list.
1116///
1117/// Note that this means that the directionality of linked list links is
1118/// opposite when stored in TLS vs when stored for a suspended fiber. The
1119/// thread's current list pointed to by TLS is youngest-to-oldest links, while a
1120/// suspended fiber stores oldest-to-youngest links.
1121pub(crate) mod tls {
1122 #[cfg(all(feature = "component-model-async", feature = "gc"))]
1123 use crate::module::ModuleRegistry;
1124 #[cfg(all(feature = "component-model-async", feature = "gc"))]
1125 use crate::store::StoreOpaque;
1126
1127 use super::CallThreadState;
1128
1129 pub use raw::Ptr;
1130
1131 // An even *more* inner module for dealing with TLS. This actually has the
1132 // thread local variable and has functions to access the variable.
1133 //
1134 // Note that this is specially done to fully encapsulate that the accessors
1135 // for tls may or may not be inlined. Wasmtime's async support employs stack
1136 // switching which can resume execution on different OS threads. This means
1137 // that borrows of our TLS pointer must never live across accesses because
1138 // otherwise the access may be split across two threads and cause unsafety.
1139 //
1140 // This also means that extra care is taken by the runtime to save/restore
1141 // these TLS values when the runtime may have crossed threads.
1142 //
1143 // Note, though, that if async support is disabled at compile time then
1144 // these functions are free to be inlined.
1145 pub(super) mod raw {
1146 use super::CallThreadState;
1147
1148 pub type Ptr = *const CallThreadState;
1149
1150 const _: () = {
1151 assert!(core::mem::align_of::<CallThreadState>() > 1);
1152 };
1153
1154 fn tls_get() -> (Ptr, bool) {
1155 let mut initialized = false;
1156 let p = crate::runtime::vm::sys::tls_get().map_addr(|a| {
1157 initialized = (a & 1) != 0;
1158 a & !1
1159 });
1160 (p.cast(), initialized)
1161 }
1162
1163 fn tls_set(ptr: Ptr, initialized: bool) {
1164 let encoded = ptr.map_addr(|a| a | usize::from(initialized));
1165 crate::runtime::vm::sys::tls_set(encoded.cast_mut().cast::<u8>());
1166 }
1167
1168 #[cfg_attr(feature = "async", inline(never))] // see module docs
1169 #[cfg_attr(not(feature = "async"), inline)]
1170 pub fn replace(val: Ptr) -> Ptr {
1171 // When a new value is configured that means that we may be
1172 // entering WebAssembly so check to see if this thread has
1173 // performed per-thread initialization for traps.
1174 let (prev, initialized) = tls_get();
1175 if !initialized {
1176 super::super::lazy_per_thread_init();
1177 }
1178 tls_set(val, true);
1179 prev
1180 }
1181
1182 /// Eagerly initialize thread-local runtime functionality. This will be performed
1183 /// lazily by the runtime if users do not perform it eagerly.
1184 #[cfg_attr(feature = "async", inline(never))] // see module docs
1185 #[cfg_attr(not(feature = "async"), inline)]
1186 pub fn initialize() {
1187 let (state, initialized) = tls_get();
1188 if initialized {
1189 return;
1190 }
1191 super::super::lazy_per_thread_init();
1192 tls_set(state, true);
1193 }
1194
1195 #[cfg_attr(feature = "async", inline(never))] // see module docs
1196 #[cfg_attr(not(feature = "async"), inline)]
1197 pub fn get() -> Ptr {
1198 tls_get().0
1199 }
1200 }
1201
1202 pub use raw::initialize as tls_eager_initialize;
1203 #[cfg(all(feature = "component-model-async", feature = "gc"))]
1204 use wasmtime_unwinder::Unwind;
1205
1206 /// Opaque state used to persist the state of the `CallThreadState`
1207 /// activations associated with a fiber stack that's used as part of an
1208 /// async wasm call.
1209 #[cfg(feature = "async")]
1210 pub struct AsyncWasmCallState {
1211 // The head of a linked list of activations that are currently present
1212 // on an async call's fiber stack. This pointer points to the oldest
1213 // activation frame where the `prev` links internally link to younger
1214 // activation frames.
1215 //
1216 // When pushed onto a thread this linked list is traversed to get pushed
1217 // onto the current thread at the time.
1218 //
1219 // If this pointer is null then that means that the fiber this state is
1220 // associated with has no activations.
1221 state: raw::Ptr,
1222 }
1223
1224 // SAFETY: This is a relatively unsafe unsafe block and not really all that
1225 // well audited. The general idea is that the linked list of activations
1226 // owned by `self.state` are safe to send to other threads, but that relies
1227 // on everything internally being safe as well as stack variables and such.
1228 // This is more-or-less tied to the very large comment in `fiber.rs` about
1229 // `unsafe impl Send` there.
1230 #[cfg(feature = "async")]
1231 unsafe impl Send for AsyncWasmCallState {}
1232
1233 #[cfg(feature = "async")]
1234 impl AsyncWasmCallState {
1235 /// Creates new state that initially starts as null.
1236 pub fn new() -> AsyncWasmCallState {
1237 AsyncWasmCallState {
1238 state: core::ptr::null_mut(),
1239 }
1240 }
1241
1242 /// Pushes the saved state of this wasm's call onto the current thread's
1243 /// state.
1244 ///
1245 /// This will iterate over the linked list of states stored within
1246 /// `self` and push them sequentially onto the current thread's
1247 /// activation list.
1248 ///
1249 /// The returned `PreviousAsyncWasmCallState` captures the state of this
1250 /// thread just before this operation, and it must have its `restore`
1251 /// method called to restore the state when the async wasm is suspended
1252 /// from.
1253 ///
1254 /// # Unsafety
1255 ///
1256 /// Must be carefully coordinated with
1257 /// `PreviousAsyncWasmCallState::restore` and fiber switches to ensure
1258 /// that this doesn't push stale data and the data is popped
1259 /// appropriately.
1260 pub unsafe fn push(self) -> PreviousAsyncWasmCallState {
1261 // First save the state of TLS as-is so when this state is popped
1262 // off later on we know where to stop.
1263 let ret = PreviousAsyncWasmCallState { state: raw::get() };
1264
1265 // The oldest activation, if present, has various `VMStoreContext`
1266 // fields saved within it. These fields were the state for the
1267 // *youngest* activation when a suspension previously happened. By
1268 // swapping them back into the store this is an O(1) way of
1269 // restoring the state of a store's metadata fields at the time of
1270 // the suspension.
1271 //
1272 // The store's previous values before this function will all get
1273 // saved in the oldest activation's state on the stack. The store's
1274 // current state then describes the youngest activation which is
1275 // restored via the loop below.
1276 unsafe {
1277 if let Some(state) = self.state.as_ref() {
1278 state.swap();
1279 }
1280 }
1281
1282 // Our `state` pointer is a linked list of oldest-to-youngest so by
1283 // pushing in order of the list we restore the youngest-to-oldest
1284 // list as stored in the state of this current thread.
1285 let mut ptr = self.state;
1286 unsafe {
1287 while let Some(state) = ptr.as_ref() {
1288 ptr = state.prev.replace(core::ptr::null_mut());
1289 state.push();
1290 }
1291 }
1292 ret
1293 }
1294
1295 /// Performs a runtime check that this state is indeed null.
1296 pub fn assert_null(&self) {
1297 assert!(self.state.is_null());
1298 }
1299
1300 /// Asserts that the current CallThreadState pointer, if present, is not
1301 /// in the `range` specified.
1302 ///
1303 /// This is used when exiting a future in Wasmtime to assert that the
1304 /// current CallThreadState pointer does not point within the stack
1305 /// we're leaving (e.g. allocated for a fiber).
1306 pub fn assert_current_state_not_in_range(range: core::ops::Range<usize>) {
1307 let p = raw::get() as usize;
1308 assert!(!range.contains(&p));
1309 }
1310
1311 #[cfg(all(feature = "component-model-async", feature = "gc"))]
1312 pub(crate) fn trace_gc_roots(
1313 &mut self,
1314 modules: &ModuleRegistry,
1315 unwind: &dyn Unwind,
1316 gc_roots_list: &mut crate::vm::GcRootsList,
1317 ) {
1318 let mut ptr = self.state;
1319 unsafe {
1320 while let Some(state) = ptr.as_ref() {
1321 let _ = wasmtime_unwinder::visit_frames::<()>(
1322 unwind,
1323 state.old_last_wasm_exit_pc(),
1324 state.old_last_wasm_exit_fp(),
1325 state.old_last_wasm_entry_fp(),
1326 |frame| {
1327 StoreOpaque::trace_wasm_stack_frame(modules, gc_roots_list, frame);
1328 core::ops::ControlFlow::Continue(())
1329 },
1330 );
1331
1332 ptr = state.prev.get();
1333 }
1334 }
1335 }
1336 }
1337
1338 /// Opaque state used to help control TLS state across stack switches for
1339 /// async support.
1340 ///
1341 /// This structure is returned from [`AsyncWasmCallState::push`] and
1342 /// represents the state of this thread's TLS variable prior to the push
1343 /// operation.
1344 #[cfg(feature = "async")]
1345 pub struct PreviousAsyncWasmCallState {
1346 // The raw value of this thread's TLS pointer when this structure was
1347 // created. This is not dereferenced or inspected but is used to halt
1348 // linked list traversal in [`PreviousAsyncWasmCallState::restore`].
1349 state: raw::Ptr,
1350 }
1351
1352 #[cfg(feature = "async")]
1353 impl PreviousAsyncWasmCallState {
1354 /// Pops a fiber's linked list of activations and stores them in
1355 /// `AsyncWasmCallState`.
1356 ///
1357 /// This will pop the top activation of this current thread continuously
1358 /// until it reaches whatever the current activation was when
1359 /// [`AsyncWasmCallState::push`] was originally called.
1360 ///
1361 /// # Unsafety
1362 ///
1363 /// Must be paired with a `push` and only performed at a time when a
1364 /// fiber is being suspended.
1365 pub unsafe fn restore(self) -> AsyncWasmCallState {
1366 let thread_head = self.state;
1367 core::mem::forget(self);
1368 let mut ret = AsyncWasmCallState::new();
1369 loop {
1370 // If the current TLS state is as we originally found it, then
1371 // this loop is finished.
1372 //
1373 // Note, though, that before exiting, if the oldest
1374 // `CallThreadState` is present, the current state of
1375 // `VMStoreContext` is saved off within it. This will save the
1376 // current state, before this function, of `VMStoreContext`
1377 // into the `EntryStoreContext` stored with the oldest
1378 // activation. This is a bit counter-intuitive where the state
1379 // for the youngest activation is stored in the "old" state
1380 // of the oldest activation.
1381 //
1382 // What this does is restores the state of the store to just
1383 // before this async fiber was started. The fiber's state will
1384 // be entirely self-contained in the fiber itself and the
1385 // returned `AsyncWasmCallState`. Resumption above in
1386 // `AsyncWasmCallState::push` will perform the swap back into
1387 // the store to hook things up again.
1388 let ptr = raw::get();
1389 if ptr == thread_head {
1390 unsafe {
1391 if let Some(state) = ret.state.as_ref() {
1392 state.swap();
1393 }
1394 }
1395
1396 break ret;
1397 }
1398
1399 // Pop this activation from the current thread's TLS state, and
1400 // then afterwards push it onto our own linked list within this
1401 // `AsyncWasmCallState`. Note that the linked list in
1402 // `AsyncWasmCallState` is stored in reverse order so a
1403 // subsequent `push` later on pushes everything in the right
1404 // order.
1405 unsafe {
1406 (*ptr).pop();
1407 if let Some(state) = ret.state.as_ref() {
1408 (*ptr).prev.set(state);
1409 }
1410 }
1411 ret.state = ptr;
1412 }
1413 }
1414 }
1415
1416 #[cfg(feature = "async")]
1417 impl Drop for PreviousAsyncWasmCallState {
1418 fn drop(&mut self) {
1419 panic!("must be consumed with `restore`");
1420 }
1421 }
1422
1423 /// Configures thread local state such that for the duration of the
1424 /// execution of `closure` any call to `with` will yield `state`, unless
1425 /// this is recursively called again.
1426 #[inline]
1427 pub fn set<R>(state: &mut CallThreadState, closure: impl FnOnce(&CallThreadState) -> R) -> R {
1428 struct Reset<'a> {
1429 state: &'a CallThreadState,
1430 }
1431
1432 impl Drop for Reset<'_> {
1433 #[inline]
1434 fn drop(&mut self) {
1435 unsafe {
1436 self.state.pop();
1437 }
1438 }
1439 }
1440
1441 unsafe {
1442 state.push();
1443 let reset = Reset { state };
1444 closure(reset.state)
1445 }
1446 }
1447
1448 /// Returns the last pointer configured with `set` above, if any.
1449 pub fn with<R>(closure: impl FnOnce(Option<&CallThreadState>) -> R) -> R {
1450 let p = raw::get();
1451 unsafe { closure(if p.is_null() { None } else { Some(&*p) }) }
1452 }
1453}