From 4f84c5619c5a71523e0578797828b2fd3a70a498 Mon Sep 17 00:00:00 2001 From: Shailesh Vashishth Date: Mon, 6 Oct 2025 13:59:32 +0530 Subject: [PATCH] Closes #274: Adding FFI functions for bool, float, double, string, vecbytes - Add Tests for CAPI Flatuffers - Separate function for creating Uninitialized Sandbox for C guests - Ignore Test cases for float and double until Github issue #179 is fixed. Signed-off-by: Shailesh Vashishth --- src/hyperlight_guest_capi/src/flatbuffer.rs | 42 ++++++++- src/hyperlight_host/tests/common/mod.rs | 8 ++ src/hyperlight_host/tests/integration_test.rs | 94 ++++++++++++++++++- src/tests/c_guests/c_simpleguest/main.c | 80 +++++++++++++++- 4 files changed, 221 insertions(+), 3 deletions(-) diff --git a/src/hyperlight_guest_capi/src/flatbuffer.rs b/src/hyperlight_guest_capi/src/flatbuffer.rs index b710d4245..ff12400d6 100644 --- a/src/hyperlight_guest_capi/src/flatbuffer.rs +++ b/src/hyperlight_guest_capi/src/flatbuffer.rs @@ -15,6 +15,9 @@ limitations under the License. */ use alloc::boxed::Box; +use alloc::ffi::CString; +use alloc::string::String; +use alloc::vec::Vec; use core::ffi::{CStr, c_char}; use hyperlight_common::flatbuffer_wrappers::util::get_flatbuffer_result; @@ -92,6 +95,13 @@ pub extern "C" fn hl_flatbuffer_result_from_Bytes(data: *const u8, len: usize) - Box::new(unsafe { FfiVec::from_vec(vec) }) } +#[unsafe(no_mangle)] +pub extern "C" fn hl_flatbuffer_result_from_Bool(value: bool) -> Box { + let vec = get_flatbuffer_result(value); + + Box::new(unsafe { FfiVec::from_vec(vec) }) +} + //--- Functions for getting values returned by host functions calls #[unsafe(no_mangle)] @@ -115,4 +125,34 @@ pub extern "C" fn hl_get_host_return_value_as_ULong() -> u64 { get_host_return_value().expect("Unable to get host return value as ulong") } -// TODO add bool, float, double, string, vecbytes +#[unsafe(no_mangle)] +pub extern "C" fn hl_get_host_return_value_as_Bool() -> bool { + get_host_return_value().expect("Unable to get host return value as bool") +} + +#[unsafe(no_mangle)] +pub extern "C" fn hl_get_host_return_value_as_Float() -> f32 { + get_host_return_value().expect("Unable to get host return value as f32") +} + +#[unsafe(no_mangle)] +pub extern "C" fn hl_get_host_return_value_as_Double() -> f64 { + get_host_return_value().expect("Unable to get host return value as f64") +} + +#[unsafe(no_mangle)] +pub extern "C" fn hl_get_host_return_value_as_String() -> *const c_char { + let string_value: String = + get_host_return_value().expect("Unable to get host return value as string"); + + let c_string = CString::new(string_value).expect("Failed to create CString"); + c_string.into_raw() +} + +#[unsafe(no_mangle)] +pub extern "C" fn hl_get_host_return_value_as_VecBytes() -> Box { + let vec_value: Vec = + get_host_return_value().expect("Unable to get host return value as vec bytes"); + + Box::new(unsafe { FfiVec::from_vec(vec_value) }) +} diff --git a/src/hyperlight_host/tests/common/mod.rs b/src/hyperlight_host/tests/common/mod.rs index ab1a97b71..41016d1be 100644 --- a/src/hyperlight_host/tests/common/mod.rs +++ b/src/hyperlight_host/tests/common/mod.rs @@ -35,6 +35,14 @@ pub fn new_uninit_rust() -> Result { ) } +/// Returns a c-language simpleguest. +pub fn new_uninit_c() -> Result { + UninitializedSandbox::new( + GuestBinary::FilePath(c_simple_guest_as_string().unwrap()), + None, + ) +} + pub fn get_simpleguest_sandboxes( writer: Option>, // An optional writer to make sure correct info is passed to the host printer ) -> Vec { diff --git a/src/hyperlight_host/tests/integration_test.rs b/src/hyperlight_host/tests/integration_test.rs index be3ead190..0869a9d5a 100644 --- a/src/hyperlight_host/tests/integration_test.rs +++ b/src/hyperlight_host/tests/integration_test.rs @@ -28,7 +28,7 @@ use hyperlight_testing::{c_simple_guest_as_string, simple_guest_as_string}; use log::LevelFilter; pub mod common; // pub to disable dead_code warning -use crate::common::{new_uninit, new_uninit_rust}; +use crate::common::{new_uninit, new_uninit_c, new_uninit_rust}; // A host function cannot be interrupted, but we can at least make sure after requesting to interrupt a host call, // we don't re-enter the guest again once the host call is done @@ -765,3 +765,95 @@ fn log_test_messages(levelfilter: Option) { .unwrap(); } } + +/// Tests whether host is able to return Bool as return type +/// or not +#[test] +fn test_if_guest_is_able_to_get_bool_return_values_from_host() { + let mut sbox1 = new_uninit_c().unwrap(); + + sbox1 + .register("HostBool", |a: i32, b: i32| a + b > 10) + .unwrap(); + let mut sbox3 = sbox1.evolve().unwrap(); + + for i in 1..10 { + if i < 6 { + let res = sbox3 + .call::("GuestRetrievesBoolValue", (i, i)) + .unwrap(); + println!("{:?}", res); + assert!(!res); + } else { + let res = sbox3 + .call::("GuestRetrievesBoolValue", (i, i)) + .unwrap(); + println!("{:?}", res); + assert!(res); + } + } +} + +/// Tests whether host is able to return Float/f32 as return type +/// or not +/// Adding Ignore attribute, due known issues with float and double +/// calculations - see Github issue #179. Once it is fixed we can +/// remove ignore attribute +#[ignore] +#[test] +fn test_if_guest_is_able_to_get_float_return_values_from_host() { + let mut sbox1 = new_uninit_c().unwrap(); + + sbox1 + .register("HostAddFloat", |a: f32, b: f32| a + b) + .unwrap(); + let mut sbox3 = sbox1.evolve().unwrap(); + let res = sbox3 + .call::("GuestRetrievesFloatValue", (1.34_f32, 1.34_f32)) + .unwrap(); + println!("{:?}", res); + assert_eq!(res, 2.68_f32); +} + +/// Tests whether host is able to return Double/f64 as return type +/// or not +/// Adding Ignore attribute, due known issues with float and double +/// calculations - see Github issue #179. Once it is fixed we can +/// remove ignore attribute +#[ignore] +#[test] +fn test_if_guest_is_able_to_get_double_return_values_from_host() { + let mut sbox1 = new_uninit_c().unwrap(); + + sbox1 + .register("HostAddDouble", |a: f64, b: f64| a + b) + .unwrap(); + let mut sbox3 = sbox1.evolve().unwrap(); + let res = sbox3 + .call::("GuestRetrievesDoubleValue", (1.34_f64, 1.34_f64)) + .unwrap(); + println!("{:?}", res); + assert_eq!(res, 2.68_f64); +} + +/// Tests whether host is able to return String as return type +/// or not +#[test] +fn test_if_guest_is_able_to_get_string_return_values_from_host() { + let mut sbox1 = new_uninit_c().unwrap(); + + sbox1 + .register("HostAddStrings", |a: String| { + a + ", string added by Host Function" + }) + .unwrap(); + let mut sbox3 = sbox1.evolve().unwrap(); + let res = sbox3 + .call::("GuestRetrievesStringValue", ()) + .unwrap(); + println!("{:?}", res); + assert_eq!( + res, + "Guest Function, string added by Host Function".to_string() + ); +} diff --git a/src/tests/c_guests/c_simpleguest/main.c b/src/tests/c_guests/c_simpleguest/main.c index 2822b108d..30caff590 100644 --- a/src/tests/c_guests/c_simpleguest/main.c +++ b/src/tests/c_guests/c_simpleguest/main.c @@ -256,6 +256,80 @@ int guest_function(const char *from_host) { return 0; } +bool guest_fn_checks_if_host_returns_bool_value(int32_t a, int32_t b) { + hl_Parameter params[2]; + + params[0].tag = hl_ParameterType_Int; + params[0].value.Int = a; + + params[1].tag = hl_ParameterType_Int; + params[1].value.Int = b; + + const hl_FunctionCall host_call = {.function_name = "HostBool", + .parameters = params, + .parameters_len = 2, + .return_type = hl_ReturnType_Bool + }; + hl_call_host_function(&host_call); + return hl_get_host_return_value_as_Bool(); +} + +float guest_fn_checks_if_host_returns_float_value(float a, float b) { + hl_Parameter params[2]; + + params[0].tag = hl_ParameterType_Float; + params[0].value.Float = a; + + params[1].tag = hl_ParameterType_Float; + params[1].value.Float = b; + + const hl_FunctionCall host_call = {.function_name = "HostAddFloat", + .parameters = params, + .parameters_len = 2, + .return_type = hl_ReturnType_Float + }; + hl_call_host_function(&host_call); + return hl_get_host_return_value_as_Float(); +} + +double guest_fn_checks_if_host_returns_double_value(double a, double b) { + hl_Parameter params[2]; + + params[0].tag = hl_ParameterType_Double; + params[0].value.Double = a; + + params[1].tag = hl_ParameterType_Double; + params[1].value.Double = b; + + const hl_FunctionCall host_call = {.function_name = "HostAddDouble", + .parameters = params, + .parameters_len = 2, + .return_type = hl_ReturnType_Double + }; + hl_call_host_function(&host_call); + return hl_get_host_return_value_as_Double(); +} + +const char* guest_fn_checks_if_host_returns_string_value() { + char guest_message[256] = "Guest Function"; + hl_Parameter params; + + params.tag = hl_ParameterType_String; + params.value.String = guest_message; + + const hl_FunctionCall host_call = {.function_name = "HostAddStrings", + .parameters = ¶ms, + .parameters_len = 1, + .return_type = hl_ReturnType_String + }; + hl_call_host_function(&host_call); + return hl_get_host_return_value_as_String(); +} + +HYPERLIGHT_WRAP_FUNCTION(guest_fn_checks_if_host_returns_float_value, Float, 2, Float, Float) +HYPERLIGHT_WRAP_FUNCTION(guest_fn_checks_if_host_returns_double_value, Double, 2, Double, Double) +HYPERLIGHT_WRAP_FUNCTION(guest_fn_checks_if_host_returns_string_value, String, 0) +HYPERLIGHT_WRAP_FUNCTION(guest_fn_checks_if_host_returns_bool_value, Bool, 2, Int, Int) HYPERLIGHT_WRAP_FUNCTION(echo, String, 1, String) // HYPERLIGHT_WRAP_FUNCTION(set_byte_array_to_zero, 1, VecBytes) is not valid for functions that return VecBytes HYPERLIGHT_WRAP_FUNCTION(guest_function, Int, 1, String) @@ -289,6 +363,10 @@ HYPERLIGHT_WRAP_FUNCTION(log_message, Int, 2, String, Long) void hyperlight_main(void) { + HYPERLIGHT_REGISTER_FUNCTION("GuestRetrievesFloatValue", guest_fn_checks_if_host_returns_float_value); + HYPERLIGHT_REGISTER_FUNCTION("GuestRetrievesDoubleValue", guest_fn_checks_if_host_returns_double_value); + HYPERLIGHT_REGISTER_FUNCTION("GuestRetrievesStringValue", guest_fn_checks_if_host_returns_string_value); + HYPERLIGHT_REGISTER_FUNCTION("GuestRetrievesBoolValue", guest_fn_checks_if_host_returns_bool_value); HYPERLIGHT_REGISTER_FUNCTION("Echo", echo); // HYPERLIGHT_REGISTER_FUNCTION macro does not work for functions that return VecBytes, // so we use hl_register_function_definition directly @@ -338,4 +416,4 @@ hl_Vec *c_guest_dispatch_function(const hl_FunctionCall *function_call) { } return NULL; -} +} \ No newline at end of file