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