hyperlight_host/sandbox/
mod.rs

1/*
2Copyright 2024 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/// A container to leak, store and manage outb handlers for in-process
27/// executions. On non-in-process executions (e.g. windows without
28/// in-process mode turned on, or linux), the same container is just
29/// a no-op
30#[cfg(inprocess)]
31pub(crate) mod leaked_outb;
32/// Functionality for dealing with memory access from the VM guest
33/// executable
34pub(crate) mod mem_access;
35/// Functionality for interacting with a sandbox's internally-stored
36/// `SandboxMemoryManager`
37pub(crate) mod mem_mgr;
38pub(crate) mod outb;
39/// Options for configuring a sandbox
40mod run_options;
41/// Functionality for creating uninitialized sandboxes, manipulating them,
42/// and converting them to initialized sandboxes.
43pub mod uninitialized;
44/// Functionality for properly converting `UninitializedSandbox`es to
45/// initialized `Sandbox`es.
46pub(crate) mod uninitialized_evolve;
47
48use std::collections::HashMap;
49
50/// Re-export for `SandboxConfiguration` type
51pub use config::SandboxConfiguration;
52/// Re-export for the `MultiUseSandbox` type
53pub use initialized_multi_use::MultiUseSandbox;
54/// Re-export for `SandboxRunOptions` type
55pub use run_options::SandboxRunOptions;
56use tracing::{instrument, Span};
57/// Re-export for `GuestBinary` type
58pub use uninitialized::GuestBinary;
59/// Re-export for `UninitializedSandbox` type
60pub use uninitialized::UninitializedSandbox;
61
62use self::mem_mgr::MemMgrWrapper;
63use crate::func::HyperlightFunction;
64use crate::hypervisor::hypervisor_handler::HypervisorHandler;
65#[cfg(target_os = "windows")]
66use crate::hypervisor::windows_hypervisor_platform;
67use crate::mem::shared_mem::HostSharedMemory;
68
69// In case its not obvious why there are separate is_supported_platform and is_hypervisor_present functions its because
70// Hyperlight is designed to be able to run on a host that doesn't have a hypervisor.
71// In that case, the sandbox will be in process, we plan on making this a dev only feature and fixing up Linux support
72// so we should review the need for this function at that time.
73
74/// Determine if this is a supported platform for Hyperlight
75///
76/// Returns a boolean indicating whether this is a supported platform.
77#[instrument(skip_all, parent = Span::current())]
78pub fn is_supported_platform() -> bool {
79    #[cfg(not(target_os = "linux"))]
80    #[cfg(not(target_os = "windows"))]
81    return false;
82
83    true
84}
85
86/// Alias for the type of extra allowed syscalls.
87pub type ExtraAllowedSyscall = i64;
88
89/// A `HashMap` to map function names to `HyperlightFunction`s and their extra allowed syscalls.
90///
91/// Note: you cannot add extra syscalls on Windows, but the field is still present to avoid a funky
92/// conditional compilation setup. This isn't a big deal as this struct isn't public facing.
93#[derive(Clone, Default)]
94pub(super) struct FunctionsMap(
95    HashMap<String, (HyperlightFunction, Option<Vec<ExtraAllowedSyscall>>)>,
96);
97
98impl FunctionsMap {
99    /// Insert a new entry into the map
100    pub(super) fn insert(
101        &mut self,
102        key: String,
103        value: HyperlightFunction,
104        extra_syscalls: Option<Vec<ExtraAllowedSyscall>>,
105    ) {
106        self.0.insert(key, (value, extra_syscalls));
107    }
108
109    /// Get the value associated with the given key, if it exists.
110    pub(super) fn get(
111        &self,
112        key: &str,
113    ) -> Option<&(HyperlightFunction, Option<Vec<ExtraAllowedSyscall>>)> {
114        self.0.get(key)
115    }
116
117    /// Get the length of the map.
118    fn len(&self) -> usize {
119        self.0.len()
120    }
121}
122
123impl PartialEq for FunctionsMap {
124    #[instrument(skip_all, parent = Span::current(), level= "Trace")]
125    fn eq(&self, other: &Self) -> bool {
126        self.len() == other.len() && self.0.keys().all(|k| other.0.contains_key(k))
127    }
128}
129
130impl Eq for FunctionsMap {}
131
132/// Determine whether a suitable hypervisor is available to run
133/// this sandbox.
134///
135//  Returns a boolean indicating whether a suitable hypervisor is present.
136#[instrument(skip_all, parent = Span::current())]
137pub fn is_hypervisor_present() -> bool {
138    hypervisor::get_available_hypervisor().is_some()
139}
140
141pub(crate) trait WrapperGetter {
142    #[allow(dead_code)]
143    fn get_mgr_wrapper(&self) -> &MemMgrWrapper<HostSharedMemory>;
144    fn get_mgr_wrapper_mut(&mut self) -> &mut MemMgrWrapper<HostSharedMemory>;
145    fn get_hv_handler(&self) -> &HypervisorHandler;
146    #[allow(dead_code)]
147    fn get_hv_handler_mut(&mut self) -> &mut HypervisorHandler;
148}
149
150#[cfg(test)]
151mod tests {
152    use std::sync::Arc;
153    use std::thread;
154
155    use crossbeam_queue::ArrayQueue;
156    use hyperlight_testing::simple_guest_as_string;
157
158    use crate::sandbox::uninitialized::GuestBinary;
159    use crate::sandbox_state::sandbox::EvolvableSandbox;
160    use crate::sandbox_state::transition::Noop;
161    use crate::{new_error, MultiUseSandbox, UninitializedSandbox};
162
163    #[test]
164    // TODO: add support for testing on WHP
165    #[cfg(target_os = "linux")]
166    fn is_hypervisor_present() {
167        use std::path::Path;
168
169        cfg_if::cfg_if! {
170            if #[cfg(all(kvm, mshv))] {
171                assert_eq!(Path::new("/dev/kvm").exists() || Path::new("/dev/mshv").exists(), super::is_hypervisor_present());
172            } else if #[cfg(kvm)] {
173                assert_eq!(Path::new("/dev/kvm").exists(), super::is_hypervisor_present());
174            } else if #[cfg(mshv)] {
175                assert_eq!(Path::new("/dev/mshv").exists(), super::is_hypervisor_present());
176            } else {
177                assert!(!super::is_hypervisor_present());
178            }
179        }
180    }
181
182    #[test]
183    fn check_create_and_use_sandbox_on_different_threads() {
184        let unintializedsandbox_queue = Arc::new(ArrayQueue::<UninitializedSandbox>::new(10));
185        let sandbox_queue = Arc::new(ArrayQueue::<MultiUseSandbox>::new(10));
186
187        for i in 0..10 {
188            let simple_guest_path = simple_guest_as_string().expect("Guest Binary Missing");
189            let unintializedsandbox = UninitializedSandbox::new(
190                GuestBinary::FilePath(simple_guest_path),
191                None,
192                None,
193                None,
194            )
195            .unwrap_or_else(|_| panic!("Failed to create UninitializedSandbox {}", i));
196
197            unintializedsandbox_queue
198                .push(unintializedsandbox)
199                .unwrap_or_else(|_| panic!("Failed to push UninitializedSandbox {}", i));
200        }
201
202        let thread_handles = (0..10)
203            .map(|i| {
204                let uq = unintializedsandbox_queue.clone();
205                let sq = sandbox_queue.clone();
206                thread::spawn(move || {
207                    let uninitialized_sandbox = uq.pop().unwrap_or_else(|| {
208                        panic!("Failed to pop UninitializedSandbox thread {}", i)
209                    });
210                    let host_funcs = uninitialized_sandbox
211                        .host_funcs
212                        .try_lock()
213                        .map_err(|_| new_error!("Error locking"));
214
215                    assert!(host_funcs.is_ok());
216
217                    host_funcs
218                        .unwrap()
219                        .host_print(format!(
220                            "Printing from UninitializedSandbox on Thread {}\n",
221                            i
222                        ))
223                        .unwrap();
224
225                    let sandbox = uninitialized_sandbox
226                        .evolve(Noop::default())
227                        .unwrap_or_else(|_| {
228                            panic!("Failed to initialize UninitializedSandbox thread {}", i)
229                        });
230
231                    sq.push(sandbox).unwrap_or_else(|_| {
232                        panic!("Failed to push UninitializedSandbox thread {}", i)
233                    })
234                })
235            })
236            .collect::<Vec<_>>();
237
238        for handle in thread_handles {
239            handle.join().unwrap();
240        }
241
242        let thread_handles = (0..10)
243            .map(|i| {
244                let sq = sandbox_queue.clone();
245                thread::spawn(move || {
246                    let sandbox = sq
247                        .pop()
248                        .unwrap_or_else(|| panic!("Failed to pop Sandbox thread {}", i));
249                    let host_funcs = sandbox
250                        ._host_funcs
251                        .try_lock()
252                        .map_err(|_| new_error!("Error locking"));
253
254                    assert!(host_funcs.is_ok());
255
256                    host_funcs
257                        .unwrap()
258                        .host_print(format!("Print from Sandbox on Thread {}\n", i))
259                        .unwrap();
260                })
261            })
262            .collect::<Vec<_>>();
263
264        for handle in thread_handles {
265            handle.join().unwrap();
266        }
267    }
268}