casper_wasmi_core/trap.rs
1use crate::HostError;
2use alloc::boxed::Box;
3use core::fmt::{self, Display};
4
5#[cfg(feature = "std")]
6use std::error::Error as StdError;
7
8/// Error type which can be thrown by wasm code or by host environment.
9///
10/// Under some conditions, wasm execution may produce a `Trap`, which immediately aborts execution.
11/// Traps can't be handled by WebAssembly code, but are reported to the embedder.
12#[derive(Debug)]
13pub enum Trap {
14 /// Traps during Wasm execution.
15 Code(TrapCode),
16 /// Traps and errors during host execution.
17 Host(Box<dyn HostError>),
18}
19
20impl Trap {
21 /// Wraps the host error in a [`Trap`].
22 #[inline]
23 pub fn host<U>(host_error: U) -> Self
24 where
25 U: HostError + Sized,
26 {
27 Self::Host(Box::new(host_error))
28 }
29
30 /// Returns `true` if `self` trap originating from host code.
31 #[inline]
32 pub fn is_host(&self) -> bool {
33 matches!(self, Self::Host(_))
34 }
35
36 /// Returns the [`TrapCode`] traps originating from Wasm execution.
37 #[inline]
38 pub fn code(&self) -> Option<TrapCode> {
39 if let Self::Code(trap_code) = self {
40 return Some(*trap_code);
41 }
42 None
43 }
44}
45
46impl From<TrapCode> for Trap {
47 #[inline]
48 fn from(error: TrapCode) -> Self {
49 Self::Code(error)
50 }
51}
52
53impl<U> From<U> for Trap
54where
55 U: HostError + Sized,
56{
57 #[inline]
58 fn from(e: U) -> Self {
59 Trap::host(e)
60 }
61}
62
63impl Display for Trap {
64 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65 match self {
66 Trap::Code(trap_code) => Display::fmt(trap_code, f),
67 Trap::Host(host_error) => Display::fmt(host_error, f),
68 }
69 }
70}
71
72#[cfg(feature = "std")]
73impl StdError for Trap {
74 fn description(&self) -> &str {
75 self.code().map(|code| code.trap_message()).unwrap_or("")
76 }
77}
78
79/// Error type which can be thrown by wasm code or by host environment.
80///
81/// See [`Trap`] for details.
82///
83/// [`Trap`]: struct.Trap.html
84#[derive(Debug, Copy, Clone)]
85pub enum TrapCode {
86 /// Wasm code executed `unreachable` opcode.
87 ///
88 /// `unreachable` is a special opcode which always traps upon execution.
89 /// This opcode have a similar purpose as `ud2` in x86.
90 Unreachable,
91
92 /// Attempt to load or store at the address which
93 /// lies outside of bounds of the memory.
94 ///
95 /// Since addresses are interpreted as unsigned integers, out of bounds access
96 /// can't happen with negative addresses (i.e. they will always wrap).
97 MemoryAccessOutOfBounds,
98
99 /// Attempt to access table element at index which
100 /// lies outside of bounds.
101 ///
102 /// This typically can happen when `call_indirect` is executed
103 /// with index that lies out of bounds.
104 ///
105 /// Since indexes are interpreted as unsinged integers, out of bounds access
106 /// can't happen with negative indexes (i.e. they will always wrap).
107 TableAccessOutOfBounds,
108
109 /// Attempt to access table element which is uninitialized (i.e. `None`).
110 ///
111 /// This typically can happen when `call_indirect` is executed.
112 ElemUninitialized,
113
114 /// Attempt to divide by zero.
115 ///
116 /// This trap typically can happen if `div` or `rem` is executed with
117 /// zero as divider.
118 DivisionByZero,
119
120 /// An integer arithmetic operation caused an overflow.
121 ///
122 /// This can happen when:
123 ///
124 /// - Trying to do signed division (or get the remainder) -2<sup>N-1</sup> over -1. This is
125 /// because the result +2<sup>N-1</sup> isn't representable as a N-bit signed integer.
126 IntegerOverflow,
127
128 /// Attempt to make a conversion to an int failed.
129 ///
130 /// This can happen when:
131 ///
132 /// - Trying to truncate NaNs, infinity, or value for which the result is out of range into an integer.
133 InvalidConversionToInt,
134
135 /// Stack overflow.
136 ///
137 /// This is likely caused by some infinite or very deep recursion.
138 /// Extensive inlining might also be the cause of stack overflow.
139 StackOverflow,
140
141 /// Attempt to invoke a function with mismatching signature.
142 ///
143 /// This can happen if a Wasm or host function was invoked
144 /// with mismatching parameters or result values.
145 ///
146 /// This can always happen with indirect calls as they always
147 /// specify the expected signature of function. If an indirect call is executed
148 /// with an index that points to a function with signature different of what is
149 /// expected by this indirect call, this trap is raised.
150 UnexpectedSignature,
151}
152
153impl TrapCode {
154 /// Returns the trap message as specified by the WebAssembly specification.
155 ///
156 /// # Note
157 ///
158 /// This API is primarily useful for the Wasm spec testsuite but might have
159 /// other uses since it avoid heap memory allocation in certain cases.
160 pub fn trap_message(&self) -> &'static str {
161 match self {
162 TrapCode::Unreachable => "unreachable",
163 TrapCode::MemoryAccessOutOfBounds => "out of bounds memory access",
164 TrapCode::TableAccessOutOfBounds => "undefined element",
165 TrapCode::ElemUninitialized => "uninitialized element",
166 TrapCode::DivisionByZero => "integer divide by zero",
167 TrapCode::IntegerOverflow => "integer overflow",
168 TrapCode::InvalidConversionToInt => "invalid conversion to integer",
169 TrapCode::StackOverflow => "call stack exhausted",
170 TrapCode::UnexpectedSignature => "indirect call type mismatch",
171 }
172 }
173}
174
175impl Display for TrapCode {
176 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
177 write!(f, "{}", self.trap_message())
178 }
179}