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