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    rkyv::CheckBytes,
28)]
29#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
30#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
31#[repr(u32)]
32#[archive(as = "Self")]
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
75impl TrapCode {
76    /// Gets the message for this trap code
77    pub fn message(&self) -> &str {
78        match self {
79            Self::StackOverflow => "call stack exhausted",
80            Self::HeapAccessOutOfBounds => "out of bounds memory access",
81            Self::HeapMisaligned => "misaligned heap",
82            Self::TableAccessOutOfBounds => "undefined element: out of bounds table access",
83            Self::IndirectCallToNull => "uninitialized element",
84            Self::BadSignature => "indirect call type mismatch",
85            Self::IntegerOverflow => "integer overflow",
86            Self::IntegerDivisionByZero => "integer divide by zero",
87            Self::BadConversionToInteger => "invalid conversion to integer",
88            Self::UnreachableCodeReached => "unreachable",
89            Self::UnalignedAtomic => "unaligned atomic access",
90        }
91    }
92}
93
94impl Display for TrapCode {
95    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
96        let identifier = match *self {
97            Self::StackOverflow => "stk_ovf",
98            Self::HeapAccessOutOfBounds => "heap_get_oob",
99            Self::HeapMisaligned => "heap_misaligned",
100            Self::TableAccessOutOfBounds => "table_get_oob",
101            Self::IndirectCallToNull => "icall_null",
102            Self::BadSignature => "bad_sig",
103            Self::IntegerOverflow => "int_ovf",
104            Self::IntegerDivisionByZero => "int_divz",
105            Self::BadConversionToInteger => "bad_toint",
106            Self::UnreachableCodeReached => "unreachable",
107            Self::UnalignedAtomic => "unalign_atom",
108        };
109        f.write_str(identifier)
110    }
111}
112
113impl FromStr for TrapCode {
114    type Err = ();
115
116    fn from_str(s: &str) -> Result<Self, Self::Err> {
117        match s {
118            "stk_ovf" => Ok(Self::StackOverflow),
119            "heap_get_oob" => Ok(Self::HeapAccessOutOfBounds),
120            "heap_misaligned" => Ok(Self::HeapMisaligned),
121            "table_get_oob" => Ok(Self::TableAccessOutOfBounds),
122            "icall_null" => Ok(Self::IndirectCallToNull),
123            "bad_sig" => Ok(Self::BadSignature),
124            "int_ovf" => Ok(Self::IntegerOverflow),
125            "int_divz" => Ok(Self::IntegerDivisionByZero),
126            "bad_toint" => Ok(Self::BadConversionToInteger),
127            "unreachable" => Ok(Self::UnreachableCodeReached),
128            "unalign_atom" => Ok(Self::UnalignedAtomic),
129            _ => Err(()),
130        }
131    }
132}
133
134// TODO: OnCalledAction is needed for asyncify. It will be refactored with https://github.com/wasmerio/wasmer/issues/3451
135/// After the stack is unwound via asyncify what
136/// should the call loop do next
137#[derive(Debug)]
138pub enum OnCalledAction {
139    /// Will call the function again
140    InvokeAgain,
141    /// Will return the result of the invocation
142    Finish,
143    /// Traps with an error
144    Trap(Box<dyn std::error::Error + Send + Sync>),
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150
151    // Everything but user-defined codes.
152    const CODES: [TrapCode; 11] = [
153        TrapCode::StackOverflow,
154        TrapCode::HeapAccessOutOfBounds,
155        TrapCode::HeapMisaligned,
156        TrapCode::TableAccessOutOfBounds,
157        TrapCode::IndirectCallToNull,
158        TrapCode::BadSignature,
159        TrapCode::IntegerOverflow,
160        TrapCode::IntegerDivisionByZero,
161        TrapCode::BadConversionToInteger,
162        TrapCode::UnreachableCodeReached,
163        TrapCode::UnalignedAtomic,
164    ];
165
166    #[test]
167    fn display() {
168        for r in &CODES {
169            let tc = *r;
170            assert_eq!(tc.to_string().parse(), Ok(tc));
171        }
172        assert_eq!("bogus".parse::<TrapCode>(), Err(()));
173
174        // assert_eq!(TrapCode::User(17).to_string(), "user17");
175        // assert_eq!("user22".parse(), Ok(TrapCode::User(22)));
176        assert_eq!("user".parse::<TrapCode>(), Err(()));
177        assert_eq!("user-1".parse::<TrapCode>(), Err(()));
178        assert_eq!("users".parse::<TrapCode>(), Err(()));
179    }
180}