near_vm_vm/trap/
trapcode.rs

1// This file contains code from external sources.
2// Attributions: https://github.com/wasmerio/wasmer/blob/2.3.0/ATTRIBUTIONS.md
3
4//! Trap codes describing the reason for a trap.
5
6use core::fmt::{self, Display, Formatter};
7use core::str::FromStr;
8use thiserror::Error;
9
10/// A trap code describing the reason for a trap.
11///
12/// All trap instructions have an explicit trap code.
13#[derive(
14    Clone,
15    Copy,
16    PartialEq,
17    Eq,
18    Debug,
19    Hash,
20    Error,
21    rkyv::Serialize,
22    rkyv::Deserialize,
23    rkyv::Archive,
24)]
25#[repr(u32)]
26pub enum TrapCode {
27    /// The current stack space was exhausted.
28    ///
29    /// On some platforms, a stack overflow may also be indicated by a segmentation fault from the
30    /// stack guard page.
31    StackOverflow = 0,
32
33    /// A `heap_addr` instruction detected an out-of-bounds error.
34    ///
35    /// Note that not all out-of-bounds heap accesses are reported this way;
36    /// some are detected by a segmentation fault on the heap unmapped or
37    /// offset-guard pages.
38    HeapAccessOutOfBounds = 1,
39
40    /// A `heap_addr` instruction was misaligned.
41    HeapMisaligned = 2,
42
43    /// A `table_addr` instruction detected an out-of-bounds error.
44    TableAccessOutOfBounds = 3,
45
46    /// Other bounds checking error.
47    OutOfBounds = 4,
48
49    /// Indirect call to a null table entry.
50    IndirectCallToNull = 5,
51
52    /// Signature mismatch on indirect call.
53    BadSignature = 6,
54
55    /// An integer arithmetic operation caused an overflow.
56    IntegerOverflow = 7,
57
58    /// An integer division by zero.
59    IntegerDivisionByZero = 8,
60
61    /// Failed float-to-int conversion.
62    BadConversionToInteger = 9,
63
64    /// Code that was supposed to have been unreachable was reached.
65    UnreachableCodeReached = 10,
66
67    /// An atomic memory access was attempted with an unaligned pointer.
68    UnalignedAtomic = 11,
69
70    /// Hit the gas limit.
71    GasExceeded = 12,
72}
73
74impl TrapCode {
75    /// Gets the message for this trap code
76    pub fn message(&self) -> &str {
77        match self {
78            Self::StackOverflow => "call stack exhausted",
79            Self::HeapAccessOutOfBounds => "out of bounds memory access",
80            Self::HeapMisaligned => "misaligned heap",
81            Self::TableAccessOutOfBounds => "undefined element: out of bounds table access",
82            Self::OutOfBounds => "out of bounds",
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            Self::GasExceeded => "gas limit exceeded",
91        }
92    }
93}
94
95impl Display for TrapCode {
96    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
97        let identifier = match *self {
98            Self::StackOverflow => "stk_ovf",
99            Self::HeapAccessOutOfBounds => "heap_get_oob",
100            Self::HeapMisaligned => "heap_misaligned",
101            Self::TableAccessOutOfBounds => "table_get_oob",
102            Self::OutOfBounds => "oob",
103            Self::IndirectCallToNull => "icall_null",
104            Self::BadSignature => "bad_sig",
105            Self::IntegerOverflow => "int_ovf",
106            Self::IntegerDivisionByZero => "int_divz",
107            Self::BadConversionToInteger => "bad_toint",
108            Self::UnreachableCodeReached => "unreachable",
109            Self::UnalignedAtomic => "unalign_atom",
110            Self::GasExceeded => "out_of_gas",
111        };
112        f.write_str(identifier)
113    }
114}
115
116impl FromStr for TrapCode {
117    type Err = ();
118
119    fn from_str(s: &str) -> Result<Self, Self::Err> {
120        match s {
121            "stk_ovf" => Ok(Self::StackOverflow),
122            "heap_get_oob" => Ok(Self::HeapAccessOutOfBounds),
123            "heap_misaligned" => Ok(Self::HeapMisaligned),
124            "table_get_oob" => Ok(Self::TableAccessOutOfBounds),
125            "oob" => Ok(Self::OutOfBounds),
126            "icall_null" => Ok(Self::IndirectCallToNull),
127            "bad_sig" => Ok(Self::BadSignature),
128            "int_ovf" => Ok(Self::IntegerOverflow),
129            "int_divz" => Ok(Self::IntegerDivisionByZero),
130            "bad_toint" => Ok(Self::BadConversionToInteger),
131            "unreachable" => Ok(Self::UnreachableCodeReached),
132            "unalign_atom" => Ok(Self::UnalignedAtomic),
133            _ => Err(()),
134        }
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141
142    // Everything but user-defined codes.
143    const CODES: [TrapCode; 12] = [
144        TrapCode::StackOverflow,
145        TrapCode::HeapAccessOutOfBounds,
146        TrapCode::HeapMisaligned,
147        TrapCode::TableAccessOutOfBounds,
148        TrapCode::OutOfBounds,
149        TrapCode::IndirectCallToNull,
150        TrapCode::BadSignature,
151        TrapCode::IntegerOverflow,
152        TrapCode::IntegerDivisionByZero,
153        TrapCode::BadConversionToInteger,
154        TrapCode::UnreachableCodeReached,
155        TrapCode::UnalignedAtomic,
156    ];
157
158    #[test]
159    fn display() {
160        for r in &CODES {
161            let tc = *r;
162            assert_eq!(tc.to_string().parse(), Ok(tc));
163        }
164        assert_eq!("bogus".parse::<TrapCode>(), Err(()));
165
166        // assert_eq!(TrapCode::User(17).to_string(), "user17");
167        // assert_eq!("user22".parse(), Ok(TrapCode::User(22)));
168        assert_eq!("user".parse::<TrapCode>(), Err(()));
169        assert_eq!("user-1".parse::<TrapCode>(), Err(()));
170        assert_eq!("users".parse::<TrapCode>(), Err(()));
171    }
172}