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
20mod 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;
26/// Functionality for dealing with memory access from the VM guest
27/// executable
28pub(crate) mod mem_access;
29/// Functionality for interacting with a sandbox's internally-stored
30/// `SandboxMemoryManager`
31pub(crate) mod mem_mgr;
32pub(crate) mod outb;
33/// Functionality for creating uninitialized sandboxes, manipulating them,
34/// and converting them to initialized sandboxes.
35pub mod uninitialized;
36/// Functionality for properly converting `UninitializedSandbox`es to
37/// initialized `Sandbox`es.
38pub(crate) mod uninitialized_evolve;
39
40/// Trait used by the macros to paper over the differences between hyperlight and hyperlight-wasm
41mod callable;
42
43/// Trait used by the macros to paper over the differences between hyperlight and hyperlight-wasm
44pub use callable::Callable;
45/// Re-export for `SandboxConfiguration` type
46pub use config::SandboxConfiguration;
47/// Re-export for the `MultiUseSandbox` type
48pub use initialized_multi_use::MultiUseSandbox;
49use tracing::{Span, instrument};
50/// Re-export for `GuestBinary` type
51pub use uninitialized::GuestBinary;
52/// Re-export for `UninitializedSandbox` type
53pub use uninitialized::UninitializedSandbox;
54
55use self::mem_mgr::MemMgrWrapper;
56#[cfg(target_os = "windows")]
57use crate::hypervisor::windows_hypervisor_platform;
58use crate::mem::shared_mem::HostSharedMemory;
59
60// In case its not obvious why there are separate is_supported_platform and is_hypervisor_present functions its because
61// Hyperlight is designed to be able to run on a host that doesn't have a hypervisor.
62// In that case, the sandbox will be in process, we plan on making this a dev only feature and fixing up Linux support
63// so we should review the need for this function at that time.
64
65/// Determine if this is a supported platform for Hyperlight
66///
67/// Returns a boolean indicating whether this is a supported platform.
68#[instrument(skip_all, parent = Span::current())]
69pub fn is_supported_platform() -> bool {
70    #[cfg(not(target_os = "linux"))]
71    #[cfg(not(target_os = "windows"))]
72    return false;
73
74    true
75}
76
77/// Alias for the type of extra allowed syscalls.
78pub type ExtraAllowedSyscall = i64;
79
80/// Determine whether a suitable hypervisor is available to run
81/// this sandbox.
82///
83//  Returns a boolean indicating whether a suitable hypervisor is present.
84#[instrument(skip_all, parent = Span::current())]
85pub fn is_hypervisor_present() -> bool {
86    hypervisor::get_available_hypervisor().is_some()
87}
88
89pub(crate) trait WrapperGetter {
90    #[allow(dead_code)]
91    fn get_mgr_wrapper(&self) -> &MemMgrWrapper<HostSharedMemory>;
92    fn get_mgr_wrapper_mut(&mut self) -> &mut MemMgrWrapper<HostSharedMemory>;
93}
94
95#[cfg(test)]
96mod tests {
97    use std::sync::Arc;
98    use std::thread;
99
100    use crossbeam_queue::ArrayQueue;
101    use hyperlight_testing::simple_guest_as_string;
102
103    use crate::sandbox::uninitialized::GuestBinary;
104    use crate::sandbox_state::sandbox::EvolvableSandbox;
105    use crate::sandbox_state::transition::Noop;
106    use crate::{MultiUseSandbox, UninitializedSandbox, new_error};
107
108    #[test]
109    // TODO: add support for testing on WHP
110    #[cfg(target_os = "linux")]
111    fn is_hypervisor_present() {
112        use std::path::Path;
113
114        cfg_if::cfg_if! {
115            if #[cfg(all(kvm, mshv))] {
116                assert_eq!(Path::new("/dev/kvm").exists() || Path::new("/dev/mshv").exists(), super::is_hypervisor_present());
117            } else if #[cfg(kvm)] {
118                assert_eq!(Path::new("/dev/kvm").exists(), super::is_hypervisor_present());
119            } else if #[cfg(mshv)] {
120                assert_eq!(Path::new("/dev/mshv").exists(), super::is_hypervisor_present());
121            } else {
122                assert!(!super::is_hypervisor_present());
123            }
124        }
125    }
126
127    #[test]
128    fn check_create_and_use_sandbox_on_different_threads() {
129        let unintializedsandbox_queue = Arc::new(ArrayQueue::<UninitializedSandbox>::new(10));
130        let sandbox_queue = Arc::new(ArrayQueue::<MultiUseSandbox>::new(10));
131
132        for i in 0..10 {
133            let simple_guest_path = simple_guest_as_string().expect("Guest Binary Missing");
134            let unintializedsandbox =
135                UninitializedSandbox::new(GuestBinary::FilePath(simple_guest_path), None)
136                    .unwrap_or_else(|_| panic!("Failed to create UninitializedSandbox {}", i));
137
138            unintializedsandbox_queue
139                .push(unintializedsandbox)
140                .unwrap_or_else(|_| panic!("Failed to push UninitializedSandbox {}", i));
141        }
142
143        let thread_handles = (0..10)
144            .map(|i| {
145                let uq = unintializedsandbox_queue.clone();
146                let sq = sandbox_queue.clone();
147                thread::spawn(move || {
148                    let uninitialized_sandbox = uq.pop().unwrap_or_else(|| {
149                        panic!("Failed to pop UninitializedSandbox thread {}", i)
150                    });
151                    let host_funcs = uninitialized_sandbox
152                        .host_funcs
153                        .try_lock()
154                        .map_err(|_| new_error!("Error locking"));
155
156                    assert!(host_funcs.is_ok());
157
158                    host_funcs
159                        .unwrap()
160                        .host_print(format!(
161                            "Printing from UninitializedSandbox on Thread {}\n",
162                            i
163                        ))
164                        .unwrap();
165
166                    let sandbox = uninitialized_sandbox
167                        .evolve(Noop::default())
168                        .unwrap_or_else(|_| {
169                            panic!("Failed to initialize UninitializedSandbox thread {}", i)
170                        });
171
172                    sq.push(sandbox).unwrap_or_else(|_| {
173                        panic!("Failed to push UninitializedSandbox thread {}", i)
174                    })
175                })
176            })
177            .collect::<Vec<_>>();
178
179        for handle in thread_handles {
180            handle.join().unwrap();
181        }
182
183        let thread_handles = (0..10)
184            .map(|i| {
185                let sq = sandbox_queue.clone();
186                thread::spawn(move || {
187                    let sandbox = sq
188                        .pop()
189                        .unwrap_or_else(|| panic!("Failed to pop Sandbox thread {}", i));
190                    let host_funcs = sandbox
191                        ._host_funcs
192                        .try_lock()
193                        .map_err(|_| new_error!("Error locking"));
194
195                    assert!(host_funcs.is_ok());
196
197                    host_funcs
198                        .unwrap()
199                        .host_print(format!("Print from Sandbox on Thread {}\n", i))
200                        .unwrap();
201                })
202            })
203            .collect::<Vec<_>>();
204
205        for handle in thread_handles {
206            handle.join().unwrap();
207        }
208    }
209}