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