hyperlight_host/sandbox/
mod.rs

1/*
2Copyright 2025  The Hyperlight Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17/// Configuration needed to establish a sandbox.
18pub mod config;
19/// Functionality for reading, but not modifying host functions
20pub(crate) mod host_funcs;
21/// Functionality for dealing with `Sandbox`es that contain Hypervisors
22pub(crate) mod hypervisor;
23/// Functionality for dealing with initialized sandboxes that can
24/// call 0 or more guest functions
25pub mod initialized_multi_use;
26pub(crate) mod outb;
27/// Functionality for creating uninitialized sandboxes, manipulating them,
28/// and converting them to initialized sandboxes.
29pub mod uninitialized;
30/// Functionality for properly converting `UninitializedSandbox`es to
31/// initialized `Sandbox`es.
32pub(crate) mod uninitialized_evolve;
33
34/// Representation of a snapshot of a `Sandbox`.
35pub mod snapshot;
36
37/// Trait used by the macros to paper over the differences between hyperlight and hyperlight-wasm
38mod callable;
39
40/// Module for tracing guest execution
41#[cfg(feature = "trace_guest")]
42pub(crate) mod trace;
43
44/// Trait used by the macros to paper over the differences between hyperlight and hyperlight-wasm
45pub use callable::Callable;
46/// Re-export for `SandboxConfiguration` type
47pub use config::SandboxConfiguration;
48/// Re-export for the `MultiUseSandbox` type
49pub use initialized_multi_use::MultiUseSandbox;
50use tracing::{Span, instrument};
51/// Re-export for `GuestBinary` type
52pub use uninitialized::GuestBinary;
53/// Re-export for `UninitializedSandbox` type
54pub use uninitialized::UninitializedSandbox;
55
56#[cfg(target_os = "windows")]
57use crate::hypervisor::windows_hypervisor_platform;
58
59// In case its not obvious why there are separate is_supported_platform and is_hypervisor_present functions its because
60// Hyperlight is designed to be able to run on a host that doesn't have a hypervisor.
61// In that case, the sandbox will be in process, we plan on making this a dev only feature and fixing up Linux support
62// so we should review the need for this function at that time.
63
64/// Determine if this is a supported platform for Hyperlight
65///
66/// Returns a boolean indicating whether this is a supported platform.
67#[instrument(skip_all, parent = Span::current())]
68pub fn is_supported_platform() -> bool {
69    #[cfg(not(target_os = "linux"))]
70    #[cfg(not(target_os = "windows"))]
71    return false;
72
73    true
74}
75
76/// Alias for the type of extra allowed syscalls.
77pub type ExtraAllowedSyscall = i64;
78
79/// Determine whether a suitable hypervisor is available to run
80/// this sandbox.
81///
82///  Returns a boolean indicating whether a suitable hypervisor is present.
83#[instrument(skip_all, parent = Span::current())]
84pub fn is_hypervisor_present() -> bool {
85    hypervisor::get_available_hypervisor().is_some()
86}
87
88#[cfg(test)]
89mod tests {
90    use std::sync::Arc;
91    use std::thread;
92
93    use crossbeam_queue::ArrayQueue;
94    use hyperlight_testing::simple_guest_as_string;
95
96    use crate::sandbox::uninitialized::GuestBinary;
97    use crate::{MultiUseSandbox, UninitializedSandbox, new_error};
98
99    #[test]
100    // TODO: add support for testing on WHP
101    #[cfg(target_os = "linux")]
102    fn is_hypervisor_present() {
103        use std::path::Path;
104
105        cfg_if::cfg_if! {
106            if #[cfg(all(kvm, mshv3))] {
107                assert_eq!(Path::new("/dev/kvm").exists() || Path::new("/dev/mshv").exists(), super::is_hypervisor_present());
108            } else if #[cfg(kvm)] {
109                assert_eq!(Path::new("/dev/kvm").exists(), super::is_hypervisor_present());
110            } else if #[cfg(mshv3)] {
111                assert_eq!(Path::new("/dev/mshv").exists(), super::is_hypervisor_present());
112            } else {
113                assert!(!super::is_hypervisor_present());
114            }
115        }
116    }
117
118    #[test]
119    fn check_create_and_use_sandbox_on_different_threads() {
120        let unintializedsandbox_queue = Arc::new(ArrayQueue::<UninitializedSandbox>::new(10));
121        let sandbox_queue = Arc::new(ArrayQueue::<MultiUseSandbox>::new(10));
122
123        for i in 0..10 {
124            let simple_guest_path = simple_guest_as_string().expect("Guest Binary Missing");
125            let unintializedsandbox =
126                UninitializedSandbox::new(GuestBinary::FilePath(simple_guest_path), None)
127                    .unwrap_or_else(|_| panic!("Failed to create UninitializedSandbox {}", i));
128
129            unintializedsandbox_queue
130                .push(unintializedsandbox)
131                .unwrap_or_else(|_| panic!("Failed to push UninitializedSandbox {}", i));
132        }
133
134        let thread_handles = (0..10)
135            .map(|i| {
136                let uq = unintializedsandbox_queue.clone();
137                let sq = sandbox_queue.clone();
138                thread::spawn(move || {
139                    let uninitialized_sandbox = uq.pop().unwrap_or_else(|| {
140                        panic!("Failed to pop UninitializedSandbox thread {}", i)
141                    });
142                    let host_funcs = uninitialized_sandbox
143                        .host_funcs
144                        .try_lock()
145                        .map_err(|_| new_error!("Error locking"));
146
147                    assert!(host_funcs.is_ok());
148
149                    host_funcs
150                        .unwrap()
151                        .host_print(format!(
152                            "Printing from UninitializedSandbox on Thread {}\n",
153                            i
154                        ))
155                        .unwrap();
156
157                    let sandbox = uninitialized_sandbox.evolve().unwrap_or_else(|_| {
158                        panic!("Failed to initialize UninitializedSandbox thread {}", i)
159                    });
160
161                    sq.push(sandbox).unwrap_or_else(|_| {
162                        panic!("Failed to push UninitializedSandbox thread {}", i)
163                    })
164                })
165            })
166            .collect::<Vec<_>>();
167
168        for handle in thread_handles {
169            handle.join().unwrap();
170        }
171
172        let thread_handles = (0..10)
173            .map(|i| {
174                let sq = sandbox_queue.clone();
175                thread::spawn(move || {
176                    let sandbox = sq
177                        .pop()
178                        .unwrap_or_else(|| panic!("Failed to pop Sandbox thread {}", i));
179                    let host_funcs = sandbox
180                        ._host_funcs
181                        .try_lock()
182                        .map_err(|_| new_error!("Error locking"));
183
184                    assert!(host_funcs.is_ok());
185
186                    host_funcs
187                        .unwrap()
188                        .host_print(format!("Print from Sandbox on Thread {}\n", i))
189                        .unwrap();
190                })
191            })
192            .collect::<Vec<_>>();
193
194        for handle in thread_handles {
195            handle.join().unwrap();
196        }
197    }
198}