Skip to main content

solana_sbpf/
error.rs

1// Copyright 2016 6WIND S.A. <quentin.monnet@6wind.com>
2//
3// Licensed under the Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0> or
4// the MIT license <http://opensource.org/licenses/MIT>, at your option. This file may not be
5// copied, modified, or distributed except according to those terms.
6
7//! This module contains error and result types
8
9use {
10    crate::{elf::ElfError, memory_region::AccessType, verifier::VerifierError},
11    std::error::Error,
12};
13
14/// Error definitions
15#[derive(Debug, thiserror::Error)]
16// Note: `#[repr(u64)]` is used for `Self::discriminant` and
17// `emit_exception_kind` in JIT, but the actual memory layout of this enum's
18// variants is not depended on by the VM.
19#[repr(u64)]
20pub enum EbpfError {
21    /// ELF error
22    #[error("ELF error: {0}")]
23    ElfError(#[from] ElfError),
24    /// Function was already registered
25    #[error("function #{0} was already registered")]
26    FunctionAlreadyRegistered(usize),
27    /// Exceeded max BPF to BPF call depth
28    #[error("exceeded max BPF to BPF call depth")]
29    CallDepthExceeded,
30    /// Attempt to exit from root call frame
31    #[error("attempted to exit root call frame")]
32    ExitRootCallFrame,
33    /// Divide by zero"
34    #[error("divide by zero at BPF instruction")]
35    DivideByZero,
36    /// Divide overflow
37    #[error("division overflow at BPF instruction")]
38    DivideOverflow,
39    /// Exceeded max instructions allowed
40    #[error("attempted to execute past the end of the text segment at BPF instruction")]
41    ExecutionOverrun,
42    /// Attempt to call to an address outside the text segment
43    #[error("callx attempted to call outside of the text segment")]
44    CallOutsideTextSegment,
45    /// Exceeded max instructions allowed
46    #[error("exceeded CUs meter at BPF instruction")]
47    ExceededMaxInstructions,
48    /// Program has not been JIT-compiled
49    #[error("program has not been JIT-compiled")]
50    JitNotCompiled,
51    /// Memory region index or virtual address space is invalid
52    #[error("Invalid memory region at index {0}")]
53    InvalidMemoryRegion(usize),
54    /// Access violation (general)
55    #[error("Access violation {0} {2} bytes at address {1:#x} (in {3} region)")]
56    AccessViolation(AccessType, u64, u64, &'static str),
57    /// Access violation (stack specific)
58    #[error("Access violation in stack frame {3} at address {1:#x} of size {2:?}")]
59    StackAccessViolation(AccessType, u64, u64, i64),
60    /// Invalid instruction
61    #[error("invalid BPF instruction")]
62    InvalidInstruction,
63    /// Unsupported instruction
64    #[error("unsupported BPF instruction")]
65    UnsupportedInstruction,
66    /// Compilation is too big to fit
67    #[error("Compilation exhausted text segment at BPF instruction {0}")]
68    ExhaustedTextSegment(usize),
69    /// Libc function call returned an error
70    #[error("Libc calling {0} {1:?} returned error code {2}")]
71    LibcInvocationFailed(&'static str, Vec<String>, i32),
72    /// Verifier error
73    #[error("Verifier error: {0}")]
74    VerifierError(#[from] VerifierError),
75    /// Syscall error
76    #[error("Syscall error: {0}")]
77    SyscallError(Box<dyn Error>),
78}
79
80impl EbpfError {
81    /// Returns the enum discriminant as a `u64`.
82    ///
83    /// This is sound only because of the `#[repr(u64)]` attribute on the enum.
84    pub fn discriminant(&self) -> u64 {
85        unsafe { *std::ptr::addr_of!(*self).cast::<u64>() }
86    }
87}
88
89/// Same as `Result` but provides a stable memory layout
90#[derive(Debug)]
91#[repr(C, u64)]
92pub enum StableResult<T, E> {
93    /// Success
94    Ok(T),
95    /// Failure
96    Err(E),
97}
98
99impl<T: std::fmt::Debug, E: std::fmt::Debug> StableResult<T, E> {
100    /// `true` if `Ok`
101    pub fn is_ok(&self) -> bool {
102        match self {
103            Self::Ok(_) => true,
104            Self::Err(_) => false,
105        }
106    }
107
108    /// `true` if `Err`
109    pub fn is_err(&self) -> bool {
110        match self {
111            Self::Ok(_) => false,
112            Self::Err(_) => true,
113        }
114    }
115
116    /// Returns the inner value if `Ok`, panics otherwise
117    pub fn unwrap(self) -> T {
118        match self {
119            Self::Ok(value) => value,
120            Self::Err(error) => panic!("unwrap {:?}", error),
121        }
122    }
123
124    /// Returns the inner error if `Err`, panics otherwise
125    pub fn unwrap_err(self) -> E {
126        match self {
127            Self::Ok(value) => panic!("unwrap_err {:?}", value),
128            Self::Err(error) => error,
129        }
130    }
131
132    /// Maps ok values, leaving error values untouched
133    pub fn map<U, O: FnOnce(T) -> U>(self, op: O) -> StableResult<U, E> {
134        match self {
135            Self::Ok(value) => StableResult::<U, E>::Ok(op(value)),
136            Self::Err(error) => StableResult::<U, E>::Err(error),
137        }
138    }
139
140    /// Maps error values, leaving ok values untouched
141    pub fn map_err<F, O: FnOnce(E) -> F>(self, op: O) -> StableResult<T, F> {
142        match self {
143            Self::Ok(value) => StableResult::<T, F>::Ok(value),
144            Self::Err(error) => StableResult::<T, F>::Err(op(error)),
145        }
146    }
147
148    #[cfg_attr(
149        any(
150            not(feature = "jit"),
151            target_os = "windows",
152            not(target_arch = "x86_64")
153        ),
154        allow(dead_code)
155    )]
156    pub(crate) fn discriminant(&self) -> u64 {
157        unsafe { *std::ptr::addr_of!(*self).cast::<u64>() }
158    }
159}
160
161impl<T, E> From<StableResult<T, E>> for Result<T, E> {
162    fn from(result: StableResult<T, E>) -> Self {
163        match result {
164            StableResult::Ok(value) => Ok(value),
165            StableResult::Err(value) => Err(value),
166        }
167    }
168}
169
170impl<T, E> From<Result<T, E>> for StableResult<T, E> {
171    fn from(result: Result<T, E>) -> Self {
172        match result {
173            Ok(value) => Self::Ok(value),
174            Err(value) => Self::Err(value),
175        }
176    }
177}
178
179/// Return value of programs and syscalls
180pub type ProgramResult = StableResult<u64, EbpfError>;
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185
186    #[test]
187    fn test_program_result_is_stable() {
188        let ok = ProgramResult::Ok(42);
189        assert_eq!(ok.discriminant(), 0);
190        let err = ProgramResult::Err(EbpfError::JitNotCompiled);
191        assert_eq!(err.discriminant(), 1);
192    }
193}