use std::array::TryFromSliceError;
use std::cell::{BorrowError, BorrowMutError};
use std::convert::Infallible;
use std::error::Error;
use std::num::TryFromIntError;
use std::string::FromUtf8Error;
use std::sync::{MutexGuard, PoisonError};
use std::time::SystemTimeError;
#[cfg(target_os = "windows")]
use crossbeam_channel::{RecvError, SendError};
use flatbuffers::InvalidFlatbuffer;
use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterValue, ReturnValue};
use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
use thiserror::Error;
use crate::hypervisor::hyperlight_vm::HyperlightVmError;
#[cfg(target_os = "windows")]
use crate::hypervisor::wrappers::HandleWrapper;
use crate::mem::memory_region::MemoryRegionFlags;
use crate::mem::ptr::RawPtr;
#[derive(Error, Debug)]
pub enum HyperlightError {
#[error("Anyhow Error was returned: {0}")]
AnyhowError(#[from] anyhow::Error),
#[error("Offset: {0} out of bounds, Max is: {1}")]
BoundsCheckFailed(u64, usize),
#[error("Couldn't add offset to base address. Offset: {0}, Base Address: {1}")]
CheckedAddOverflow(u64, u64),
#[error("{0:?}")]
#[cfg(target_os = "windows")]
CrossBeamReceiveError(#[from] RecvError),
#[error("{0:?}")]
#[cfg(target_os = "windows")]
CrossBeamSendError(#[from] SendError<HandleWrapper>),
#[error("Error converting CString {0:?}")]
CStringConversionError(#[from] std::ffi::NulError),
#[error("{0}")]
Error(String),
#[error("Non-executable address {0:#x} tried to be executed")]
ExecutionAccessViolation(u64),
#[error("Execution was cancelled by the host.")]
ExecutionCanceledByHost(),
#[error("Failed to get a value from flat buffer parameter")]
FailedToGetValueFromParameter(),
#[error("Field Name {0} not found in decoded GuestLogData")]
FieldIsMissingInGuestLogData(String),
#[error("Guest aborted: {0} {1}")]
GuestAborted(u8, String),
#[error("Guest error occurred {0:?}: {1}")]
GuestError(ErrorCode, String),
#[error("Guest execution hung on the execution of a host function call")]
GuestExecutionHungOnHostFunctionCall(),
#[error("Guest call is already in progress")]
GuestFunctionCallAlreadyInProgress(),
#[error("Unsupported type: {0}")]
GuestInterfaceUnsupportedType(String),
#[error(
"Guest binary was built with hyperlight-guest-bin {guest_bin_version}, \
but the host is running hyperlight {host_version}"
)]
GuestBinVersionMismatch {
guest_bin_version: String,
host_version: String,
},
#[error("HostFunction {0} was not found")]
HostFunctionNotFound(String),
#[doc(hidden)]
#[error("Internal Hyperlight VM error: {0}")]
HyperlightVmError(#[from] HyperlightVmError),
#[error("Reading Writing or Seeking data failed {0:?}")]
IOError(#[from] std::io::Error),
#[error("Failed To Convert Size to usize")]
IntConversionFailure(#[from] TryFromIntError),
#[error("The flatbuffer is invalid")]
InvalidFlatBuffer(#[from] InvalidFlatbuffer),
#[error("Conversion of str data to json failed")]
JsonConversionFailure(#[from] serde_json::Error),
#[error("Unable to lock resource")]
LockAttemptFailed(String),
#[error("Memory Access Violation at address {0:#x} of type {1}, but memory is marked as {2}")]
MemoryAccessViolation(u64, MemoryRegionFlags, MemoryRegionFlags),
#[error("Memory Allocation Failed with OS Error {0:?}.")]
MemoryAllocationFailed(Option<i32>),
#[error("Memory Protection Failed with OS Error {0:?}.")]
MemoryProtectionFailed(Option<i32>),
#[error("Memory region size mismatch: host size {0:?}, guest size {1:?} region {2:?}")]
MemoryRegionSizeMismatch(usize, usize, String),
#[error("Memory requested {0} exceeds maximum size allowed {1}")]
MemoryRequestTooBig(usize, usize),
#[error("Memory requested {0} is less than the minimum size allowed {1}")]
MemoryRequestTooSmall(usize, usize),
#[error("Metric Not Found {0:?}.")]
MetricNotFound(&'static str),
#[error("mmap failed with os error {0:?}")]
MmapFailed(Option<i32>),
#[error("mprotect failed with os error {0:?}")]
MprotectFailed(Option<i32>),
#[error("No Hypervisor was found for Sandbox")]
NoHypervisorFound(),
#[error("Restore_state called with no valid snapshot")]
NoMemorySnapshot,
#[error("Failed To Convert Parameter Value {0:?} to {1:?}")]
ParameterValueConversionFailure(ParameterValue, &'static str),
#[error("Failure processing PE File {0:?}")]
PEFileProcessingFailure(#[from] goblin::error::Error),
#[error("The sandbox was poisoned")]
PoisonedSandbox,
#[error("Raw pointer ({0:?}) was less than the base address ({1})")]
RawPointerLessThanBaseAddress(RawPtr, u64),
#[error("RefCell borrow failed")]
RefCellBorrowFailed(#[from] BorrowError),
#[error("RefCell mut borrow failed")]
RefCellMutBorrowFailed(#[from] BorrowMutError),
#[error("Failed To Convert Return Value {0:?} to {1:?}")]
ReturnValueConversionFailure(ReturnValue, &'static str),
#[error("Snapshot was taken from a different sandbox")]
SnapshotSandboxMismatch,
#[error("SystemTimeError {0:?}")]
SystemTimeError(#[from] SystemTimeError),
#[error("TryFromSliceError {0:?}")]
TryFromSliceError(#[from] TryFromSliceError),
#[error("The number of arguments to the function is wrong: got {0:?} expected {1:?}")]
UnexpectedNoOfArguments(usize, usize),
#[error("The parameter value type is unexpected got {0:?} expected {1:?}")]
UnexpectedParameterValueType(ParameterValue, String),
#[error("The return value type is unexpected got {0:?} expected {1:?}")]
UnexpectedReturnValueType(ReturnValue, String),
#[error("String Conversion of UTF8 data to str failed")]
UTF8StringConversionFailure(#[from] FromUtf8Error),
#[error(
"The capacity of the vector is incorrect. Capacity: {0}, Length: {1}, FlatBuffer Size: {2}"
)]
VectorCapacityIncorrect(usize, usize, i32),
#[error("vmm sys Error {0:?}")]
#[cfg(target_os = "linux")]
VmmSysError(vmm_sys_util::errno::Error),
#[cfg(target_os = "windows")]
#[error("Windows API Error Result {0:?}")]
WindowsAPIError(#[from] windows_result::Error),
}
impl From<Infallible> for HyperlightError {
fn from(_: Infallible) -> Self {
"Impossible as this is an infallible error".into()
}
}
impl From<&str> for HyperlightError {
fn from(s: &str) -> Self {
HyperlightError::Error(s.to_string())
}
}
impl<T> From<PoisonError<MutexGuard<'_, T>>> for HyperlightError {
fn from(e: PoisonError<MutexGuard<'_, T>>) -> Self {
let source = match e.source() {
Some(s) => s.to_string(),
None => String::from(""),
};
HyperlightError::LockAttemptFailed(source)
}
}
impl HyperlightError {
pub(crate) fn is_poison_error(&self) -> bool {
match self {
HyperlightError::GuestAborted(_, _)
| HyperlightError::ExecutionCanceledByHost()
| HyperlightError::PoisonedSandbox
| HyperlightError::ExecutionAccessViolation(_)
| HyperlightError::MemoryAccessViolation(_, _, _)
| HyperlightError::MemoryRegionSizeMismatch(_, _, _)
| HyperlightError::HyperlightVmError(HyperlightVmError::Restore(_)) => true,
HyperlightError::HyperlightVmError(HyperlightVmError::UpdateRegion(_))
| HyperlightError::HyperlightVmError(HyperlightVmError::AccessPageTable(_)) => true,
HyperlightError::HyperlightVmError(HyperlightVmError::DispatchGuestCall(e)) => {
e.is_poison_error()
}
HyperlightError::AnyhowError(_)
| HyperlightError::BoundsCheckFailed(_, _)
| HyperlightError::CheckedAddOverflow(_, _)
| HyperlightError::CStringConversionError(_)
| HyperlightError::Error(_)
| HyperlightError::FailedToGetValueFromParameter()
| HyperlightError::FieldIsMissingInGuestLogData(_)
| HyperlightError::GuestBinVersionMismatch { .. }
| HyperlightError::GuestError(_, _)
| HyperlightError::GuestExecutionHungOnHostFunctionCall()
| HyperlightError::GuestFunctionCallAlreadyInProgress()
| HyperlightError::GuestInterfaceUnsupportedType(_)
| HyperlightError::HostFunctionNotFound(_)
| HyperlightError::HyperlightVmError(HyperlightVmError::Create(_))
| HyperlightError::HyperlightVmError(HyperlightVmError::Initialize(_))
| HyperlightError::HyperlightVmError(HyperlightVmError::MapRegion(_))
| HyperlightError::HyperlightVmError(HyperlightVmError::UnmapRegion(_))
| HyperlightError::IOError(_)
| HyperlightError::IntConversionFailure(_)
| HyperlightError::InvalidFlatBuffer(_)
| HyperlightError::JsonConversionFailure(_)
| HyperlightError::LockAttemptFailed(_)
| HyperlightError::MemoryAllocationFailed(_)
| HyperlightError::MemoryProtectionFailed(_)
| HyperlightError::MemoryRequestTooBig(_, _)
| HyperlightError::MemoryRequestTooSmall(_, _)
| HyperlightError::MetricNotFound(_)
| HyperlightError::MmapFailed(_)
| HyperlightError::MprotectFailed(_)
| HyperlightError::NoHypervisorFound()
| HyperlightError::NoMemorySnapshot
| HyperlightError::ParameterValueConversionFailure(_, _)
| HyperlightError::PEFileProcessingFailure(_)
| HyperlightError::RawPointerLessThanBaseAddress(_, _)
| HyperlightError::RefCellBorrowFailed(_)
| HyperlightError::RefCellMutBorrowFailed(_)
| HyperlightError::ReturnValueConversionFailure(_, _)
| HyperlightError::SnapshotSandboxMismatch
| HyperlightError::SystemTimeError(_)
| HyperlightError::TryFromSliceError(_)
| HyperlightError::UnexpectedNoOfArguments(_, _)
| HyperlightError::UnexpectedParameterValueType(_, _)
| HyperlightError::UnexpectedReturnValueType(_, _)
| HyperlightError::UTF8StringConversionFailure(_)
| HyperlightError::VectorCapacityIncorrect(_, _, _) => false,
#[cfg(target_os = "windows")]
HyperlightError::CrossBeamReceiveError(_) => false,
#[cfg(target_os = "windows")]
HyperlightError::CrossBeamSendError(_) => false,
#[cfg(target_os = "windows")]
HyperlightError::WindowsAPIError(_) => false,
#[cfg(target_os = "linux")]
HyperlightError::VmmSysError(_) => false,
}
}
}
#[macro_export]
macro_rules! new_error {
($msg:literal $(,)?) => {{
let __args = std::format_args!($msg);
let __err_msg = match __args.as_str() {
Some(msg) => String::from(msg),
None => std::format!($msg),
};
$crate::HyperlightError::Error(__err_msg)
}};
($fmtstr:expr, $($arg:tt)*) => {{
let __err_msg = std::format!($fmtstr, $($arg)*);
$crate::error::HyperlightError::Error(__err_msg)
}};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hypervisor::hyperlight_vm::{
DispatchGuestCallError, HandleIoError, HyperlightVmError, RunVmError,
};
use crate::sandbox::outb::HandleOutbError;
#[test]
fn test_promote_execution_cancelled_by_host() {
let err = DispatchGuestCallError::Run(RunVmError::ExecutionCancelledByHost);
let (promoted, should_poison) = err.promote();
assert!(
should_poison,
"ExecutionCancelledByHost should poison the sandbox"
);
assert!(
matches!(promoted, HyperlightError::ExecutionCanceledByHost()),
"Expected HyperlightError::ExecutionCanceledByHost, got {:?}",
promoted
);
}
#[test]
fn test_promote_guest_aborted() {
let err = DispatchGuestCallError::Run(RunVmError::HandleIo(HandleIoError::Outb(
HandleOutbError::GuestAborted {
code: 42,
message: "test abort".to_string(),
},
)));
let (promoted, should_poison) = err.promote();
assert!(should_poison, "GuestAborted should poison the sandbox");
match promoted {
HyperlightError::GuestAborted(code, msg) => {
assert_eq!(code, 42);
assert_eq!(msg, "test abort");
}
_ => panic!("Expected HyperlightError::GuestAborted, got {:?}", promoted),
}
}
#[test]
fn test_promote_memory_access_violation() {
let err = DispatchGuestCallError::Run(RunVmError::MemoryAccessViolation {
addr: 0xDEADBEEF,
access_type: MemoryRegionFlags::WRITE,
region_flags: MemoryRegionFlags::READ,
});
let (promoted, should_poison) = err.promote();
assert!(
should_poison,
"MemoryAccessViolation should poison the sandbox"
);
match promoted {
HyperlightError::MemoryAccessViolation(addr, access_type, region_flags) => {
assert_eq!(addr, 0xDEADBEEF);
assert_eq!(access_type, MemoryRegionFlags::WRITE);
assert_eq!(region_flags, MemoryRegionFlags::READ);
}
_ => panic!(
"Expected HyperlightError::MemoryAccessViolation, got {:?}",
promoted
),
}
}
#[test]
fn test_promote_other_run_errors_wrapped() {
let err = DispatchGuestCallError::Run(RunVmError::MmioReadUnmapped(0x1000));
let (promoted, should_poison) = err.promote();
assert!(should_poison, "Run errors should poison the sandbox");
assert!(
matches!(
promoted,
HyperlightError::HyperlightVmError(HyperlightVmError::DispatchGuestCall(_))
),
"Expected HyperlightError::HyperlightVmError, got {:?}",
promoted
);
}
}