Skip to content

Commit f24d902

Browse files
committed
Richer error handling for entire HyperlightVm
Signed-off-by: Ludvig Liljenberg <[email protected]>
1 parent 8c8d62a commit f24d902

File tree

15 files changed

+1142
-450
lines changed

15 files changed

+1142
-450
lines changed

src/hyperlight_host/src/error.rs

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterValue, Ret
3030
use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
3131
use thiserror::Error;
3232

33+
use crate::hypervisor::hyperlight_vm::HyperlightVmError;
3334
#[cfg(target_os = "windows")]
3435
use crate::hypervisor::wrappers::HandleWrapper;
3536
use crate::mem::memory_region::MemoryRegionFlags;
@@ -111,6 +112,14 @@ pub enum HyperlightError {
111112
#[error("HostFunction {0} was not found")]
112113
HostFunctionNotFound(String),
113114

115+
/// Hyperlight VM error.
116+
///
117+
/// **Note:** This error variant is considered internal and its structure is not stable.
118+
/// It may change between versions without notice. Users should not rely on this.
119+
#[doc(hidden)]
120+
#[error("Internal Hyperlight VM error: {0}")]
121+
HyperlightVmError(#[from] HyperlightVmError),
122+
114123
/// Reading Writing or Seeking data failed.
115124
#[error("Reading Writing or Seeking data failed {0:?}")]
116125
IOError(#[from] std::io::Error),
@@ -127,11 +136,6 @@ pub enum HyperlightError {
127136
#[error("Conversion of str data to json failed")]
128137
JsonConversionFailure(#[from] serde_json::Error),
129138

130-
/// KVM Error Occurred
131-
#[error("KVM Error {0:?}")]
132-
#[cfg(kvm)]
133-
KVMError(#[from] kvm_ioctls::Error),
134-
135139
/// An attempt to get a lock from a Mutex failed.
136140
#[error("Unable to lock resource")]
137141
LockAttemptFailed(String),
@@ -168,11 +172,6 @@ pub enum HyperlightError {
168172
#[error("mprotect failed with os error {0:?}")]
169173
MprotectFailed(Option<i32>),
170174

171-
/// mshv Error Occurred
172-
#[error("mshv Error {0:?}")]
173-
#[cfg(mshv3)]
174-
MSHVError(#[from] mshv_ioctls::MshvError),
175-
176175
/// No Hypervisor was found for Sandbox.
177176
#[error("No Hypervisor was found for Sandbox")]
178177
NoHypervisorFound(),
@@ -242,11 +241,6 @@ pub enum HyperlightError {
242241
#[error("SystemTimeError {0:?}")]
243242
SystemTimeError(#[from] SystemTimeError),
244243

245-
/// Error occurred when translating guest address
246-
#[error("An error occurred when translating guest address: {0:?}")]
247-
#[cfg(gdb)]
248-
TranslateGuestAddress(u64),
249-
250244
/// Error occurred converting a slice to an array
251245
#[error("TryFromSliceError {0:?}")]
252246
TryFromSliceError(#[from] TryFromSliceError),
@@ -334,6 +328,11 @@ impl HyperlightError {
334328
| HyperlightError::SnapshotSizeMismatch(_, _)
335329
| HyperlightError::MemoryRegionSizeMismatch(_, _, _) => true,
336330

331+
// HyperlightVmError::DispatchGuestCall may poison the sandbox
332+
HyperlightError::HyperlightVmError(HyperlightVmError::DispatchGuestCall(e)) => {
333+
e.is_poison_error()
334+
}
335+
337336
// All other errors do not poison the sandbox.
338337
HyperlightError::AnyhowError(_)
339338
| HyperlightError::BoundsCheckFailed(_, _)
@@ -348,6 +347,10 @@ impl HyperlightError {
348347
| HyperlightError::GuestInterfaceUnsupportedType(_)
349348
| HyperlightError::GuestOffsetIsInvalid(_)
350349
| HyperlightError::HostFunctionNotFound(_)
350+
| HyperlightError::HyperlightVmError(HyperlightVmError::Create(_))
351+
| HyperlightError::HyperlightVmError(HyperlightVmError::Initialize(_))
352+
| HyperlightError::HyperlightVmError(HyperlightVmError::MapRegion(_))
353+
| HyperlightError::HyperlightVmError(HyperlightVmError::UnmapRegion(_))
351354
| HyperlightError::IOError(_)
352355
| HyperlightError::IntConversionFailure(_)
353356
| HyperlightError::InvalidFlatBuffer(_)
@@ -384,12 +387,6 @@ impl HyperlightError {
384387
HyperlightError::WindowsAPIError(_) => false,
385388
#[cfg(target_os = "linux")]
386389
HyperlightError::VmmSysError(_) => false,
387-
#[cfg(kvm)]
388-
HyperlightError::KVMError(_) => false,
389-
#[cfg(mshv3)]
390-
HyperlightError::MSHVError(_) => false,
391-
#[cfg(gdb)]
392-
HyperlightError::TranslateGuestAddress(_) => false,
393390
}
394391
}
395392
}

src/hyperlight_host/src/hypervisor/gdb/arch.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,18 @@ limitations under the License.
1616

1717
//! This file contains architecture specific code for the x86_64
1818
19-
use super::{DebuggableVm, VcpuStopReason};
20-
use crate::Result;
19+
use super::{DebugError, DebuggableVm, VcpuStopReason};
2120
use crate::hypervisor::regs::CommonRegisters;
21+
use crate::hypervisor::virtual_machine::RegisterError;
22+
23+
/// Errors that can occur when determining the vCPU stop reason
24+
#[derive(Debug, thiserror::Error)]
25+
pub enum VcpuStopReasonError {
26+
#[error("Failed to get registers: {0}")]
27+
GetRegs(#[from] RegisterError),
28+
#[error("Failed to remove hardware breakpoint: {0}")]
29+
RemoveHwBreakpoint(#[from] DebugError),
30+
}
2231

2332
// Described in Table 6-1. Exceptions and Interrupts at Page 6-13 Vol. 1
2433
// of Intel 64 and IA-32 Architectures Software Developer's Manual
@@ -56,7 +65,7 @@ pub(crate) fn vcpu_stop_reason(
5665
dr6: u64,
5766
entrypoint: u64,
5867
exception: u32,
59-
) -> Result<VcpuStopReason> {
68+
) -> std::result::Result<VcpuStopReason, VcpuStopReasonError> {
6069
let CommonRegisters { rip, .. } = vm.regs()?;
6170
if DB_EX_ID == exception {
6271
// If the BS flag in DR6 register is set, it means a single step

src/hyperlight_host/src/hypervisor/gdb/mod.rs

Lines changed: 71 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,16 @@ use x86_64_target::HyperlightSandboxTarget;
3333

3434
use super::InterruptHandle;
3535
use super::regs::CommonRegisters;
36+
use crate::HyperlightError;
3637
use crate::hypervisor::regs::CommonFpu;
37-
use crate::hypervisor::virtual_machine::VirtualMachine;
38+
use crate::hypervisor::virtual_machine::{HypervisorError, RegisterError, VirtualMachine};
3839
use crate::mem::layout::SandboxMemoryLayout;
3940
use crate::mem::memory_region::MemoryRegion;
4041
use crate::mem::mgr::SandboxMemoryManager;
4142
use crate::mem::shared_mem::HostSharedMemory;
42-
use crate::{HyperlightError, new_error};
4343

4444
#[derive(Debug, Error)]
45-
pub(crate) enum GdbTargetError {
45+
pub enum GdbTargetError {
4646
#[error("Error encountered while binding to address and port")]
4747
CannotBind,
4848
#[error("Error encountered while listening for connections")]
@@ -86,6 +86,17 @@ pub(crate) struct DebugMemoryAccess {
8686
pub(crate) guest_mmap_regions: Vec<MemoryRegion>,
8787
}
8888

89+
/// Errors that can occur during debug memory access operations
90+
#[derive(Debug, thiserror::Error)]
91+
pub enum DebugMemoryAccessError {
92+
#[error("Failed to copy memory: {0}")]
93+
CopyFailed(Box<HyperlightError>),
94+
#[error("Failed to acquire lock at {0}:{1} - {2}")]
95+
LockFailed(&'static str, u32, String),
96+
#[error("Failed to translate guest address {0:#x}")]
97+
TranslateGuestAddress(u64),
98+
}
99+
89100
impl DebugMemoryAccess {
90101
/// Reads memory from the guest's address space with a maximum length of a PAGE_SIZE
91102
///
@@ -94,8 +105,12 @@ impl DebugMemoryAccess {
94105
/// * `gpa` - Guest physical address to read from.
95106
/// This address is shall be translated before calling this function
96107
/// # Returns
97-
/// * `Result<(), HyperlightError>` - Ok if successful, Err otherwise
98-
pub(crate) fn read(&self, data: &mut [u8], gpa: u64) -> crate::Result<()> {
108+
/// * `Result<(), DebugMemoryAccessError>` - Ok if successful, Err otherwise
109+
pub(crate) fn read(
110+
&self,
111+
data: &mut [u8],
112+
gpa: u64,
113+
) -> std::result::Result<(), DebugMemoryAccessError> {
99114
let read_len = data.len();
100115

101116
let mem_offset = (gpa as usize)
@@ -107,7 +122,7 @@ impl DebugMemoryAccess {
107122
gpa,
108123
SandboxMemoryLayout::BASE_ADDRESS
109124
);
110-
HyperlightError::TranslateGuestAddress(gpa)
125+
DebugMemoryAccessError::TranslateGuestAddress(gpa)
111126
})?;
112127

113128
// First check the mapped memory regions to see if the address is within any of them
@@ -123,7 +138,7 @@ impl DebugMemoryAccess {
123138
mem_offset,
124139
reg.guest_region.start,
125140
);
126-
HyperlightError::TranslateGuestAddress(mem_offset as u64)
141+
DebugMemoryAccessError::TranslateGuestAddress(mem_offset as u64)
127142
})?;
128143

129144
let bytes: &[u8] = unsafe {
@@ -144,9 +159,10 @@ impl DebugMemoryAccess {
144159

145160
self.dbg_mem_access_fn
146161
.try_lock()
147-
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
162+
.map_err(|e| DebugMemoryAccessError::LockFailed(file!(), line!(), e.to_string()))?
148163
.get_shared_mem_mut()
149-
.copy_to_slice(&mut data[..read_len], mem_offset)?;
164+
.copy_to_slice(&mut data[..read_len], mem_offset)
165+
.map_err(|e| DebugMemoryAccessError::CopyFailed(Box::new(e)))?;
150166
}
151167

152168
Ok(())
@@ -159,8 +175,12 @@ impl DebugMemoryAccess {
159175
/// * `gpa` - Guest physical address to write to.
160176
/// This address is shall be translated before calling this function
161177
/// # Returns
162-
/// * `Result<(), HyperlightError>` - Ok if successful, Err otherwise
163-
pub(crate) fn write(&self, data: &[u8], gpa: u64) -> crate::Result<()> {
178+
/// * `Result<(), DebugMemoryAccessError>` - Ok if successful, Err otherwise
179+
pub(crate) fn write(
180+
&self,
181+
data: &[u8],
182+
gpa: u64,
183+
) -> std::result::Result<(), DebugMemoryAccessError> {
164184
let write_len = data.len();
165185

166186
let mem_offset = (gpa as usize)
@@ -172,7 +192,7 @@ impl DebugMemoryAccess {
172192
gpa,
173193
SandboxMemoryLayout::BASE_ADDRESS
174194
);
175-
HyperlightError::TranslateGuestAddress(gpa)
195+
DebugMemoryAccessError::TranslateGuestAddress(gpa)
176196
})?;
177197

178198
// First check the mapped memory regions to see if the address is within any of them
@@ -188,7 +208,7 @@ impl DebugMemoryAccess {
188208
mem_offset,
189209
reg.guest_region.start,
190210
);
191-
HyperlightError::TranslateGuestAddress(mem_offset as u64)
211+
DebugMemoryAccessError::TranslateGuestAddress(mem_offset as u64)
192212
})?;
193213

194214
let bytes: &mut [u8] = unsafe {
@@ -213,9 +233,10 @@ impl DebugMemoryAccess {
213233

214234
self.dbg_mem_access_fn
215235
.try_lock()
216-
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
236+
.map_err(|e| DebugMemoryAccessError::LockFailed(file!(), line!(), e.to_string()))?
217237
.get_shared_mem_mut()
218-
.copy_from_slice(&data[..write_len], mem_offset)?;
238+
.copy_from_slice(&data[..write_len], mem_offset)
239+
.map_err(|e| DebugMemoryAccessError::CopyFailed(Box::new(e)))?;
219240
}
220241

221242
Ok(())
@@ -275,24 +296,42 @@ pub(crate) enum DebugResponse {
275296
WriteRegisters,
276297
}
277298

299+
/// Errors that can occur during debug operations
300+
#[derive(Debug, Clone, thiserror::Error)]
301+
pub enum DebugError {
302+
#[error("Hardware breakpoint not found at address {0:#x}")]
303+
HwBreakpointNotFound(u64),
304+
#[error("Failed to enable/disable intercept: {enable}, {inner}")]
305+
Intercept {
306+
enable: bool,
307+
inner: HypervisorError,
308+
},
309+
#[error("Register operation failed: {0}")]
310+
Register(#[from] RegisterError),
311+
#[error("Maximum hardware breakpoints ({0}) exceeded")]
312+
TooManyHwBreakpoints(usize),
313+
#[error("Translation of guest virtual address failed: {0}")]
314+
TranslateGva(u64),
315+
}
316+
278317
/// Trait for VMs that support debugging capabilities.
279318
/// This extends the base VirtualMachine trait with GDB-specific functionality.
280319
pub(crate) trait DebuggableVm: VirtualMachine {
281320
/// Translates a guest virtual address to a guest physical address
282-
fn translate_gva(&self, gva: u64) -> crate::Result<u64>;
321+
fn translate_gva(&self, gva: u64) -> std::result::Result<u64, DebugError>;
283322

284323
/// Enable/disable debugging
285-
fn set_debug(&mut self, enable: bool) -> crate::Result<()>;
324+
fn set_debug(&mut self, enable: bool) -> std::result::Result<(), DebugError>;
286325

287326
/// Enable/disable single stepping
288-
fn set_single_step(&mut self, enable: bool) -> crate::Result<()>;
327+
fn set_single_step(&mut self, enable: bool) -> std::result::Result<(), DebugError>;
289328

290329
/// Add a hardware breakpoint at the given address.
291330
/// Must be idempotent.
292-
fn add_hw_breakpoint(&mut self, addr: u64) -> crate::Result<()>;
331+
fn add_hw_breakpoint(&mut self, addr: u64) -> std::result::Result<(), DebugError>;
293332

294333
/// Remove a hardware breakpoint at the given address
295-
fn remove_hw_breakpoint(&mut self, addr: u64) -> crate::Result<()>;
334+
fn remove_hw_breakpoint(&mut self, addr: u64) -> std::result::Result<(), DebugError>;
296335
}
297336

298337
/// Debug communication channel that is used for sending a request type and
@@ -513,7 +552,9 @@ mod tests {
513552
}
514553

515554
let mut read_data = [0u8; 1];
516-
mem_access.read(&mut read_data, (BASE_VIRT + offset) as u64)?;
555+
mem_access
556+
.read(&mut read_data, (BASE_VIRT + offset) as u64)
557+
.unwrap();
517558

518559
assert_eq!(read_data[0], 0xAA);
519560

@@ -536,7 +577,9 @@ mod tests {
536577
}
537578

538579
let mut read_data = [0u8; 16];
539-
mem_access.read(&mut read_data, (BASE_VIRT + offset) as u64)?;
580+
mem_access
581+
.read(&mut read_data, (BASE_VIRT + offset) as u64)
582+
.unwrap();
540583

541584
assert_eq!(
542585
read_data,
@@ -556,7 +599,9 @@ mod tests {
556599
}
557600

558601
let write_data = [0xCCu8; 1];
559-
mem_access.write(&write_data, (BASE_VIRT + offset) as u64)?;
602+
mem_access
603+
.write(&write_data, (BASE_VIRT + offset) as u64)
604+
.unwrap();
560605

561606
let slice = unsafe { get_mmap_slice(&mut mem_access) };
562607
assert_eq!(slice[offset], write_data[0]);
@@ -577,7 +622,9 @@ mod tests {
577622
}
578623

579624
let write_data = [0xAAu8; 16];
580-
mem_access.write(&write_data, (BASE_VIRT + offset) as u64)?;
625+
mem_access
626+
.write(&write_data, (BASE_VIRT + offset) as u64)
627+
.unwrap();
581628

582629
let slice = unsafe { get_mmap_slice(&mut mem_access) };
583630
assert_eq!(slice[offset..offset + 16], write_data);

0 commit comments

Comments
 (0)