Skip to content

Commit 5cd594e

Browse files
authored
Improve the ergonomics of registering host functions (#468)
Signed-off-by: Jorge Prendes <[email protected]>
1 parent fd5f9c8 commit 5cd594e

File tree

8 files changed

+109
-86
lines changed

8 files changed

+109
-86
lines changed

README.md

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,11 @@ fn main() -> hyperlight_host::Result<()> {
4747
None, // default host print function
4848
)?;
4949

50-
// Register a host function
51-
fn sleep_5_secs() -> hyperlight_host::Result<()> {
50+
// Registering a host function makes it available to be called by the guest
51+
uninitialized_sandbox.register("Sleep5Secs", || {
5252
thread::sleep(std::time::Duration::from_secs(5));
5353
Ok(())
54-
}
55-
56-
let host_function = Arc::new(Mutex::new(sleep_5_secs));
57-
58-
// Registering a host function makes it available to be called by the guest
59-
host_function.register(&mut uninitialized_sandbox, "Sleep5Secs")?;
54+
})?;
6055
// Note: This function is unused by the guest code below, it's just here for demonstration purposes
6156

6257
// Initialize sandbox to be able to call host functions

src/hyperlight_host/benches/benchmarks.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,10 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
use std::sync::{Arc, Mutex};
1817
use std::time::Duration;
1918

2019
use criterion::{criterion_group, criterion_main, Criterion};
2120
use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterValue, ReturnType};
22-
use hyperlight_host::func::HostFunction;
2321
use hyperlight_host::sandbox::{MultiUseSandbox, SandboxConfiguration, UninitializedSandbox};
2422
use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox;
2523
use hyperlight_host::sandbox_state::transition::Noop;
@@ -110,12 +108,8 @@ fn guest_call_benchmark(c: &mut Criterion) {
110108
let mut uninitialized_sandbox = create_uninit_sandbox();
111109

112110
// Define a host function that adds two integers and register it.
113-
fn add(a: i32, b: i32) -> hyperlight_host::Result<i32> {
114-
Ok(a + b)
115-
}
116-
let host_function = Arc::new(Mutex::new(add));
117-
host_function
118-
.register(&mut uninitialized_sandbox, "HostAdd")
111+
uninitialized_sandbox
112+
.register("HostAdd", |a: i32, b: i32| Ok(a + b))
119113
.unwrap();
120114

121115
let multiuse_sandbox: MultiUseSandbox =

src/hyperlight_host/examples/guest-debugging/main.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,9 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
use std::sync::{Arc, Mutex};
1817
use std::thread;
1918

2019
use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterValue, ReturnType};
21-
use hyperlight_host::func::HostFunction;
2220
#[cfg(gdb)]
2321
use hyperlight_host::sandbox::config::DebugInfo;
2422
use hyperlight_host::sandbox::SandboxConfiguration;
@@ -55,14 +53,10 @@ fn main() -> hyperlight_host::Result<()> {
5553
)?;
5654

5755
// Register a host functions
58-
fn sleep_5_secs() -> hyperlight_host::Result<()> {
56+
uninitialized_sandbox.register("Sleep5Secs", || {
5957
thread::sleep(std::time::Duration::from_secs(5));
6058
Ok(())
61-
}
62-
63-
let host_function = Arc::new(Mutex::new(sleep_5_secs));
64-
65-
host_function.register(&mut uninitialized_sandbox, "Sleep5Secs")?;
59+
})?;
6660
// Note: This function is unused, it's just here for demonstration purposes
6761

6862
// Initialize sandbox to be able to call host functions

src/hyperlight_host/examples/hello-world/main.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,9 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
use std::sync::{Arc, Mutex};
1817
use std::thread;
1918

2019
use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterValue, ReturnType};
21-
use hyperlight_host::func::HostFunction;
2220
use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox;
2321
use hyperlight_host::sandbox_state::transition::Noop;
2422
use hyperlight_host::{MultiUseSandbox, UninitializedSandbox};
@@ -35,14 +33,10 @@ fn main() -> hyperlight_host::Result<()> {
3533
)?;
3634

3735
// Register a host functions
38-
fn sleep_5_secs() -> hyperlight_host::Result<()> {
36+
uninitialized_sandbox.register("Sleep5Secs", || {
3937
thread::sleep(std::time::Duration::from_secs(5));
4038
Ok(())
41-
}
42-
43-
let host_function = Arc::new(Mutex::new(sleep_5_secs));
44-
45-
host_function.register(&mut uninitialized_sandbox, "Sleep5Secs")?;
39+
})?;
4640
// Note: This function is unused, it's just here for demonstration purposes
4741

4842
// Initialize sandbox to be able to call host functions

src/hyperlight_host/src/func/guest_dispatch.rs

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ mod tests {
112112

113113
use super::*;
114114
use crate::func::call_ctx::MultiUseGuestCallContext;
115-
use crate::func::host_functions::HostFunction;
116115
use crate::sandbox::is_hypervisor_present;
117116
use crate::sandbox::uninitialized::GuestBinary;
118117
use crate::sandbox_state::sandbox::EvolvableSandbox;
@@ -152,8 +151,6 @@ mod tests {
152151

153152
// First, run to make sure it fails.
154153
{
155-
let make_get_pid_syscall_func = Arc::new(Mutex::new(make_get_pid_syscall));
156-
157154
let mut usbox = UninitializedSandbox::new(
158155
GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
159156
None,
@@ -162,7 +159,7 @@ mod tests {
162159
)
163160
.unwrap();
164161

165-
make_get_pid_syscall_func.register(&mut usbox, "MakeGetpidSyscall")?;
162+
usbox.register("MakeGetpidSyscall", make_get_pid_syscall)?;
166163

167164
let mut sbox: MultiUseSandbox = usbox.evolve(Noop::default())?;
168165

@@ -188,8 +185,6 @@ mod tests {
188185
// Second, run with allowing `SYS_getpid`
189186
#[cfg(feature = "seccomp")]
190187
{
191-
let make_get_pid_syscall_func = Arc::new(Mutex::new(make_get_pid_syscall));
192-
193188
let mut usbox = UninitializedSandbox::new(
194189
GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
195190
None,
@@ -198,9 +193,9 @@ mod tests {
198193
)
199194
.unwrap();
200195

201-
make_get_pid_syscall_func.register_with_extra_allowed_syscalls(
202-
&mut usbox,
196+
usbox.register_with_extra_allowed_syscalls(
203197
"MakeGetpidSyscall",
198+
make_get_pid_syscall,
204199
vec![libc::SYS_getpid],
205200
)?;
206201
// ^^^ note, we are allowing SYS_getpid
@@ -453,18 +448,12 @@ mod tests {
453448
Ok(())
454449
}
455450

456-
let host_spin_func = Arc::new(Mutex::new(spin));
457-
458451
#[cfg(any(target_os = "windows", not(feature = "seccomp")))]
459-
host_spin_func.register(&mut usbox, "Spin").unwrap();
452+
usbox.register("Spin", spin).unwrap();
460453

461454
#[cfg(all(target_os = "linux", feature = "seccomp"))]
462-
host_spin_func
463-
.register_with_extra_allowed_syscalls(
464-
&mut usbox,
465-
"Spin",
466-
vec![libc::SYS_clock_nanosleep],
467-
)
455+
usbox
456+
.register_with_extra_allowed_syscalls("Spin", spin, vec![libc::SYS_clock_nanosleep])
468457
.unwrap();
469458

470459
let sandbox: MultiUseSandbox = usbox.evolve(Noop::default()).unwrap();

src/hyperlight_host/src/func/host_functions.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,15 @@ pub trait HostFunction<R, Args> {
6868
) -> Result<()>;
6969
}
7070

71+
/// Tait for types that can be converted into types implementing `HostFunction`.
72+
pub trait IntoHostFunction<R, Args> {
73+
/// Concrete type of the returned host function
74+
type Output: HostFunction<R, Args>;
75+
76+
/// Convert the type into a host function
77+
fn into_host_function(self) -> Self::Output;
78+
}
79+
7180
macro_rules! impl_host_function {
7281
(@count) => { 0 };
7382
(@count $P:ident $(, $R:ident)*) => {
@@ -109,6 +118,42 @@ macro_rules! impl_host_function {
109118
}
110119
}
111120

121+
impl<R $(, $P)*, F> IntoHostFunction<R, ($($P,)*)> for F
122+
where
123+
F: FnMut($($P),*) -> Result<R> + Send + 'static,
124+
Arc<Mutex<F>>: HostFunction<R, ($($P,)*)>,
125+
{
126+
type Output = Arc<Mutex<F>>;
127+
128+
fn into_host_function(self) -> Self::Output {
129+
Arc::new(Mutex::new(self))
130+
}
131+
}
132+
133+
impl<R $(, $P)*, F> IntoHostFunction<R, ($($P,)*)> for Arc<Mutex<F>>
134+
where
135+
F: FnMut($($P),*) -> Result<R> + Send + 'static,
136+
Arc<Mutex<F>>: HostFunction<R, ($($P,)*)>,
137+
{
138+
type Output = Arc<Mutex<F>>;
139+
140+
fn into_host_function(self) -> Self::Output {
141+
self
142+
}
143+
}
144+
145+
impl<R $(, $P)*, F> IntoHostFunction<R, ($($P,)*)> for &Arc<Mutex<F>>
146+
where
147+
F: FnMut($($P),*) -> Result<R> + Send + 'static,
148+
Arc<Mutex<F>>: HostFunction<R, ($($P,)*)>,
149+
{
150+
type Output = Arc<Mutex<F>>;
151+
152+
fn into_host_function(self) -> Self::Output {
153+
self.clone()
154+
}
155+
}
156+
112157
fn register_host_function<T, $($P,)* R>(
113158
self_: Arc<Mutex<T>>,
114159
sandbox: &mut UninitializedSandbox,

src/hyperlight_host/src/sandbox/uninitialized.rs

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use super::mem_mgr::MemMgrWrapper;
3030
use super::run_options::SandboxRunOptions;
3131
use super::uninitialized_evolve::evolve_impl_multi_use;
3232
use crate::error::HyperlightError::GuestBinaryShouldBeAFile;
33-
use crate::func::host_functions::HostFunction;
33+
use crate::func::host_functions::{HostFunction, IntoHostFunction};
3434
use crate::mem::exe::ExeInfo;
3535
use crate::mem::mgr::{SandboxMemoryManager, STACK_COOKIE_LEN};
3636
use crate::mem::shared_mem::ExclusiveSharedMemory;
@@ -224,24 +224,15 @@ impl UninitializedSandbox {
224224
// If we were passed a writer for host print register it otherwise use the default.
225225
match host_print_writer {
226226
Some(writer_func) => {
227-
#[allow(clippy::arc_with_non_send_sync)]
228-
let writer_func = Arc::new(Mutex::new(writer_func));
229-
230227
#[cfg(any(target_os = "windows", not(feature = "seccomp")))]
231-
writer_func
232-
.try_lock()
233-
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
234-
.register(&mut sandbox, "HostPrint")?;
228+
writer_func.register(&mut sandbox, "HostPrint")?;
235229

236230
#[cfg(all(target_os = "linux", feature = "seccomp"))]
237-
writer_func
238-
.try_lock()
239-
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
240-
.register_with_extra_allowed_syscalls(
241-
&mut sandbox,
242-
"HostPrint",
243-
extra_allowed_syscalls_for_writer_func,
244-
)?;
231+
writer_func.register_with_extra_allowed_syscalls(
232+
&mut sandbox,
233+
"HostPrint",
234+
extra_allowed_syscalls_for_writer_func,
235+
)?;
245236
}
246237
None => {
247238
let default_writer = Arc::new(Mutex::new(default_writer_func));
@@ -309,6 +300,31 @@ impl UninitializedSandbox {
309300
pub fn set_max_guest_log_level(&mut self, log_level: LevelFilter) {
310301
self.max_guest_log_level = Some(log_level);
311302
}
303+
304+
/// Register a host function with the given name in the sandbox.
305+
pub fn register<F, R, Args>(&mut self, name: impl AsRef<str>, host_func: F) -> Result<()>
306+
where
307+
F: IntoHostFunction<R, Args>,
308+
{
309+
host_func.into_host_function().register(self, name.as_ref())
310+
}
311+
312+
/// Register the host function with the given name in the sandbox, allowing extra syscalls.
313+
#[cfg(all(feature = "seccomp", target_os = "linux"))]
314+
pub fn register_with_extra_allowed_syscalls<F, R, Args>(
315+
&mut self,
316+
name: impl AsRef<str>,
317+
host_func: F,
318+
extra_allowed_syscalls: impl IntoIterator<Item = crate::sandbox::ExtraAllowedSyscall>,
319+
) -> Result<()>
320+
where
321+
F: IntoHostFunction<R, Args>,
322+
{
323+
let extra_allowed_syscalls: Vec<_> = extra_allowed_syscalls.into_iter().collect();
324+
host_func
325+
.into_host_function()
326+
.register_with_extra_allowed_syscalls(self, name.as_ref(), extra_allowed_syscalls)
327+
}
312328
}
313329
// Check to see if the current version of Windows is supported
314330
// Hyperlight is only supported on Windows 11 and Windows Server 2022 and later
@@ -355,7 +371,6 @@ mod tests {
355371
use tracing_core::Subscriber;
356372
use uuid::Uuid;
357373

358-
use crate::func::HostFunction;
359374
use crate::sandbox::uninitialized::GuestBinary;
360375
use crate::sandbox::SandboxConfiguration;
361376
use crate::sandbox_state::sandbox::EvolvableSandbox;
@@ -516,9 +531,8 @@ mod tests {
516531
// simple register + call
517532
{
518533
let mut usbox = uninitialized_sandbox();
519-
let test0 = |arg: i32| -> Result<i32> { Ok(arg + 1) };
520-
let test_func0 = Arc::new(Mutex::new(test0));
521-
test_func0.register(&mut usbox, "test0").unwrap();
534+
535+
usbox.register("test0", |arg: i32| Ok(arg + 1)).unwrap();
522536

523537
let sandbox: Result<MultiUseSandbox> = usbox.evolve(Noop::default());
524538
assert!(sandbox.is_ok());
@@ -542,9 +556,8 @@ mod tests {
542556
// multiple parameters register + call
543557
{
544558
let mut usbox = uninitialized_sandbox();
545-
let test1 = |arg1: i32, arg2: i32| -> Result<i32> { Ok(arg1 + arg2) };
546-
let test_func1 = Arc::new(Mutex::new(test1));
547-
test_func1.register(&mut usbox, "test1").unwrap();
559+
560+
usbox.register("test1", |a: i32, b: i32| Ok(a + b)).unwrap();
548561

549562
let sandbox: Result<MultiUseSandbox> = usbox.evolve(Noop::default());
550563
assert!(sandbox.is_ok());
@@ -571,12 +584,13 @@ mod tests {
571584
// incorrect arguments register + call
572585
{
573586
let mut usbox = uninitialized_sandbox();
574-
let test2 = |arg1: String| -> Result<()> {
575-
println!("test2 called: {}", arg1);
576-
Ok(())
577-
};
578-
let test_func2 = Arc::new(Mutex::new(test2));
579-
test_func2.register(&mut usbox, "test2").unwrap();
587+
588+
usbox
589+
.register("test2", |msg: String| {
590+
println!("test2 called: {}", msg);
591+
Ok(())
592+
})
593+
.unwrap();
580594

581595
let sandbox: Result<MultiUseSandbox> = usbox.evolve(Noop::default());
582596
assert!(sandbox.is_ok());

src/hyperlight_host/tests/sandbox_host_tests.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use core::f64;
1818
use std::sync::{Arc, Mutex};
1919

2020
use common::new_uninit;
21-
use hyperlight_host::func::{HostFunction, ParameterValue, ReturnType, ReturnValue};
21+
use hyperlight_host::func::{ParameterValue, ReturnType, ReturnValue};
2222
use hyperlight_host::sandbox::SandboxConfiguration;
2323
use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox;
2424
use hyperlight_host::sandbox_state::transition::Noop;
@@ -545,16 +545,15 @@ fn callback_test_helper() -> Result<()> {
545545
// create host function
546546
let vec = Arc::new(Mutex::new(vec![]));
547547
let vec_cloned = vec.clone();
548-
let host_func1 = Arc::new(Mutex::new(move |msg: String| {
548+
549+
sandbox.register("HostMethod1", move |msg: String| {
549550
let len = msg.len();
550551
vec_cloned
551552
.try_lock()
552553
.map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
553554
.push(msg);
554555
Ok(len as i32)
555-
}));
556-
557-
host_func1.register(&mut sandbox, "HostMethod1").unwrap();
556+
})?;
558557

559558
// call guest function that calls host function
560559
let mut init_sandbox: MultiUseSandbox = sandbox.evolve(Noop::default())?;
@@ -611,10 +610,9 @@ fn host_function_error() -> Result<()> {
611610
// when a host function returns an error, an infinite loop is created.
612611
for mut sandbox in get_callbackguest_uninit_sandboxes(None).into_iter().take(1) {
613612
// create host function
614-
let host_func1 = Arc::new(Mutex::new(|_msg: String| -> Result<String> {
613+
sandbox.register("HostMethod1", |_: String| -> Result<String> {
615614
Err(new_error!("Host function error!"))
616-
}));
617-
host_func1.register(&mut sandbox, "HostMethod1").unwrap();
615+
})?;
618616

619617
// call guest function that calls host function
620618
let mut init_sandbox: MultiUseSandbox = sandbox.evolve(Noop::default())?;

0 commit comments

Comments
 (0)