hyperlight_host/sandbox/
mod.rs1pub mod config;
19pub(crate) mod host_funcs;
21pub(crate) mod hypervisor;
23pub mod initialized_multi_use;
26pub(crate) mod mem_mgr;
29pub(crate) mod outb;
30pub mod uninitialized;
33pub(crate) mod uninitialized_evolve;
36
37pub mod snapshot;
39
40mod callable;
42
43#[cfg(feature = "unwind_guest")]
44use std::io::Write;
45#[cfg(feature = "trace_guest")]
46use std::sync::{Arc, Mutex};
47
48pub use callable::Callable;
50pub use config::SandboxConfiguration;
52#[cfg(feature = "unwind_guest")]
53use framehop::Unwinder;
54pub use initialized_multi_use::MultiUseSandbox;
56use tracing::{Span, instrument};
57pub use uninitialized::GuestBinary;
59pub use uninitialized::UninitializedSandbox;
61
62use self::mem_mgr::MemMgrWrapper;
63#[cfg(target_os = "windows")]
64use crate::hypervisor::windows_hypervisor_platform;
65use crate::mem::shared_mem::HostSharedMemory;
66
67#[instrument(skip_all, parent = Span::current())]
76pub fn is_supported_platform() -> bool {
77 #[cfg(not(target_os = "linux"))]
78 #[cfg(not(target_os = "windows"))]
79 return false;
80
81 true
82}
83
84pub type ExtraAllowedSyscall = i64;
86
87#[instrument(skip_all, parent = Span::current())]
92pub fn is_hypervisor_present() -> bool {
93 hypervisor::get_available_hypervisor().is_some()
94}
95
96#[cfg(feature = "trace_guest")]
97#[derive(Clone)]
98pub(crate) struct TraceInfo {
101 pub epoch: std::time::Instant,
104 pub tsc_freq: Option<u64>,
106 pub guest_start_epoch: Option<std::time::Instant>,
110 pub guest_start_tsc: Option<u64>,
121 #[allow(dead_code)]
123 pub file: Arc<Mutex<std::fs::File>>,
124 #[cfg(feature = "unwind_guest")]
126 #[allow(dead_code)]
127 pub unwind_module: Arc<dyn crate::mem::exe::UnwindInfo>,
128 #[cfg(feature = "unwind_guest")]
130 pub unwinder: framehop::x86_64::UnwinderX86_64<Vec<u8>>,
131 #[cfg(feature = "unwind_guest")]
133 pub unwind_cache: Arc<Mutex<framehop::x86_64::CacheX86_64>>,
134}
135#[cfg(feature = "trace_guest")]
136impl TraceInfo {
137 pub fn new(
140 #[cfg(feature = "unwind_guest")] unwind_module: Arc<dyn crate::mem::exe::UnwindInfo>,
141 ) -> crate::Result<Self> {
142 let mut path = std::env::current_dir()?;
143 path.push("trace");
144
145 if !path.exists() {
147 std::fs::create_dir(&path)?;
148 }
149 path.push(uuid::Uuid::new_v4().to_string());
150 path.set_extension("trace");
151
152 log::info!("Creating trace file at: {}", path.display());
153 println!("Creating trace file at: {}", path.display());
154
155 #[cfg(feature = "unwind_guest")]
156 let hash = unwind_module.hash();
157 #[cfg(feature = "unwind_guest")]
158 let (unwinder, unwind_cache) = {
159 let mut unwinder = framehop::x86_64::UnwinderX86_64::new();
160 unwinder.add_module(unwind_module.clone().as_module());
161 let cache = framehop::x86_64::CacheX86_64::new();
162 (unwinder, Arc::new(Mutex::new(cache)))
163 };
164 if !hyperlight_guest_tracing::invariant_tsc::has_invariant_tsc() {
165 log::warn!(
168 "Invariant TSC is not supported on this platform, trace timestamps may be inaccurate"
169 );
170 }
171
172 let ret = Self {
173 epoch: std::time::Instant::now(),
174 tsc_freq: None,
175 guest_start_epoch: None,
176 guest_start_tsc: None,
177 file: Arc::new(Mutex::new(std::fs::File::create_new(path)?)),
178 #[cfg(feature = "unwind_guest")]
179 unwind_module,
180 #[cfg(feature = "unwind_guest")]
181 unwinder,
182 #[cfg(feature = "unwind_guest")]
183 unwind_cache,
184 };
185 #[cfg(feature = "unwind_guest")]
187 self::outb::record_trace_frame(&ret, 0, |f| {
188 let _ = f.write_all(hash.as_bytes());
189 })?;
190 Ok(ret)
191 }
192
193 fn calculate_tsc_freq(&mut self) -> crate::Result<()> {
195 let (start, start_time) = match (
196 self.guest_start_tsc.as_ref(),
197 self.guest_start_epoch.as_ref(),
198 ) {
199 (Some(start), Some(start_time)) => (*start, *start_time),
200 _ => {
201 log::error!(
208 "Guest start TSC and time are not set. Calculating TSC frequency will use current time and TSC."
209 );
210 (
211 hyperlight_guest_tracing::invariant_tsc::read_tsc(),
212 std::time::Instant::now(),
213 )
214 }
215 };
216
217 let end_time = std::time::Instant::now();
218 let end = hyperlight_guest_tracing::invariant_tsc::read_tsc();
219
220 let elapsed = end_time.duration_since(start_time).as_secs_f64();
221 let tsc_freq = ((end - start) as f64 / elapsed) as u64;
222
223 log::info!("Calculated TSC frequency: {} Hz", tsc_freq);
224 self.tsc_freq = Some(tsc_freq);
225
226 Ok(())
227 }
228}
229
230pub(crate) trait WrapperGetter {
231 #[allow(dead_code)]
232 fn get_mgr_wrapper(&self) -> &MemMgrWrapper<HostSharedMemory>;
233 fn get_mgr_wrapper_mut(&mut self) -> &mut MemMgrWrapper<HostSharedMemory>;
234}
235
236#[cfg(test)]
237mod tests {
238 use std::sync::Arc;
239 use std::thread;
240
241 use crossbeam_queue::ArrayQueue;
242 use hyperlight_testing::simple_guest_as_string;
243
244 use crate::sandbox::uninitialized::GuestBinary;
245 use crate::{MultiUseSandbox, UninitializedSandbox, new_error};
246
247 #[test]
248 #[cfg(target_os = "linux")]
250 fn is_hypervisor_present() {
251 use std::path::Path;
252
253 cfg_if::cfg_if! {
254 if #[cfg(all(kvm, mshv))] {
255 assert_eq!(Path::new("/dev/kvm").exists() || Path::new("/dev/mshv").exists(), super::is_hypervisor_present());
256 } else if #[cfg(kvm)] {
257 assert_eq!(Path::new("/dev/kvm").exists(), super::is_hypervisor_present());
258 } else if #[cfg(mshv)] {
259 assert_eq!(Path::new("/dev/mshv").exists(), super::is_hypervisor_present());
260 } else {
261 assert!(!super::is_hypervisor_present());
262 }
263 }
264 }
265
266 #[test]
267 fn check_create_and_use_sandbox_on_different_threads() {
268 let unintializedsandbox_queue = Arc::new(ArrayQueue::<UninitializedSandbox>::new(10));
269 let sandbox_queue = Arc::new(ArrayQueue::<MultiUseSandbox>::new(10));
270
271 for i in 0..10 {
272 let simple_guest_path = simple_guest_as_string().expect("Guest Binary Missing");
273 let unintializedsandbox =
274 UninitializedSandbox::new(GuestBinary::FilePath(simple_guest_path), None)
275 .unwrap_or_else(|_| panic!("Failed to create UninitializedSandbox {}", i));
276
277 unintializedsandbox_queue
278 .push(unintializedsandbox)
279 .unwrap_or_else(|_| panic!("Failed to push UninitializedSandbox {}", i));
280 }
281
282 let thread_handles = (0..10)
283 .map(|i| {
284 let uq = unintializedsandbox_queue.clone();
285 let sq = sandbox_queue.clone();
286 thread::spawn(move || {
287 let uninitialized_sandbox = uq.pop().unwrap_or_else(|| {
288 panic!("Failed to pop UninitializedSandbox thread {}", i)
289 });
290 let host_funcs = uninitialized_sandbox
291 .host_funcs
292 .try_lock()
293 .map_err(|_| new_error!("Error locking"));
294
295 assert!(host_funcs.is_ok());
296
297 host_funcs
298 .unwrap()
299 .host_print(format!(
300 "Printing from UninitializedSandbox on Thread {}\n",
301 i
302 ))
303 .unwrap();
304
305 let sandbox = uninitialized_sandbox.evolve().unwrap_or_else(|_| {
306 panic!("Failed to initialize UninitializedSandbox thread {}", i)
307 });
308
309 sq.push(sandbox).unwrap_or_else(|_| {
310 panic!("Failed to push UninitializedSandbox thread {}", i)
311 })
312 })
313 })
314 .collect::<Vec<_>>();
315
316 for handle in thread_handles {
317 handle.join().unwrap();
318 }
319
320 let thread_handles = (0..10)
321 .map(|i| {
322 let sq = sandbox_queue.clone();
323 thread::spawn(move || {
324 let sandbox = sq
325 .pop()
326 .unwrap_or_else(|| panic!("Failed to pop Sandbox thread {}", i));
327 let host_funcs = sandbox
328 ._host_funcs
329 .try_lock()
330 .map_err(|_| new_error!("Error locking"));
331
332 assert!(host_funcs.is_ok());
333
334 host_funcs
335 .unwrap()
336 .host_print(format!("Print from Sandbox on Thread {}\n", i))
337 .unwrap();
338 })
339 })
340 .collect::<Vec<_>>();
341
342 for handle in thread_handles {
343 handle.join().unwrap();
344 }
345 }
346}