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