wasmer_vm/trap/
trap.rs

1use backtrace::Backtrace;
2use std::any::Any;
3use std::error::Error;
4use std::fmt;
5use wasmer_types::TrapCode;
6
7use crate::{StoreObjects, VMExceptionRef};
8
9/// Stores trace message with backtrace.
10#[derive(Debug)]
11pub enum Trap {
12    /// A user-raised trap through `raise_user_trap`.
13    User(Box<dyn Error + Send + Sync>),
14
15    /// A trap raised from the Wasm generated code
16    ///
17    /// Note: this trap is deterministic (assuming a deterministic host implementation)
18    Wasm {
19        /// The program counter in generated code where this trap happened.
20        pc: usize,
21        /// Native stack backtrace at the time the trap occurred
22        backtrace: Backtrace,
23        /// Optional trapcode associated to the signal that caused the trap
24        signal_trap: Option<TrapCode>,
25    },
26
27    /// A trap raised from a wasm libcall
28    ///
29    /// Note: this trap is deterministic (assuming a deterministic host implementation)
30    Lib {
31        /// Code of the trap.
32        trap_code: TrapCode,
33        /// Native stack backtrace at the time the trap occurred
34        backtrace: Backtrace,
35    },
36
37    /// A trap indicating that the runtime was unable to allocate sufficient memory.
38    ///
39    /// Note: this trap is nondeterministic, since it depends on the host system.
40    OOM {
41        /// Native stack backtrace at the time the OOM occurred
42        backtrace: Backtrace,
43    },
44
45    /// A WASM exception was thrown but not caught.
46    UncaughtException {
47        /// The exception reference of the uncaught exception.
48        exnref: VMExceptionRef,
49        /// Native stack backtrace at the time the exception was thrown.
50        /// This is a clone of the backtrace stored in the exception itself.
51        backtrace: Backtrace,
52    },
53}
54
55fn _assert_trap_is_sync_and_send(t: &Trap) -> (&dyn Sync, &dyn Send) {
56    (t, t)
57}
58
59impl Trap {
60    /// Construct a new Error with the given a user error.
61    ///
62    /// Internally saves a backtrace when constructed.
63    pub fn user(err: Box<dyn Error + Send + Sync>) -> Self {
64        Self::User(err)
65    }
66
67    /// Construct a new Wasm trap with the given source location and backtrace.
68    ///
69    /// Internally saves a backtrace when constructed.
70    pub fn wasm(pc: usize, backtrace: Backtrace, signal_trap: Option<TrapCode>) -> Self {
71        Self::Wasm {
72            pc,
73            backtrace,
74            signal_trap,
75        }
76    }
77
78    /// Returns trap code, if it's a Trap
79    pub fn to_trap(self) -> Option<TrapCode> {
80        unimplemented!()
81    }
82
83    /// Construct a new Wasm trap with the given trap code.
84    ///
85    /// Internally saves a backtrace when constructed.
86    pub fn lib(trap_code: TrapCode) -> Self {
87        let backtrace = Backtrace::new_unresolved();
88        Self::Lib {
89            trap_code,
90            backtrace,
91        }
92    }
93
94    /// Construct a new OOM trap with the given source location and trap code.
95    ///
96    /// Internally saves a backtrace when constructed.
97    pub fn oom() -> Self {
98        let backtrace = Backtrace::new_unresolved();
99        Self::OOM { backtrace }
100    }
101
102    /// Construct a new UncaughtException trap with the given exception reference.
103    pub fn uncaught_exception(exnref: VMExceptionRef, ctx: &StoreObjects) -> Self {
104        Self::UncaughtException {
105            backtrace: exnref.0.get(ctx).backtrace().clone(),
106            exnref,
107        }
108    }
109
110    /// Attempts to downcast the `Trap` to a concrete type.
111    pub fn downcast<T: Error + 'static>(self) -> Result<T, Self> {
112        match self {
113            // We only try to downcast user errors
114            Self::User(err) if err.is::<T>() => Ok(*err.downcast::<T>().unwrap()),
115            _ => Err(self),
116        }
117    }
118
119    /// Attempts to downcast the `Trap` to a concrete type.
120    pub fn downcast_ref<T: Error + 'static>(&self) -> Option<&T> {
121        match &self {
122            // We only try to downcast user errors
123            Self::User(err) if err.is::<T>() => err.downcast_ref::<T>(),
124            _ => None,
125        }
126    }
127
128    /// Returns true if the `Trap` is the same as T
129    pub fn is<T: Error + 'static>(&self) -> bool {
130        match self {
131            Self::User(err) => err.is::<T>(),
132            _ => false,
133        }
134    }
135
136    /// Returns true if the trap is an exception
137    pub fn is_exception(&self) -> bool {
138        matches!(self, Self::UncaughtException { .. })
139    }
140
141    /// If the `Trap` is an uncaught exception, returns it.
142    pub fn to_exception_ref(&self) -> Option<VMExceptionRef> {
143        match self {
144            // Self::UncaughtException { exnref, .. } => Some(Exception::from_vm_exceptionref(
145            //     crate::vm::VMExceptionRef::Sys(exnref.clone()),
146            // )),
147            Self::UncaughtException { exnref, .. } => Some(exnref.clone()),
148            _ => None,
149        }
150    }
151}
152
153impl std::error::Error for Trap {
154    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
155        match &self {
156            Self::User(err) => Some(&**err),
157            _ => None,
158        }
159    }
160}
161
162impl fmt::Display for Trap {
163    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164        match self {
165            Self::User(e) => write!(f, "{e}"),
166            Self::Lib { .. } => write!(f, "lib"),
167            Self::Wasm { .. } => write!(f, "wasm"),
168            Self::OOM { .. } => write!(f, "Wasmer VM out of memory"),
169            Self::UncaughtException { .. } => write!(f, "Uncaught wasm exception"),
170        }
171    }
172}
173
174/// The reason a Wasm execution is being unwound.
175///
176/// Shared by both trap-handler backends. `WasmTrap` is only produced by the OS
177/// backend (signal handler); it is never constructed in baremetal mode.
178#[derive(Debug)]
179#[non_exhaustive]
180pub enum UnwindReason {
181    /// A panic caused by the host
182    Panic(Box<dyn Any + Send>),
183    /// A custom error triggered by the user
184    UserTrap(Box<dyn Error + Send + Sync>),
185    /// A Trap triggered by a wasm libcall
186    LibTrap(Trap),
187    /// A trap caused by the Wasm generated code
188    WasmTrap {
189        /// Native stack backtrace at the time the trap occurred
190        backtrace: Backtrace,
191        /// Program counter in generated code where the trap occurred
192        pc: usize,
193        /// Optional trap code associated with the faulting signal
194        signal_trap: Option<TrapCode>,
195    },
196}
197
198impl UnwindReason {
199    /// Convert to a [`Trap`], or resume unwinding for the `Panic` variant (never returns).
200    pub fn into_trap(self) -> Trap {
201        match self {
202            Self::UserTrap(data) => Trap::User(data),
203            Self::LibTrap(trap) => trap,
204            Self::WasmTrap {
205                backtrace,
206                pc,
207                signal_trap,
208            } => Trap::wasm(pc, backtrace, signal_trap),
209            Self::Panic(panic) => std::panic::resume_unwind(panic),
210        }
211    }
212}