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