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