1use std::fmt::Debug;
18use std::option::Option;
19use std::path::Path;
20use std::sync::{Arc, Mutex};
21use std::time::Duration;
22
23use tracing::{instrument, Span};
24
25#[cfg(gdb)]
26use super::config::DebugInfo;
27use super::host_funcs::{default_writer_func, HostFuncsWrapper};
28use super::mem_mgr::MemMgrWrapper;
29use super::run_options::SandboxRunOptions;
30use super::uninitialized_evolve::evolve_impl_multi_use;
31use crate::error::HyperlightError::GuestBinaryShouldBeAFile;
32use crate::func::host_functions::HostFunction1;
33use crate::mem::exe::ExeInfo;
34use crate::mem::mgr::{SandboxMemoryManager, STACK_COOKIE_LEN};
35use crate::mem::shared_mem::ExclusiveSharedMemory;
36use crate::sandbox::SandboxConfiguration;
37use crate::sandbox_state::sandbox::EvolvableSandbox;
38use crate::sandbox_state::transition::Noop;
39use crate::{log_build_details, log_then_return, new_error, MultiUseSandbox, Result};
40
41pub struct UninitializedSandbox {
49 pub(crate) host_funcs: Arc<Mutex<HostFuncsWrapper>>,
51 pub(crate) mgr: MemMgrWrapper<ExclusiveSharedMemory>,
53 pub(crate) run_inprocess: bool,
54 pub(crate) max_initialization_time: Duration,
55 pub(crate) max_execution_time: Duration,
56 pub(crate) max_wait_for_cancellation: Duration,
57 #[cfg(gdb)]
58 pub(crate) debug_info: Option<DebugInfo>,
59}
60
61impl crate::sandbox_state::sandbox::UninitializedSandbox for UninitializedSandbox {
62 #[instrument(skip_all, parent = Span::current(), level = "Trace")]
63 fn get_uninitialized_sandbox(&self) -> &crate::sandbox::UninitializedSandbox {
64 self
65 }
66
67 #[instrument(skip_all, parent = Span::current(), level = "Trace")]
68 fn get_uninitialized_sandbox_mut(&mut self) -> &mut crate::sandbox::UninitializedSandbox {
69 self
70 }
71}
72
73impl Debug for UninitializedSandbox {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 f.debug_struct("UninitializedSandbox")
76 .field("memory_layout", &self.mgr.unwrap_mgr().layout)
77 .finish()
78 }
79}
80
81impl crate::sandbox_state::sandbox::Sandbox for UninitializedSandbox {
82 fn check_stack_guard(&self) -> Result<bool> {
83 log_then_return!(
84 "Checking the stack cookie before the sandbox is initialized is unsupported"
85 );
86 }
87}
88
89impl
90 EvolvableSandbox<
91 UninitializedSandbox,
92 MultiUseSandbox,
93 Noop<UninitializedSandbox, MultiUseSandbox>,
94 > for UninitializedSandbox
95{
96 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
98 fn evolve(self, _: Noop<UninitializedSandbox, MultiUseSandbox>) -> Result<MultiUseSandbox> {
99 evolve_impl_multi_use(self)
100 }
101}
102
103#[derive(Debug)]
105pub enum GuestBinary {
106 Buffer(Vec<u8>),
108 FilePath(String),
110}
111
112impl UninitializedSandbox {
113 #[instrument(
121 err(Debug),
122 skip(guest_binary, host_print_writer),
123 parent = Span::current()
124 )]
125 pub fn new(
126 guest_binary: GuestBinary,
127 cfg: Option<SandboxConfiguration>,
128 sandbox_run_options: Option<SandboxRunOptions>,
129 host_print_writer: Option<&dyn HostFunction1<String, i32>>,
130 ) -> Result<Self> {
131 log_build_details();
132
133 #[cfg(target_os = "windows")]
135 check_windows_version()?;
136
137 let guest_binary = match guest_binary {
139 GuestBinary::FilePath(binary_path) => {
140 let path = Path::new(&binary_path)
141 .canonicalize()
142 .map_err(|e| new_error!("GuestBinary not found: '{}': {}", binary_path, e))?;
143 GuestBinary::FilePath(
144 path.into_os_string()
145 .into_string()
146 .map_err(|e| new_error!("Error converting OsString to String: {:?}", e))?,
147 )
148 }
149 buffer @ GuestBinary::Buffer(_) => buffer,
150 };
151
152 let run_opts = sandbox_run_options.unwrap_or_default();
153
154 let run_inprocess = run_opts.in_process();
155 let use_loadlib = run_opts.use_loadlib();
156
157 if run_inprocess && cfg!(not(inprocess)) {
158 log_then_return!(
159 "Inprocess mode is only available in debug builds, and also requires cargo feature 'inprocess'"
160 )
161 }
162
163 if use_loadlib && cfg!(not(all(inprocess, target_os = "windows"))) {
164 log_then_return!("Inprocess mode with LoadLibrary is only available on Windows")
165 }
166
167 let sandbox_cfg = cfg.unwrap_or_default();
168
169 #[cfg(gdb)]
170 let debug_info = sandbox_cfg.get_guest_debug_info();
171 let mut mem_mgr_wrapper = {
172 let mut mgr = UninitializedSandbox::load_guest_binary(
173 sandbox_cfg,
174 &guest_binary,
175 run_inprocess,
176 use_loadlib,
177 )?;
178 let stack_guard = Self::create_stack_guard();
179 mgr.set_stack_guard(&stack_guard)?;
180 MemMgrWrapper::new(mgr, stack_guard)
181 };
182
183 mem_mgr_wrapper.write_memory_layout(run_inprocess)?;
184
185 let host_funcs = Arc::new(Mutex::new(HostFuncsWrapper::default()));
186
187 let mut sandbox = Self {
188 host_funcs,
189 mgr: mem_mgr_wrapper,
190 run_inprocess,
191 max_initialization_time: Duration::from_millis(
192 sandbox_cfg.get_max_initialization_time() as u64,
193 ),
194 max_execution_time: Duration::from_millis(sandbox_cfg.get_max_execution_time() as u64),
195 max_wait_for_cancellation: Duration::from_millis(
196 sandbox_cfg.get_max_wait_for_cancellation() as u64,
197 ),
198 #[cfg(gdb)]
199 debug_info,
200 };
201
202 #[cfg(all(target_os = "linux", feature = "seccomp"))]
207 let extra_allowed_syscalls_for_writer_func = vec![
208 libc::SYS_mmap,
215 libc::SYS_brk,
216 libc::SYS_mprotect,
217 #[cfg(mshv)]
218 libc::SYS_close,
219 ];
220
221 match host_print_writer {
223 Some(writer_func) => {
224 #[allow(clippy::arc_with_non_send_sync)]
225 let writer_func = Arc::new(Mutex::new(writer_func));
226
227 #[cfg(any(target_os = "windows", not(feature = "seccomp")))]
228 writer_func
229 .try_lock()
230 .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
231 .register(&mut sandbox, "HostPrint")?;
232
233 #[cfg(all(target_os = "linux", feature = "seccomp"))]
234 writer_func
235 .try_lock()
236 .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?
237 .register_with_extra_allowed_syscalls(
238 &mut sandbox,
239 "HostPrint",
240 extra_allowed_syscalls_for_writer_func,
241 )?;
242 }
243 None => {
244 let default_writer = Arc::new(Mutex::new(default_writer_func));
245
246 #[cfg(any(target_os = "windows", not(feature = "seccomp")))]
247 default_writer.register(&mut sandbox, "HostPrint")?;
248
249 #[cfg(all(target_os = "linux", feature = "seccomp"))]
250 default_writer.register_with_extra_allowed_syscalls(
251 &mut sandbox,
252 "HostPrint",
253 extra_allowed_syscalls_for_writer_func,
254 )?;
255 }
256 }
257
258 crate::debug!("Sandbox created: {:#?}", sandbox);
259
260 Ok(sandbox)
261 }
262
263 #[instrument(skip_all, parent = Span::current(), level = "Trace")]
264 fn create_stack_guard() -> [u8; STACK_COOKIE_LEN] {
265 rand::random::<[u8; STACK_COOKIE_LEN]>()
266 }
267
268 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
279 pub(super) fn load_guest_binary(
280 cfg: SandboxConfiguration,
281 guest_binary: &GuestBinary,
282 inprocess: bool,
283 use_loadlib: bool,
284 ) -> Result<SandboxMemoryManager<ExclusiveSharedMemory>> {
285 let mut exe_info = match guest_binary {
286 GuestBinary::FilePath(bin_path_str) => ExeInfo::from_file(bin_path_str)?,
287 GuestBinary::Buffer(buffer) => ExeInfo::from_buf(buffer)?,
288 };
289
290 if use_loadlib {
291 let path = match guest_binary {
292 GuestBinary::FilePath(bin_path_str) => bin_path_str,
293 GuestBinary::Buffer(_) => {
294 log_then_return!(GuestBinaryShouldBeAFile());
295 }
296 };
297 SandboxMemoryManager::load_guest_binary_using_load_library(cfg, path, &mut exe_info)
298 } else {
299 SandboxMemoryManager::load_guest_binary_into_memory(cfg, &mut exe_info, inprocess)
300 }
301 }
302}
303#[cfg(target_os = "windows")]
306fn check_windows_version() -> Result<()> {
307 use windows_version::{is_server, OsVersion};
308 const WINDOWS_MAJOR: u32 = 10;
309 const WINDOWS_MINOR: u32 = 0;
310 const WINDOWS_PACK: u32 = 0;
311
312 if is_server() {
314 if OsVersion::current() < OsVersion::new(WINDOWS_MAJOR, WINDOWS_MINOR, WINDOWS_PACK, 20348)
315 {
316 return Err(new_error!(
317 "Hyperlight Requires Windows Server 2022 or newer"
318 ));
319 }
320 } else if OsVersion::current()
321 < OsVersion::new(WINDOWS_MAJOR, WINDOWS_MINOR, WINDOWS_PACK, 22000)
322 {
323 return Err(new_error!("Hyperlight Requires Windows 11 or newer"));
324 }
325 Ok(())
326}
327
328#[cfg(test)]
329mod tests {
330 use std::path::PathBuf;
331 use std::sync::{Arc, Mutex};
332 use std::time::Duration;
333 use std::{fs, thread};
334
335 use crossbeam_queue::ArrayQueue;
336 use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterValue, ReturnValue};
337 use hyperlight_testing::logger::{Logger as TestLogger, LOGGER as TEST_LOGGER};
338 use hyperlight_testing::tracing_subscriber::TracingSubscriber as TestSubcriber;
339 use hyperlight_testing::{simple_guest_as_string, simple_guest_exe_as_string};
340 use log::Level;
341 use serde_json::{Map, Value};
342 use serial_test::serial;
343 use tracing::Level as tracing_level;
344 use tracing_core::callsite::rebuild_interest_cache;
345 use tracing_core::Subscriber;
346 use uuid::Uuid;
347
348 use crate::func::{HostFunction1, HostFunction2};
349 use crate::sandbox::uninitialized::GuestBinary;
350 use crate::sandbox::SandboxConfiguration;
351 use crate::sandbox_state::sandbox::EvolvableSandbox;
352 use crate::sandbox_state::transition::Noop;
353 use crate::testing::log_values::{test_value_as_str, try_to_strings};
354 use crate::{new_error, MultiUseSandbox, Result, SandboxRunOptions, UninitializedSandbox};
355
356 #[test]
357 fn test_in_process() {
358 let simple_guest_path = simple_guest_as_string().unwrap();
359 let sbox = UninitializedSandbox::new(
360 GuestBinary::FilePath(simple_guest_path.clone()),
361 None,
362 Some(SandboxRunOptions::RunInProcess(false)),
363 None,
364 );
365
366 assert_eq!(sbox.is_ok(), cfg!(inprocess));
368
369 let sbox = UninitializedSandbox::new(
370 GuestBinary::FilePath(simple_guest_path.clone()),
371 None,
372 Some(SandboxRunOptions::RunInProcess(true)),
373 None,
374 );
375
376 assert!(sbox.is_err());
378
379 let simple_guest_path = simple_guest_exe_as_string().unwrap();
380 let sbox = UninitializedSandbox::new(
381 GuestBinary::FilePath(simple_guest_path.clone()),
382 None,
383 Some(SandboxRunOptions::RunInProcess(false)),
384 None,
385 );
386
387 assert_eq!(sbox.is_ok(), cfg!(all(inprocess)));
389
390 let sbox = UninitializedSandbox::new(
391 GuestBinary::FilePath(simple_guest_path.clone()),
392 None,
393 Some(SandboxRunOptions::RunInProcess(true)),
394 None,
395 );
396
397 assert_eq!(sbox.is_ok(), cfg!(all(inprocess, target_os = "windows")));
399 }
400
401 #[test]
402 fn test_new_sandbox() {
403 let binary_path = simple_guest_as_string().unwrap();
406 let sandbox =
407 UninitializedSandbox::new(GuestBinary::FilePath(binary_path.clone()), None, None, None);
408 assert!(sandbox.is_ok());
409
410 let mut binary_path_does_not_exist = binary_path.clone();
413 binary_path_does_not_exist.push_str(".nonexistent");
414 let uninitialized_sandbox = UninitializedSandbox::new(
415 GuestBinary::FilePath(binary_path_does_not_exist),
416 None,
417 None,
418 None,
419 );
420 assert!(uninitialized_sandbox.is_err());
421
422 let cfg = {
424 let mut cfg = SandboxConfiguration::default();
425 cfg.set_input_data_size(0x1000);
426 cfg.set_output_data_size(0x1000);
427 cfg.set_host_function_definition_size(0x1000);
428 cfg.set_host_exception_size(0x1000);
429 cfg.set_guest_error_buffer_size(0x1000);
430 cfg.set_stack_size(0x1000);
431 cfg.set_heap_size(0x1000);
432 cfg.set_max_execution_time(Duration::from_millis(1001));
433 cfg.set_max_execution_cancel_wait_time(Duration::from_millis(9));
434 Some(cfg)
435 };
436
437 let uninitialized_sandbox =
438 UninitializedSandbox::new(GuestBinary::FilePath(binary_path.clone()), cfg, None, None);
439 assert!(uninitialized_sandbox.is_ok());
440
441 let uninitialized_sandbox =
442 UninitializedSandbox::new(GuestBinary::FilePath(binary_path), None, None, None)
443 .unwrap();
444
445 let _sandbox: MultiUseSandbox = uninitialized_sandbox.evolve(Noop::default()).unwrap();
448
449 let binary_path = simple_guest_as_string().unwrap();
452 let sandbox = UninitializedSandbox::new(
453 GuestBinary::Buffer(fs::read(binary_path).unwrap()),
454 None,
455 None,
456 None,
457 );
458 assert!(sandbox.is_ok());
459
460 let binary_path = simple_guest_as_string().unwrap();
463 let mut bytes = fs::read(binary_path).unwrap();
464 let _ = bytes.split_off(100);
465 let sandbox = UninitializedSandbox::new(GuestBinary::Buffer(bytes), None, None, None);
466 assert!(sandbox.is_err());
467
468 #[cfg(target_os = "windows")]
470 {
471 let binary_path = simple_guest_as_string().unwrap();
472 let sandbox = UninitializedSandbox::new(
473 GuestBinary::Buffer(fs::read(binary_path).unwrap()),
474 None,
475 Some(SandboxRunOptions::RunInProcess(true)),
476 None,
477 );
478 assert!(sandbox.is_err());
479 }
480 }
481
482 #[test]
483 fn test_load_guest_binary_manual() {
484 let cfg = SandboxConfiguration::default();
485
486 let simple_guest_path = simple_guest_as_string().unwrap();
487
488 UninitializedSandbox::load_guest_binary(
489 cfg,
490 &GuestBinary::FilePath(simple_guest_path),
491 false,
492 false,
493 )
494 .unwrap();
495 }
496
497 #[test]
498 fn test_host_functions() {
499 let uninitialized_sandbox = || {
500 UninitializedSandbox::new(
501 GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
502 None,
503 None,
504 None,
505 )
506 .unwrap()
507 };
508
509 {
511 let mut usbox = uninitialized_sandbox();
512 let test0 = |arg: i32| -> Result<i32> { Ok(arg + 1) };
513 let test_func0 = Arc::new(Mutex::new(test0));
514 test_func0.register(&mut usbox, "test0").unwrap();
515
516 let sandbox: Result<MultiUseSandbox> = usbox.evolve(Noop::default());
517 assert!(sandbox.is_ok());
518 let sandbox = sandbox.unwrap();
519
520 let host_funcs = sandbox
521 ._host_funcs
522 .try_lock()
523 .map_err(|_| new_error!("Error locking"));
524
525 assert!(host_funcs.is_ok());
526
527 let res = host_funcs
528 .unwrap()
529 .call_host_function("test0", vec![ParameterValue::Int(1)])
530 .unwrap();
531
532 assert_eq!(res, ReturnValue::Int(2));
533 }
534
535 {
537 let mut usbox = uninitialized_sandbox();
538 let test1 = |arg1: i32, arg2: i32| -> Result<i32> { Ok(arg1 + arg2) };
539 let test_func1 = Arc::new(Mutex::new(test1));
540 test_func1.register(&mut usbox, "test1").unwrap();
541
542 let sandbox: Result<MultiUseSandbox> = usbox.evolve(Noop::default());
543 assert!(sandbox.is_ok());
544 let sandbox = sandbox.unwrap();
545
546 let host_funcs = sandbox
547 ._host_funcs
548 .try_lock()
549 .map_err(|_| new_error!("Error locking"));
550
551 assert!(host_funcs.is_ok());
552
553 let res = host_funcs
554 .unwrap()
555 .call_host_function(
556 "test1",
557 vec![ParameterValue::Int(1), ParameterValue::Int(2)],
558 )
559 .unwrap();
560
561 assert_eq!(res, ReturnValue::Int(3));
562 }
563
564 {
566 let mut usbox = uninitialized_sandbox();
567 let test2 = |arg1: String| -> Result<()> {
568 println!("test2 called: {}", arg1);
569 Ok(())
570 };
571 let test_func2 = Arc::new(Mutex::new(test2));
572 test_func2.register(&mut usbox, "test2").unwrap();
573
574 let sandbox: Result<MultiUseSandbox> = usbox.evolve(Noop::default());
575 assert!(sandbox.is_ok());
576 let sandbox = sandbox.unwrap();
577
578 let host_funcs = sandbox
579 ._host_funcs
580 .try_lock()
581 .map_err(|_| new_error!("Error locking"));
582
583 assert!(host_funcs.is_ok());
584
585 let res = host_funcs.unwrap().call_host_function("test2", vec![]);
586 assert!(res.is_err());
587 }
588
589 {
591 let usbox = uninitialized_sandbox();
592 let sandbox: Result<MultiUseSandbox> = usbox.evolve(Noop::default());
593 assert!(sandbox.is_ok());
594 let sandbox = sandbox.unwrap();
595
596 let host_funcs = sandbox
597 ._host_funcs
598 .try_lock()
599 .map_err(|_| new_error!("Error locking"));
600
601 assert!(host_funcs.is_ok());
602
603 let res = host_funcs.unwrap().call_host_function("test4", vec![]);
604 assert!(res.is_err());
605 }
606 }
607
608 #[test]
609 #[serial]
610 fn test_load_guest_binary_load_lib() {
611 let cfg = SandboxConfiguration::default();
612 let simple_guest_path = simple_guest_exe_as_string().unwrap();
613 let mgr_res = UninitializedSandbox::load_guest_binary(
614 cfg,
615 &GuestBinary::FilePath(simple_guest_path),
616 true,
617 true,
618 );
619 #[cfg(target_os = "linux")]
620 {
621 assert!(mgr_res.is_err())
622 }
623 #[cfg(target_os = "windows")]
624 {
625 #[cfg(inprocess)]
626 {
627 assert!(mgr_res.is_ok())
628 }
629 #[cfg(not(inprocess))]
630 {
631 assert!(mgr_res.is_err())
632 }
633 }
634 }
635
636 #[test]
637 fn test_host_print() {
638 let received_msg = Arc::new(Mutex::new(String::new()));
643 let received_msg_clone = received_msg.clone();
644
645 let writer = move |msg| {
646 let mut received_msg = received_msg_clone
647 .try_lock()
648 .map_err(|_| new_error!("Error locking"))
649 .unwrap();
650 *received_msg = msg;
651 Ok(0)
652 };
653
654 let hostfunc = Arc::new(Mutex::new(writer));
655
656 let sandbox = UninitializedSandbox::new(
657 GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
658 None,
659 None,
660 Some(&hostfunc),
661 )
662 .expect("Failed to create sandbox");
663
664 let host_funcs = sandbox
665 .host_funcs
666 .try_lock()
667 .map_err(|_| new_error!("Error locking"));
668
669 assert!(host_funcs.is_ok());
670
671 host_funcs.unwrap().host_print("test".to_string()).unwrap();
672
673 drop(sandbox);
674
675 assert_eq!(
676 received_msg
677 .try_lock()
678 .map_err(|_| new_error!("Error locking"))
679 .unwrap()
680 .as_str(),
681 "test"
682 );
683
684 fn fn_writer(msg: String) -> Result<i32> {
745 assert_eq!(msg, "test2");
746 Ok(0)
747 }
748
749 let writer_func = Arc::new(Mutex::new(fn_writer));
750 let sandbox = UninitializedSandbox::new(
751 GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
752 None,
753 None,
754 Some(&writer_func),
755 )
756 .expect("Failed to create sandbox");
757
758 let host_funcs = sandbox
759 .host_funcs
760 .try_lock()
761 .map_err(|_| new_error!("Error locking"));
762
763 assert!(host_funcs.is_ok());
764
765 host_funcs.unwrap().host_print("test2".to_string()).unwrap();
766
767 let mut test_host_print = TestHostPrint::new();
770
771 let writer_closure = move |s| test_host_print.write(s);
774
775 let writer_method = Arc::new(Mutex::new(writer_closure));
776
777 let sandbox = UninitializedSandbox::new(
778 GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
779 None,
780 None,
781 Some(&writer_method),
782 )
783 .expect("Failed to create sandbox");
784
785 let host_funcs = sandbox
786 .host_funcs
787 .try_lock()
788 .map_err(|_| new_error!("Error locking"));
789
790 assert!(host_funcs.is_ok());
791
792 host_funcs.unwrap().host_print("test3".to_string()).unwrap();
793 }
794
795 struct TestHostPrint {}
796
797 impl TestHostPrint {
798 fn new() -> Self {
799 TestHostPrint {}
800 }
801
802 fn write(&mut self, msg: String) -> Result<i32> {
803 assert_eq!(msg, "test3");
804 Ok(0)
805 }
806 }
807
808 #[test]
809 fn check_create_and_use_sandbox_on_different_threads() {
810 let unintializedsandbox_queue = Arc::new(ArrayQueue::<UninitializedSandbox>::new(10));
811 let sandbox_queue = Arc::new(ArrayQueue::<MultiUseSandbox>::new(10));
812
813 for i in 0..10 {
814 let simple_guest_path = simple_guest_as_string().expect("Guest Binary Missing");
815 let unintializedsandbox = {
816 let err_string = format!("failed to create UninitializedSandbox {i}");
817 let err_str = err_string.as_str();
818 UninitializedSandbox::new(
819 GuestBinary::FilePath(simple_guest_path),
820 None,
821 None,
822 None,
823 )
824 .expect(err_str)
825 };
826
827 {
828 let err_string = format!("Failed to push UninitializedSandbox {i}");
829 let err_str = err_string.as_str();
830
831 unintializedsandbox_queue
832 .push(unintializedsandbox)
833 .expect(err_str);
834 }
835 }
836
837 let thread_handles = (0..10)
838 .map(|i| {
839 let uq = unintializedsandbox_queue.clone();
840 let sq = sandbox_queue.clone();
841 thread::spawn(move || {
842 let uninitialized_sandbox = uq.pop().unwrap_or_else(|| {
843 panic!("Failed to pop UninitializedSandbox thread {}", i)
844 });
845
846 let host_funcs = uninitialized_sandbox
847 .host_funcs
848 .try_lock()
849 .map_err(|_| new_error!("Error locking"));
850
851 assert!(host_funcs.is_ok());
852
853 host_funcs
854 .unwrap()
855 .host_print(format!("Print from UninitializedSandbox on Thread {}\n", i))
856 .unwrap();
857
858 let sandbox = uninitialized_sandbox
859 .evolve(Noop::default())
860 .unwrap_or_else(|_| {
861 panic!("Failed to initialize UninitializedSandbox thread {}", i)
862 });
863
864 sq.push(sandbox).unwrap_or_else(|_| {
865 panic!("Failed to push UninitializedSandbox thread {}", i)
866 })
867 })
868 })
869 .collect::<Vec<_>>();
870
871 for handle in thread_handles {
872 handle.join().unwrap();
873 }
874
875 let thread_handles = (0..10)
876 .map(|i| {
877 let sq = sandbox_queue.clone();
878 thread::spawn(move || {
879 let sandbox = sq
880 .pop()
881 .unwrap_or_else(|| panic!("Failed to pop Sandbox thread {}", i));
882
883 let host_funcs = sandbox
884 ._host_funcs
885 .try_lock()
886 .map_err(|_| new_error!("Error locking"));
887
888 assert!(host_funcs.is_ok());
889
890 host_funcs
891 .unwrap()
892 .host_print(format!("Print from Sandbox on Thread {}\n", i))
893 .unwrap();
894 })
895 })
896 .collect::<Vec<_>>();
897
898 for handle in thread_handles {
899 handle.join().unwrap();
900 }
901 }
902
903 #[test]
904 #[ignore]
910 fn test_trace_trace() {
911 TestLogger::initialize_log_tracer();
912 rebuild_interest_cache();
913 let subscriber = TestSubcriber::new(tracing_level::TRACE);
914 tracing::subscriber::with_default(subscriber.clone(), || {
915 let correlation_id = Uuid::new_v4().as_hyphenated().to_string();
916 let span = tracing::error_span!("test_trace_logs", correlation_id).entered();
917
918 let current_span = subscriber.current_span();
921 assert!(current_span.is_known(), "Current span is unknown");
922 let current_span_metadata = current_span.into_inner().unwrap();
923 assert_eq!(
924 current_span_metadata.0.into_u64(),
925 1,
926 "Current span is not span 1"
927 );
928 assert_eq!(current_span_metadata.1.name(), "test_trace_logs");
929
930 let span_data = subscriber.get_span(1);
933 let span_attributes: &Map<String, Value> = span_data
934 .get("span")
935 .unwrap()
936 .get("attributes")
937 .unwrap()
938 .as_object()
939 .unwrap();
940
941 test_value_as_str(span_attributes, "correlation_id", correlation_id.as_str());
942
943 let mut binary_path = simple_guest_as_string().unwrap();
944 binary_path.push_str("does_not_exist");
945
946 let sbox =
947 UninitializedSandbox::new(GuestBinary::FilePath(binary_path), None, None, None);
948 assert!(sbox.is_err());
949
950 let current_span = subscriber.current_span();
953 assert!(current_span.is_known(), "Current span is unknown");
954 let current_span_metadata = current_span.into_inner().unwrap();
955 assert_eq!(
956 current_span_metadata.0.into_u64(),
957 1,
958 "Current span is not span 1"
959 );
960
961 let span_metadata = subscriber.get_span_metadata(2);
962 assert_eq!(span_metadata.name(), "new");
963
964 let events = subscriber.get_events();
967 assert_eq!(events.len(), 15);
968
969 let mut count_matching_events = 0;
970
971 for json_value in events {
972 let event_values = json_value.as_object().unwrap().get("event").unwrap();
973 let metadata_values_map =
974 event_values.get("metadata").unwrap().as_object().unwrap();
975 let event_values_map = event_values.as_object().unwrap();
976
977 let expected_error_start = "Error(\"GuestBinary not found:";
978
979 let err_vals_res = try_to_strings([
980 (metadata_values_map, "level"),
981 (event_values_map, "error"),
982 (metadata_values_map, "module_path"),
983 (metadata_values_map, "target"),
984 ]);
985 if let Ok(err_vals) = err_vals_res {
986 if err_vals[0] == "ERROR"
987 && err_vals[1].starts_with(expected_error_start)
988 && err_vals[2] == "hyperlight_host::sandbox::uninitialized"
989 && err_vals[3] == "hyperlight_host::sandbox::uninitialized"
990 {
991 count_matching_events += 1;
992 }
993 }
994 }
995 assert!(
996 count_matching_events == 1,
997 "Unexpected number of matching events {}",
998 count_matching_events
999 );
1000 span.exit();
1001 subscriber.clear();
1002 });
1003 }
1004
1005 #[test]
1006 #[ignore]
1007 fn test_log_trace() {
1010 {
1011 TestLogger::initialize_test_logger();
1012 TEST_LOGGER.set_max_level(log::LevelFilter::Trace);
1013
1014 rebuild_interest_cache();
1018
1019 let mut invalid_binary_path = simple_guest_as_string().unwrap();
1020 invalid_binary_path.push_str("does_not_exist");
1021
1022 let sbox = UninitializedSandbox::new(
1023 GuestBinary::FilePath(invalid_binary_path),
1024 None,
1025 None,
1026 None,
1027 );
1028 assert!(sbox.is_err());
1029
1030 let num_calls = TEST_LOGGER.num_log_calls();
1044 assert_eq!(19, num_calls);
1045
1046 let logcall = TEST_LOGGER.get_log_call(0).unwrap();
1049 assert_eq!(Level::Info, logcall.level);
1050
1051 assert!(logcall.args.starts_with("new; cfg"));
1052 assert_eq!("hyperlight_host::sandbox::uninitialized", logcall.target);
1053
1054 let logcall = TEST_LOGGER.get_log_call(1).unwrap();
1057 assert_eq!(Level::Trace, logcall.level);
1058 assert_eq!(logcall.args, "-> new;");
1059 assert_eq!("tracing::span::active", logcall.target);
1060
1061 let logcall = TEST_LOGGER.get_log_call(16).unwrap();
1064 assert_eq!(Level::Error, logcall.level);
1065 assert!(logcall
1066 .args
1067 .starts_with("error=Error(\"GuestBinary not found:"));
1068 assert_eq!("hyperlight_host::sandbox::uninitialized", logcall.target);
1069
1070 let logcall = TEST_LOGGER.get_log_call(17).unwrap();
1073 assert_eq!(Level::Trace, logcall.level);
1074 assert_eq!(logcall.args, "<- new;");
1075 assert_eq!("tracing::span::active", logcall.target);
1076
1077 let logcall = TEST_LOGGER.get_log_call(18).unwrap();
1080 assert_eq!(Level::Trace, logcall.level);
1081 assert_eq!(logcall.args, "-- new;");
1082 assert_eq!("tracing::span", logcall.target);
1083 }
1084 {
1085 TEST_LOGGER.clear_log_calls();
1087 TEST_LOGGER.set_max_level(log::LevelFilter::Info);
1088
1089 let mut valid_binary_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
1090 valid_binary_path.push("src");
1091 valid_binary_path.push("sandbox");
1092 valid_binary_path.push("initialized.rs");
1093
1094 let sbox = UninitializedSandbox::new(
1095 GuestBinary::FilePath(valid_binary_path.into_os_string().into_string().unwrap()),
1096 None,
1097 None,
1098 None,
1099 );
1100 assert!(sbox.is_err());
1101
1102 let num_calls = TEST_LOGGER.num_log_calls();
1105 assert_eq!(2, num_calls);
1106
1107 let logcall = TEST_LOGGER.get_log_call(0).unwrap();
1110 assert_eq!(Level::Info, logcall.level);
1111
1112 assert!(logcall.args.starts_with("new; cfg"));
1113 assert_eq!("hyperlight_host::sandbox::uninitialized", logcall.target);
1114
1115 let logcall = TEST_LOGGER.get_log_call(1).unwrap();
1118 assert_eq!(Level::Error, logcall.level);
1119 assert!(logcall
1120 .args
1121 .starts_with("error=Error(\"GuestBinary not found:"));
1122 assert_eq!("hyperlight_host::sandbox::uninitialized", logcall.target);
1123 }
1124 {
1125 TEST_LOGGER.clear_log_calls();
1126 TEST_LOGGER.set_max_level(log::LevelFilter::Error);
1127
1128 let sbox = {
1129 let res = UninitializedSandbox::new(
1130 GuestBinary::FilePath(simple_guest_as_string().unwrap()),
1131 None,
1132 None,
1133 None,
1134 );
1135 res.unwrap()
1136 };
1137 let _: Result<MultiUseSandbox> = sbox.evolve(Noop::default());
1138
1139 let num_calls = TEST_LOGGER.num_log_calls();
1140
1141 assert_eq!(0, num_calls);
1142 }
1143 }
1144
1145 #[test]
1146 fn test_invalid_path() {
1147 let invalid_path = "some/path/that/does/not/exist";
1148 let sbox = UninitializedSandbox::new(
1149 GuestBinary::FilePath(invalid_path.to_string()),
1150 None,
1151 None,
1152 None,
1153 );
1154 println!("{:?}", sbox);
1155 #[cfg(target_os = "windows")]
1156 assert!(
1157 matches!(sbox, Err(e) if e.to_string().contains("GuestBinary not found: 'some/path/that/does/not/exist': The system cannot find the path specified. (os error 3)"))
1158 );
1159 #[cfg(target_os = "linux")]
1160 assert!(
1161 matches!(sbox, Err(e) if e.to_string().contains("GuestBinary not found: 'some/path/that/does/not/exist': No such file or directory (os error 2)"))
1162 );
1163 }
1164}