Skip to content

Commit dc6820b

Browse files
committed
Update host-side error handling
- Update initialized_multi_use.rs to use new Result-like error handling - Update mem/mgr.rs to handle host function errors properly - Update sandbox/outb.rs for new error propagation pattern Signed-off-by: Ludvig Liljenberg <[email protected]>
1 parent d7172ba commit dc6820b

File tree

5 files changed

+93
-43
lines changed

5 files changed

+93
-43
lines changed

src/hyperlight_host/src/mem/mgr.rs

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ limitations under the License.
1616

1717
use std::cmp::Ordering;
1818

19+
use flatbuffers::FlatBufferBuilder;
1920
use hyperlight_common::flatbuffer_wrappers::function_call::{
2021
FunctionCall, validate_guest_function_call_buffer,
2122
};
22-
use hyperlight_common::flatbuffer_wrappers::function_types::ReturnValue;
23-
use hyperlight_common::flatbuffer_wrappers::guest_error::GuestError;
23+
use hyperlight_common::flatbuffer_wrappers::function_types::FunctionCallResult;
2424
use hyperlight_common::flatbuffer_wrappers::guest_log_data::GuestLogData;
2525
use hyperlight_common::flatbuffer_wrappers::host_function_details::HostFunctionDetails;
2626
use tracing::{Span, instrument};
@@ -501,18 +501,19 @@ impl SandboxMemoryManager<HostSharedMemory> {
501501
)
502502
}
503503

504-
/// Writes a function call result to memory
504+
/// Writes a host function call result to memory
505505
#[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
506-
pub(crate) fn write_response_from_host_method_call(&mut self, res: &ReturnValue) -> Result<()> {
507-
let function_call_ret_val_buffer = Vec::<u8>::try_from(res).map_err(|_| {
508-
new_error!(
509-
"write_response_from_host_method_call: failed to convert ReturnValue to Vec<u8>"
510-
)
511-
})?;
506+
pub(crate) fn write_response_from_host_function_call(
507+
&mut self,
508+
res: &FunctionCallResult,
509+
) -> Result<()> {
510+
let mut builder = FlatBufferBuilder::new();
511+
let data = res.encode(&mut builder);
512+
512513
self.shared_mem.push_buffer(
513514
self.layout.input_data_buffer_offset,
514515
self.layout.sandbox_memory_config.get_input_data_size(),
515-
function_call_ret_val_buffer.as_slice(),
516+
data,
516517
)
517518
}
518519

@@ -533,10 +534,11 @@ impl SandboxMemoryManager<HostSharedMemory> {
533534
)
534535
}
535536

536-
/// Reads a function call result from memory
537+
/// Reads a function call result from memory.
538+
/// A function call result can be either an error or a successful return value.
537539
#[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")]
538-
pub(crate) fn get_guest_function_call_result(&mut self) -> Result<ReturnValue> {
539-
self.shared_mem.try_pop_buffer_into::<ReturnValue>(
540+
pub(crate) fn get_guest_function_call_result(&mut self) -> Result<FunctionCallResult> {
541+
self.shared_mem.try_pop_buffer_into::<FunctionCallResult>(
540542
self.layout.output_data_buffer_offset,
541543
self.layout.sandbox_memory_config.get_output_data_size(),
542544
)
@@ -551,14 +553,6 @@ impl SandboxMemoryManager<HostSharedMemory> {
551553
)
552554
}
553555

554-
/// Get the guest error data
555-
pub(crate) fn get_guest_error(&mut self) -> Result<GuestError> {
556-
self.shared_mem.try_pop_buffer_into::<GuestError>(
557-
self.layout.output_data_buffer_offset,
558-
self.layout.sandbox_memory_config.get_output_data_size(),
559-
)
560-
}
561-
562556
pub(crate) fn clear_io_buffers(&mut self) {
563557
// Clear the output data buffer
564558
loop {

src/hyperlight_host/src/sandbox/initialized_multi_use.rs

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@ use hyperlight_common::flatbuffer_wrappers::function_call::{FunctionCall, Functi
2828
use hyperlight_common::flatbuffer_wrappers::function_types::{
2929
ParameterValue, ReturnType, ReturnValue,
3030
};
31+
use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
3132
use hyperlight_common::flatbuffer_wrappers::util::estimate_flatbuffer_capacity;
3233
use tracing::{Span, instrument};
3334

3435
use super::Callable;
3536
use super::host_funcs::FunctionRegistry;
3637
use super::snapshot::Snapshot;
37-
use crate::HyperlightError::SnapshotSandboxMismatch;
38-
use crate::func::guest_err::check_for_guest_error;
38+
use crate::HyperlightError::{self, SnapshotSandboxMismatch};
3939
use crate::func::{ParameterTuple, SupportedReturnType};
4040
use crate::hypervisor::{Hypervisor, InterruptHandle};
4141
#[cfg(unix)]
@@ -44,7 +44,9 @@ use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
4444
use crate::mem::mgr::SandboxMemoryManager;
4545
use crate::mem::ptr::RawPtr;
4646
use crate::mem::shared_mem::HostSharedMemory;
47-
use crate::metrics::maybe_time_and_emit_guest_call;
47+
use crate::metrics::{
48+
METRIC_GUEST_ERROR, METRIC_GUEST_ERROR_LABEL_CODE, maybe_time_and_emit_guest_call,
49+
};
4850
use crate::{Result, log_then_return};
4951

5052
/// Global counter for assigning unique IDs to sandboxes
@@ -410,9 +412,24 @@ impl MultiUseSandbox {
410412
)?;
411413

412414
self.mem_mgr.check_stack_guard()?;
413-
check_for_guest_error(&mut self.mem_mgr)?;
414415

415-
self.mem_mgr.get_guest_function_call_result()
416+
let guest_result = self.mem_mgr.get_guest_function_call_result()?.into_inner();
417+
418+
match guest_result {
419+
Ok(val) => Ok(val),
420+
Err(guest_error) => {
421+
metrics::counter!(
422+
METRIC_GUEST_ERROR,
423+
METRIC_GUEST_ERROR_LABEL_CODE => (guest_error.code as u64).to_string()
424+
)
425+
.increment(1);
426+
427+
Err(match guest_error.code {
428+
ErrorCode::StackOverflow => HyperlightError::StackOverflow(),
429+
_ => HyperlightError::GuestError(guest_error.code, guest_error.message),
430+
})
431+
}
432+
}
416433
})();
417434

418435
// In the happy path we do not need to clear io-buffers from the host because:
@@ -493,6 +510,36 @@ mod tests {
493510
use crate::sandbox::SandboxConfiguration;
494511
use crate::{GuestBinary, HyperlightError, MultiUseSandbox, Result, UninitializedSandbox};
495512

513+
/// Make sure input/output buffers are properly reset after guest call (with host call)
514+
#[test]
515+
#[ignore = "added this test before fixing bug"]
516+
fn host_func_error() {
517+
let path = simple_guest_as_string().unwrap();
518+
let mut sandbox = UninitializedSandbox::new(GuestBinary::FilePath(path), None).unwrap();
519+
sandbox
520+
.register("HostError", || -> Result<()> {
521+
Err(HyperlightError::Error("hi".to_string()))
522+
})
523+
.unwrap();
524+
let mut sandbox = sandbox.evolve().unwrap();
525+
526+
// will exhaust io if leaky
527+
for _ in 0..1000 {
528+
let result = sandbox
529+
.call::<i64>(
530+
"CallGivenParamlessHostFuncThatReturnsI64",
531+
"HostError".to_string(),
532+
)
533+
.unwrap_err();
534+
535+
assert!(
536+
matches!(result, HyperlightError::Error(ref msg) if msg == "hi"),
537+
"Expected HyperlightError::Error('hi'), got {:?}",
538+
result
539+
);
540+
}
541+
}
542+
496543
/// Make sure input/output buffers are properly reset after guest call (with host call)
497544
#[test]
498545
fn io_buffer_reset() {
@@ -606,6 +653,9 @@ mod tests {
606653
#[ignore]
607654
#[cfg(target_os = "linux")]
608655
fn test_violate_seccomp_filters() -> Result<()> {
656+
#[cfg(feature = "seccomp")]
657+
use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
658+
609659
fn make_get_pid_syscall() -> Result<u64> {
610660
let pid = unsafe { libc::syscall(libc::SYS_getpid) };
611661
Ok(pid as u64)
@@ -629,7 +679,9 @@ mod tests {
629679
match res {
630680
Ok(_) => panic!("Expected to fail due to seccomp violation"),
631681
Err(e) => match e {
632-
HyperlightError::DisallowedSyscall => {}
682+
HyperlightError::GuestError(t, msg)
683+
if t == ErrorCode::HostFunctionError
684+
&& msg.contains("Seccomp filter trapped on disallowed syscall") => {}
633685
_ => panic!("Expected DisallowedSyscall error: {}", e),
634686
},
635687
}

src/hyperlight_host/src/sandbox/outb.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ use std::sync::{Arc, Mutex};
2222
use fallible_iterator::FallibleIterator;
2323
#[cfg(feature = "unwind_guest")]
2424
use framehop::Unwinder;
25-
use hyperlight_common::flatbuffer_wrappers::function_types::ParameterValue;
26-
use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
25+
use hyperlight_common::flatbuffer_wrappers::function_types::{FunctionCallResult, ParameterValue};
26+
use hyperlight_common::flatbuffer_wrappers::guest_error::{ErrorCode, GuestError};
2727
use hyperlight_common::flatbuffer_wrappers::guest_log_data::GuestLogData;
2828
use hyperlight_common::outb::{Exception, OutBAction};
2929
#[cfg(feature = "trace_guest")]
@@ -283,8 +283,12 @@ pub(crate) fn handle_outb(
283283
let res = host_funcs
284284
.try_lock()
285285
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
286-
.call_host_function(&name, args)?;
287-
mem_mgr.write_response_from_host_method_call(&res)?; // push input buffers
286+
.call_host_function(&name, args)
287+
.map_err(|e| GuestError::new(ErrorCode::HostFunctionError, e.to_string()));
288+
289+
let func_result = FunctionCallResult::new(res);
290+
291+
mem_mgr.write_response_from_host_function_call(&func_result)?;
288292

289293
Ok(())
290294
}

src/tests/rust_guests/dummyguest/Cargo.lock

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/tests/rust_guests/witguest/Cargo.lock

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)