use {
crate::{elf::ElfError, memory_region::AccessType, verifier::VerifierError},
std::error::Error,
};
#[derive(Debug, thiserror::Error)]
#[repr(u64)] pub enum EbpfError {
#[error("ELF error: {0}")]
ElfError(#[from] ElfError),
#[error("function #{0} was already registered")]
FunctionAlreadyRegistered(usize),
#[error("exceeded max BPF to BPF call depth")]
CallDepthExceeded,
#[error("attempted to exit root call frame")]
ExitRootCallFrame,
#[error("divide by zero at BPF instruction")]
DivideByZero,
#[error("division overflow at BPF instruction")]
DivideOverflow,
#[error("attempted to execute past the end of the text segment at BPF instruction")]
ExecutionOverrun,
#[error("callx attempted to call outside of the text segment")]
CallOutsideTextSegment,
#[error("exceeded CUs meter at BPF instruction")]
ExceededMaxInstructions,
#[error("program has not been JIT-compiled")]
JitNotCompiled,
#[error("invalid virtual address {0:x?}")]
InvalidVirtualAddress(u64),
#[error("Invalid memory region at index {0}")]
InvalidMemoryRegion(usize),
#[error("Access violation in {3} section at address {1:#x} of size {2:?}")]
AccessViolation(AccessType, u64, u64, &'static str),
#[error("Access violation in stack frame {3} at address {1:#x} of size {2:?}")]
StackAccessViolation(AccessType, u64, u64, i64),
#[error("invalid BPF instruction")]
InvalidInstruction,
#[error("unsupported BPF instruction")]
UnsupportedInstruction,
#[error("Compilation exhausted text segment at BPF instruction {0}")]
ExhaustedTextSegment(usize),
#[error("Libc calling {0} {1:?} returned error code {2}")]
LibcInvocationFailed(&'static str, Vec<String>, i32),
#[error("Verifier error: {0}")]
VerifierError(#[from] VerifierError),
#[error("Syscall error: {0}")]
SyscallError(Box<dyn Error>),
}
#[derive(Debug)]
#[repr(C, u64)]
pub enum StableResult<T, E> {
Ok(T),
Err(E),
}
impl<T: std::fmt::Debug, E: std::fmt::Debug> StableResult<T, E> {
pub fn is_ok(&self) -> bool {
match self {
Self::Ok(_) => true,
Self::Err(_) => false,
}
}
pub fn is_err(&self) -> bool {
match self {
Self::Ok(_) => false,
Self::Err(_) => true,
}
}
pub fn unwrap(self) -> T {
match self {
Self::Ok(value) => value,
Self::Err(error) => panic!("unwrap {:?}", error),
}
}
pub fn unwrap_err(self) -> E {
match self {
Self::Ok(value) => panic!("unwrap_err {:?}", value),
Self::Err(error) => error,
}
}
pub fn map<U, O: FnOnce(T) -> U>(self, op: O) -> StableResult<U, E> {
match self {
Self::Ok(value) => StableResult::<U, E>::Ok(op(value)),
Self::Err(error) => StableResult::<U, E>::Err(error),
}
}
pub fn map_err<F, O: FnOnce(E) -> F>(self, op: O) -> StableResult<T, F> {
match self {
Self::Ok(value) => StableResult::<T, F>::Ok(value),
Self::Err(error) => StableResult::<T, F>::Err(op(error)),
}
}
#[cfg_attr(
any(
not(feature = "jit"),
target_os = "windows",
not(target_arch = "x86_64")
),
allow(dead_code)
)]
pub(crate) fn discriminant(&self) -> u64 {
unsafe { *std::ptr::addr_of!(*self).cast::<u64>() }
}
}
impl<T, E> From<StableResult<T, E>> for Result<T, E> {
fn from(result: StableResult<T, E>) -> Self {
match result {
StableResult::Ok(value) => Ok(value),
StableResult::Err(value) => Err(value),
}
}
}
impl<T, E> From<Result<T, E>> for StableResult<T, E> {
fn from(result: Result<T, E>) -> Self {
match result {
Ok(value) => Self::Ok(value),
Err(value) => Self::Err(value),
}
}
}
pub type ProgramResult = StableResult<u64, EbpfError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_program_result_is_stable() {
let ok = ProgramResult::Ok(42);
assert_eq!(ok.discriminant(), 0);
let err = ProgramResult::Err(EbpfError::JitNotCompiled);
assert_eq!(err.discriminant(), 1);
}
}