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