Skip to main content

wasmer_types/
trapcode.rs

1// This file contains code from external sources.
2// Attributions: https://github.com/wasmerio/wasmer/blob/main/docs/ATTRIBUTIONS.md
3
4//! Trap codes describing the reason for a trap.
5
6use core::fmt::{self, Display, Formatter};
7use core::str::FromStr;
8use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
9#[cfg(feature = "enable-serde")]
10use serde::{Deserialize, Serialize};
11use thiserror::Error;
12
13/// A trap code describing the reason for a trap.
14///
15/// All trap instructions have an explicit trap code.
16#[derive(
17    Clone,
18    Copy,
19    PartialEq,
20    Eq,
21    Debug,
22    Hash,
23    Error,
24    RkyvSerialize,
25    RkyvDeserialize,
26    Archive,
27    num_enum::TryFromPrimitive,
28)]
29#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
30#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
31#[rkyv(derive(Debug), compare(PartialEq))]
32#[repr(u32)]
33pub enum TrapCode {
34    /// The current stack space was exhausted.
35    ///
36    /// On some platforms, a stack overflow may also be indicated by a segmentation fault from the
37    /// stack guard page.
38    StackOverflow = 0,
39
40    /// A `heap_addr` instruction detected an out-of-bounds error.
41    ///
42    /// Note that not all out-of-bounds heap accesses are reported this way;
43    /// some are detected by a segmentation fault on the heap unmapped or
44    /// offset-guard pages.
45    HeapAccessOutOfBounds = 1,
46
47    /// A `heap_addr` instruction was misaligned.
48    HeapMisaligned = 2,
49
50    /// A `table_addr` instruction detected an out-of-bounds error.
51    TableAccessOutOfBounds = 3,
52
53    /// Indirect call to a null table entry.
54    IndirectCallToNull = 4,
55
56    /// Signature mismatch on indirect call.
57    BadSignature = 5,
58
59    /// An integer arithmetic operation caused an overflow.
60    IntegerOverflow = 6,
61
62    /// An integer division by zero.
63    IntegerDivisionByZero = 7,
64
65    /// Failed float-to-int conversion.
66    BadConversionToInteger = 8,
67
68    /// Code that was supposed to have been unreachable was reached.
69    UnreachableCodeReached = 9,
70
71    /// An atomic memory access was attempted with an unaligned pointer.
72    UnalignedAtomic = 10,
73
74    /// An exception was thrown but it was left uncaught.
75    UncaughtException = 11,
76
77    /// A throw_ref was executed but the exnref was not initialized.
78    UninitializedExnRef = 12,
79
80    /// An async imported function tried to yield when not called
81    /// via `Function::call_async`.
82    YieldOutsideAsyncContext = 13,
83
84    /// Another host thread requested interruption of running WASM.
85    HostInterrupt = 14,
86
87    /// A table modification operation for a read-only table.
88    ReadonlyTableModified = 15,
89}
90
91impl TrapCode {
92    /// Gets the message for this trap code
93    pub fn message(&self) -> &str {
94        match self {
95            Self::StackOverflow => "call stack exhausted",
96            Self::HeapAccessOutOfBounds => "out of bounds memory access",
97            Self::HeapMisaligned => "misaligned heap",
98            Self::TableAccessOutOfBounds => "undefined element: out of bounds table access",
99            Self::IndirectCallToNull => "uninitialized element",
100            Self::BadSignature => "indirect call type mismatch",
101            Self::IntegerOverflow => "integer overflow",
102            Self::IntegerDivisionByZero => "integer divide by zero",
103            Self::BadConversionToInteger => "invalid conversion to integer",
104            Self::UnreachableCodeReached => "unreachable",
105            Self::UnalignedAtomic => "unaligned atomic access",
106            Self::UncaughtException => "uncaught exception",
107            Self::UninitializedExnRef => "uninitialized exnref",
108            Self::YieldOutsideAsyncContext => {
109                "async imported function yielded when not called via `Function::call_async`"
110            }
111            Self::HostInterrupt => "interrupted by host",
112            Self::ReadonlyTableModified => "read-only table modified",
113        }
114    }
115}
116
117impl Display for TrapCode {
118    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
119        let identifier = match *self {
120            Self::StackOverflow => "stk_ovf",
121            Self::HeapAccessOutOfBounds => "heap_get_oob",
122            Self::HeapMisaligned => "heap_misaligned",
123            Self::TableAccessOutOfBounds => "table_get_oob",
124            Self::IndirectCallToNull => "icall_null",
125            Self::BadSignature => "bad_sig",
126            Self::IntegerOverflow => "int_ovf",
127            Self::IntegerDivisionByZero => "int_divz",
128            Self::BadConversionToInteger => "bad_toint",
129            Self::UnreachableCodeReached => "unreachable",
130            Self::UnalignedAtomic => "unalign_atom",
131            Self::UncaughtException => "uncaught_exception",
132            Self::UninitializedExnRef => "uninitialized_exnref",
133            Self::YieldOutsideAsyncContext => "yield_outside_async_context",
134            Self::HostInterrupt => "host_interrupt",
135            Self::ReadonlyTableModified => "readonly_table_modified",
136        };
137        f.write_str(identifier)
138    }
139}
140
141impl FromStr for TrapCode {
142    type Err = ();
143
144    fn from_str(s: &str) -> Result<Self, Self::Err> {
145        match s {
146            "stk_ovf" => Ok(Self::StackOverflow),
147            "heap_get_oob" => Ok(Self::HeapAccessOutOfBounds),
148            "heap_misaligned" => Ok(Self::HeapMisaligned),
149            "table_get_oob" => Ok(Self::TableAccessOutOfBounds),
150            "icall_null" => Ok(Self::IndirectCallToNull),
151            "bad_sig" => Ok(Self::BadSignature),
152            "int_ovf" => Ok(Self::IntegerOverflow),
153            "int_divz" => Ok(Self::IntegerDivisionByZero),
154            "bad_toint" => Ok(Self::BadConversionToInteger),
155            "unreachable" => Ok(Self::UnreachableCodeReached),
156            "unalign_atom" => Ok(Self::UnalignedAtomic),
157            "uncaught_exception" => Ok(Self::UncaughtException),
158            "uninitialized_exnref" => Ok(Self::UninitializedExnRef),
159            "yield_outside_async_context" => Ok(Self::YieldOutsideAsyncContext),
160            "host_interrupt" => Ok(Self::HostInterrupt),
161            _ => Err(()),
162        }
163    }
164}
165
166// TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451
167/// After the stack is unwound via asyncify what
168/// should the call loop do next
169#[derive(Debug)]
170pub enum OnCalledAction {
171    /// Will call the function again
172    InvokeAgain,
173    /// Will return the result of the invocation
174    Finish,
175    /// Traps with an error
176    Trap(Box<dyn std::error::Error + Send + Sync>),
177}
178
179#[cfg(test)]
180mod tests {
181    use super::*;
182
183    // Everything but user-defined codes.
184    const CODES: [TrapCode; 11] = [
185        TrapCode::StackOverflow,
186        TrapCode::HeapAccessOutOfBounds,
187        TrapCode::HeapMisaligned,
188        TrapCode::TableAccessOutOfBounds,
189        TrapCode::IndirectCallToNull,
190        TrapCode::BadSignature,
191        TrapCode::IntegerOverflow,
192        TrapCode::IntegerDivisionByZero,
193        TrapCode::BadConversionToInteger,
194        TrapCode::UnreachableCodeReached,
195        TrapCode::UnalignedAtomic,
196    ];
197
198    #[test]
199    fn display() {
200        for r in &CODES {
201            let tc = *r;
202            assert_eq!(tc.to_string().parse(), Ok(tc));
203        }
204        assert_eq!("bogus".parse::<TrapCode>(), Err(()));
205
206        // assert_eq!(TrapCode::User(17).to_string(), "user17");
207        // assert_eq!("user22".parse(), Ok(TrapCode::User(22)));
208        assert_eq!("user".parse::<TrapCode>(), Err(()));
209        assert_eq!("user-1".parse::<TrapCode>(), Err(()));
210        assert_eq!("users".parse::<TrapCode>(), Err(()));
211    }
212}