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