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, FunctionRegistry};
29use super::mem_mgr::MemMgrWrapper;
30use super::uninitialized_evolve::evolve_impl_multi_use;
31use crate::func::host_functions::{register_host_function, HostFunction};
32use crate::func::{ParameterTuple, SupportedReturnType};
33#[cfg(feature = "build-metadata")]
34use crate::log_build_details;
35use crate::mem::exe::ExeInfo;
36use crate::mem::mgr::{SandboxMemoryManager, STACK_COOKIE_LEN};
37use crate::mem::shared_mem::ExclusiveSharedMemory;
38use crate::sandbox::SandboxConfiguration;
39use crate::sandbox_state::sandbox::EvolvableSandbox;
40use crate::sandbox_state::transition::Noop;
41use crate::{log_then_return, new_error, MultiUseSandbox, Result};
42
43#[cfg(all(target_os = "linux", feature = "seccomp"))]
44const EXTRA_ALLOWED_SYSCALLS_FOR_WRITER_FUNC: &[super::ExtraAllowedSyscall] = &[
45 libc::SYS_mmap,
52 libc::SYS_brk,
53 libc::SYS_mprotect,
54 #[cfg(mshv)]
55 libc::SYS_close,
56];
57
58pub struct UninitializedSandbox {
66 pub(crate) host_funcs: Arc<Mutex<FunctionRegistry>>,
68 pub(crate) mgr: MemMgrWrapper<ExclusiveSharedMemory>,
70 pub(crate) max_initialization_time: Duration,
71 pub(crate) max_execution_time: Duration,
72 pub(crate) max_wait_for_cancellation: Duration,
73 pub(crate) max_guest_log_level: Option<LevelFilter>,
74 #[cfg(gdb)]
75 pub(crate) debug_info: Option<DebugInfo>,
76}
77
78impl crate::sandbox_state::sandbox::UninitializedSandbox for UninitializedSandbox {
79 #[instrument(skip_all, parent = Span::current(), level = "Trace")]
80 fn get_uninitialized_sandbox(&self) -> &crate::sandbox::UninitializedSandbox {
81 self
82 }
83
84 #[instrument(skip_all, parent = Span::current(), level = "Trace")]
85 fn get_uninitialized_sandbox_mut(&mut self) -> &mut crate::sandbox::UninitializedSandbox {
86 self
87 }
88}
89
90impl Debug for UninitializedSandbox {
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 f.debug_struct("UninitializedSandbox")
93 .field("memory_layout", &self.mgr.unwrap_mgr().layout)
94 .finish()
95 }
96}
97
98impl crate::sandbox_state::sandbox::Sandbox for UninitializedSandbox {
99 fn check_stack_guard(&self) -> Result<bool> {
100 log_then_return!(
101 "Checking the stack cookie before the sandbox is initialized is unsupported"
102 );
103 }
104}
105
106impl
107 EvolvableSandbox<
108 UninitializedSandbox,
109 MultiUseSandbox,
110 Noop<UninitializedSandbox, MultiUseSandbox>,
111 > for UninitializedSandbox
112{
113 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
115 fn evolve(self, _: Noop<UninitializedSandbox, MultiUseSandbox>) -> Result<MultiUseSandbox> {
116 evolve_impl_multi_use(self)
117 }
118}
119
120#[derive(Debug)]
122pub enum GuestBinary {
123 Buffer(Vec<u8>),
125 FilePath(String),
127}
128
129impl UninitializedSandbox {
130 #[instrument(
138 err(Debug),
139 skip(guest_binary),
140 parent = Span::current()
141 )]
142 pub fn new(guest_binary: GuestBinary, cfg: Option<SandboxConfiguration>) -> Result<Self> {
143 #[cfg(feature = "build-metadata")]
144 log_build_details();
145
146 #[cfg(target_os = "windows")]
148 check_windows_version()?;
149
150 let guest_binary = match guest_binary {
152 GuestBinary::FilePath(binary_path) => {
153 let path = Path::new(&binary_path)
154 .canonicalize()
155 .map_err(|e| new_error!("GuestBinary not found: '{}': {}", binary_path, e))?;
156 GuestBinary::FilePath(
157 path.into_os_string()
158 .into_string()
159 .map_err(|e| new_error!("Error converting OsString to String: {:?}", e))?,
160 )
161 }
162 buffer @ GuestBinary::Buffer(_) => buffer,
163 };
164
165 let sandbox_cfg = cfg.unwrap_or_default();
166
167 #[cfg(gdb)]
168 let debug_info = sandbox_cfg.get_guest_debug_info();
169 let mut mem_mgr_wrapper = {
170 let mut mgr = UninitializedSandbox::load_guest_binary(sandbox_cfg, &guest_binary)?;
171 let stack_guard = Self::create_stack_guard();
172 mgr.set_stack_guard(&stack_guard)?;
173 MemMgrWrapper::new(mgr, stack_guard)
174 };
175
176 mem_mgr_wrapper.write_memory_layout()?;
177
178 let host_funcs = Arc::new(Mutex::new(FunctionRegistry::default()));
179
180 let mut sandbox = Self {
181 host_funcs,
182 mgr: mem_mgr_wrapper,
183 max_initialization_time: Duration::from_millis(
184 sandbox_cfg.get_max_initialization_time() as u64,
185 ),
186 max_execution_time: Duration::from_millis(sandbox_cfg.get_max_execution_time() as u64),
187 max_wait_for_cancellation: Duration::from_millis(
188 sandbox_cfg.get_max_wait_for_cancellation() as u64,
189 ),
190 max_guest_log_level: None,
191 #[cfg(gdb)]
192 debug_info,
193 };
194
195 sandbox.register_print(default_writer_func)?;
197
198 crate::debug!("Sandbox created: {:#?}", sandbox);
199
200 Ok(sandbox)
201 }
202
203 #[instrument(skip_all, parent = Span::current(), level = "Trace")]
204 fn create_stack_guard() -> [u8; STACK_COOKIE_LEN] {
205 rand::random::<[u8; STACK_COOKIE_LEN]>()
206 }
207
208 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
219 pub(super) fn load_guest_binary(
220 cfg: SandboxConfiguration,
221 guest_binary: &GuestBinary,
222 ) -> Result<SandboxMemoryManager<ExclusiveSharedMemory>> {
223 let mut exe_info = match guest_binary {
224 GuestBinary::FilePath(bin_path_str) => ExeInfo::from_file(bin_path_str)?,
225 GuestBinary::Buffer(buffer) => ExeInfo::from_buf(buffer)?,
226 };
227
228 SandboxMemoryManager::load_guest_binary_into_memory(cfg, &mut exe_info)
229 }
230
231 pub fn set_max_guest_log_level(&mut self, log_level: LevelFilter) {
235 self.max_guest_log_level = Some(log_level);
236 }
237
238 pub fn register<Args: ParameterTuple, Output: SupportedReturnType>(
240 &mut self,
241 name: impl AsRef<str>,
242 host_func: impl Into<HostFunction<Output, Args>>,
243 ) -> Result<()> {
244 register_host_function(host_func, self, name.as_ref(), None)
245 }
246
247 #[cfg(all(feature = "seccomp", target_os = "linux"))]
251 pub fn register_with_extra_allowed_syscalls<
252 Args: ParameterTuple,
253 Output: SupportedReturnType,
254 >(
255 &mut self,
256 name: impl AsRef<str>,
257 host_func: impl Into<HostFunction<Output, Args>>,
258 extra_allowed_syscalls: impl IntoIterator<Item = crate::sandbox::ExtraAllowedSyscall>,
259 ) -> Result<()> {
260 let extra_allowed_syscalls: Vec<_> = extra_allowed_syscalls.into_iter().collect();
261 register_host_function(host_func, self, name.as_ref(), Some(extra_allowed_syscalls))
262 }
263
264 pub fn register_print(
269 &mut self,
270 print_func: impl Into<HostFunction<i32, (String,)>>,
271 ) -> Result<()> {
272 #[cfg(not(all(target_os = "linux", feature = "seccomp")))]
273 self.register("HostPrint", print_func)?;
274
275 #[cfg(all(target_os = "linux", feature = "seccomp"))]
276 self.register_with_extra_allowed_syscalls(
277 "HostPrint",
278 print_func,
279 EXTRA_ALLOWED_SYSCALLS_FOR_WRITER_FUNC.iter().copied(),
280 )?;
281
282 Ok(())
283 }
284
285 #[cfg(all(feature = "seccomp", target_os = "linux"))]
292 pub fn register_print_with_extra_allowed_syscalls(
293 &mut self,
294 print_func: impl Into<HostFunction<i32, (String,)>>,
295 extra_allowed_syscalls: impl IntoIterator<Item = crate::sandbox::ExtraAllowedSyscall>,
296 ) -> Result<()> {
297 #[cfg(all(target_os = "linux", feature = "seccomp"))]
298 self.register_with_extra_allowed_syscalls(
299 "HostPrint",
300 print_func,
301 EXTRA_ALLOWED_SYSCALLS_FOR_WRITER_FUNC
302 .iter()
303 .copied()
304 .chain(extra_allowed_syscalls),
305 )?;
306
307 Ok(())
308 }
309}
310#[cfg(target_os = "windows")]
313fn check_windows_version() -> Result<()> {
314 use windows_version::{is_server, OsVersion};
315 const WINDOWS_MAJOR: u32 = 10;
316 const WINDOWS_MINOR: u32 = 0;
317 const WINDOWS_PACK: u32 = 0;
318
319 if is_server() {
321 if OsVersion::current() < OsVersion::new(WINDOWS_MAJOR, WINDOWS_MINOR, WINDOWS_PACK, 20348)
322 {
323 return Err(new_error!(
324 "Hyperlight Requires Windows Server 2022 or newer"
325 ));
326 }
327 } else if OsVersion::current()
328 < OsVersion::new(WINDOWS_MAJOR, WINDOWS_MINOR, WINDOWS_PACK, 22000)
329 {
330 return Err(new_error!("Hyperlight Requires Windows 11 or newer"));
331 }
332 Ok(())
333}
334
335#[cfg(test)]
336mod tests {
337 use std::sync::mpsc::channel;
338 use std::sync::Arc;
339 use std::time::Duration;
340 use std::{fs, thread};
341
342 use crossbeam_queue::ArrayQueue;
343 use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterValue, ReturnValue};
344 use hyperlight_testing::simple_guest_as_string;
345
346 use crate::sandbox::uninitialized::GuestBinary;
347 use crate::sandbox::SandboxConfiguration;
348 use crate::sandbox_state::sandbox::EvolvableSandbox;
349 use crate::sandbox_state::transition::Noop;
350 use crate::{new_error, MultiUseSandbox, Result, UninitializedSandbox};
351
352 #[test]
353 fn test_new_sandbox() {
354 let binary_path = simple_guest_as_string().unwrap();
357 let sandbox = UninitializedSandbox::new(GuestBinary::FilePath(binary_path.clone()), None);
358 assert!(sandbox.is_ok());
359
360 let mut binary_path_does_not_exist = binary_path.clone();
363 binary_path_does_not_exist.push_str(".nonexistent");
364 let uninitialized_sandbox =
365 UninitializedSandbox::new(GuestBinary::FilePath(binary_path_does_not_exist), None);
366 assert!(uninitialized_sandbox.is_err());
367
368 let cfg = {
370 let mut cfg = SandboxConfiguration::default();
371 cfg.set_input_data_size(0x1000);
372 cfg.set_output_data_size(0x1000);
373 cfg.set_stack_size(0x1000);
374 cfg.set_heap_size(0x1000);
375 cfg.set_max_execution_time(Duration::from_millis(1001));
376 cfg.set_max_execution_cancel_wait_time(Duration::from_millis(9));
377 Some(cfg)
378 };
379
380 let uninitialized_sandbox =
381 UninitializedSandbox::new(GuestBinary::FilePath(binary_path.clone()), cfg);
382 assert!(uninitialized_sandbox.is_ok());
383
384 let uninitialized_sandbox =
385 UninitializedSandbox::new(GuestBinary::FilePath(binary_path), None).unwrap();
386
387 let _sandbox: MultiUseSandbox = uninitialized_sandbox.evolve(Noop::default()).unwrap();
390
391 let binary_path = simple_guest_as_string().unwrap();
394 let sandbox =
395 UninitializedSandbox::new(GuestBinary::Buffer(fs::read(binary_path).unwrap()), None);
396 assert!(sandbox.is_ok());
397
398 let binary_path = simple_guest_as_string().unwrap();
401 let mut bytes = fs::read(binary_path).unwrap();
402 let _ = bytes.split_off(100);
403 let sandbox = UninitializedSandbox::new(GuestBinary::Buffer(bytes), None);
404 assert!(sandbox.is_err());
405 }
406
407 #[test]
408 fn test_load_guest_binary_manual() {
409 let cfg = SandboxConfiguration::default();
410
411 let simple_guest_path = simple_guest_as_string().unwrap();
412
413 UninitializedSandbox::load_guest_binary(cfg, &GuestBinary::FilePath(simple_guest_path))
414 .unwrap();
415 }
416
417 #[test]
418 fn test_host_functions() {
419 let uninitialized_sandbox = || {
420 UninitializedSandbox::new(
421 GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
422 None,
423 )
424 .unwrap()
425 };
426
427 {
429 let mut usbox = uninitialized_sandbox();
430
431 usbox.register("test0", |arg: i32| Ok(arg + 1)).unwrap();
432
433 let sandbox: Result<MultiUseSandbox> = usbox.evolve(Noop::default());
434 assert!(sandbox.is_ok());
435 let sandbox = sandbox.unwrap();
436
437 let host_funcs = sandbox
438 ._host_funcs
439 .try_lock()
440 .map_err(|_| new_error!("Error locking"));
441
442 assert!(host_funcs.is_ok());
443
444 let res = host_funcs
445 .unwrap()
446 .call_host_function("test0", vec![ParameterValue::Int(1)])
447 .unwrap();
448
449 assert_eq!(res, ReturnValue::Int(2));
450 }
451
452 {
454 let mut usbox = uninitialized_sandbox();
455
456 usbox.register("test1", |a: i32, b: i32| Ok(a + b)).unwrap();
457
458 let sandbox: Result<MultiUseSandbox> = usbox.evolve(Noop::default());
459 assert!(sandbox.is_ok());
460 let sandbox = sandbox.unwrap();
461
462 let host_funcs = sandbox
463 ._host_funcs
464 .try_lock()
465 .map_err(|_| new_error!("Error locking"));
466
467 assert!(host_funcs.is_ok());
468
469 let res = host_funcs
470 .unwrap()
471 .call_host_function(
472 "test1",
473 vec![ParameterValue::Int(1), ParameterValue::Int(2)],
474 )
475 .unwrap();
476
477 assert_eq!(res, ReturnValue::Int(3));
478 }
479
480 {
482 let mut usbox = uninitialized_sandbox();
483
484 usbox
485 .register("test2", |msg: String| {
486 println!("test2 called: {}", msg);
487 Ok(())
488 })
489 .unwrap();
490
491 let sandbox: Result<MultiUseSandbox> = usbox.evolve(Noop::default());
492 assert!(sandbox.is_ok());
493 let sandbox = sandbox.unwrap();
494
495 let host_funcs = sandbox
496 ._host_funcs
497 .try_lock()
498 .map_err(|_| new_error!("Error locking"));
499
500 assert!(host_funcs.is_ok());
501
502 let res = host_funcs.unwrap().call_host_function("test2", vec![]);
503 assert!(res.is_err());
504 }
505
506 {
508 let usbox = uninitialized_sandbox();
509 let sandbox: Result<MultiUseSandbox> = usbox.evolve(Noop::default());
510 assert!(sandbox.is_ok());
511 let sandbox = sandbox.unwrap();
512
513 let host_funcs = sandbox
514 ._host_funcs
515 .try_lock()
516 .map_err(|_| new_error!("Error locking"));
517
518 assert!(host_funcs.is_ok());
519
520 let res = host_funcs.unwrap().call_host_function("test4", vec![]);
521 assert!(res.is_err());
522 }
523 }
524
525 #[test]
526 fn test_host_print() {
527 let (tx, rx) = channel();
532
533 let writer = move |msg| {
534 let _ = tx.send(msg);
535 Ok(0)
536 };
537
538 let mut sandbox = UninitializedSandbox::new(
539 GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
540 None,
541 )
542 .expect("Failed to create sandbox");
543
544 sandbox
545 .register_print(writer)
546 .expect("Failed to register host print function");
547
548 let host_funcs = sandbox
549 .host_funcs
550 .try_lock()
551 .map_err(|_| new_error!("Error locking"));
552
553 assert!(host_funcs.is_ok());
554
555 host_funcs.unwrap().host_print("test".to_string()).unwrap();
556
557 drop(sandbox);
558
559 let received_msgs: Vec<_> = rx.into_iter().collect();
560 assert_eq!(received_msgs, ["test"]);
561
562 fn fn_writer(msg: String) -> Result<i32> {
623 assert_eq!(msg, "test2");
624 Ok(0)
625 }
626
627 let mut sandbox = UninitializedSandbox::new(
628 GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
629 None,
630 )
631 .expect("Failed to create sandbox");
632
633 sandbox
634 .register_print(fn_writer)
635 .expect("Failed to register host print function");
636
637 let host_funcs = sandbox
638 .host_funcs
639 .try_lock()
640 .map_err(|_| new_error!("Error locking"));
641
642 assert!(host_funcs.is_ok());
643
644 host_funcs.unwrap().host_print("test2".to_string()).unwrap();
645
646 let mut test_host_print = TestHostPrint::new();
649
650 let writer_closure = move |s| test_host_print.write(s);
653
654 let mut sandbox = UninitializedSandbox::new(
655 GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
656 None,
657 )
658 .expect("Failed to create sandbox");
659
660 sandbox
661 .register_print(writer_closure)
662 .expect("Failed to register host print function");
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("test3".to_string()).unwrap();
672 }
673
674 struct TestHostPrint {}
675
676 impl TestHostPrint {
677 fn new() -> Self {
678 TestHostPrint {}
679 }
680
681 fn write(&mut self, msg: String) -> Result<i32> {
682 assert_eq!(msg, "test3");
683 Ok(0)
684 }
685 }
686
687 #[test]
688 fn check_create_and_use_sandbox_on_different_threads() {
689 let unintializedsandbox_queue = Arc::new(ArrayQueue::<UninitializedSandbox>::new(10));
690 let sandbox_queue = Arc::new(ArrayQueue::<MultiUseSandbox>::new(10));
691
692 for i in 0..10 {
693 let simple_guest_path = simple_guest_as_string().expect("Guest Binary Missing");
694 let unintializedsandbox = {
695 let err_string = format!("failed to create UninitializedSandbox {i}");
696 let err_str = err_string.as_str();
697 UninitializedSandbox::new(GuestBinary::FilePath(simple_guest_path), None)
698 .expect(err_str)
699 };
700
701 {
702 let err_string = format!("Failed to push UninitializedSandbox {i}");
703 let err_str = err_string.as_str();
704
705 unintializedsandbox_queue
706 .push(unintializedsandbox)
707 .expect(err_str);
708 }
709 }
710
711 let thread_handles = (0..10)
712 .map(|i| {
713 let uq = unintializedsandbox_queue.clone();
714 let sq = sandbox_queue.clone();
715 thread::spawn(move || {
716 let uninitialized_sandbox = uq.pop().unwrap_or_else(|| {
717 panic!("Failed to pop UninitializedSandbox thread {}", i)
718 });
719
720 let host_funcs = uninitialized_sandbox
721 .host_funcs
722 .try_lock()
723 .map_err(|_| new_error!("Error locking"));
724
725 assert!(host_funcs.is_ok());
726
727 host_funcs
728 .unwrap()
729 .host_print(format!("Print from UninitializedSandbox on Thread {}\n", i))
730 .unwrap();
731
732 let sandbox = uninitialized_sandbox
733 .evolve(Noop::default())
734 .unwrap_or_else(|_| {
735 panic!("Failed to initialize UninitializedSandbox thread {}", i)
736 });
737
738 sq.push(sandbox).unwrap_or_else(|_| {
739 panic!("Failed to push UninitializedSandbox thread {}", i)
740 })
741 })
742 })
743 .collect::<Vec<_>>();
744
745 for handle in thread_handles {
746 handle.join().unwrap();
747 }
748
749 let thread_handles = (0..10)
750 .map(|i| {
751 let sq = sandbox_queue.clone();
752 thread::spawn(move || {
753 let sandbox = sq
754 .pop()
755 .unwrap_or_else(|| panic!("Failed to pop Sandbox thread {}", i));
756
757 let host_funcs = sandbox
758 ._host_funcs
759 .try_lock()
760 .map_err(|_| new_error!("Error locking"));
761
762 assert!(host_funcs.is_ok());
763
764 host_funcs
765 .unwrap()
766 .host_print(format!("Print from Sandbox on Thread {}\n", i))
767 .unwrap();
768 })
769 })
770 .collect::<Vec<_>>();
771
772 for handle in thread_handles {
773 handle.join().unwrap();
774 }
775 }
776
777 #[test]
778 #[ignore]
784 #[cfg(feature = "build-metadata")]
785 fn test_trace_trace() {
786 use hyperlight_testing::logger::Logger as TestLogger;
787 use hyperlight_testing::tracing_subscriber::TracingSubscriber as TestSubscriber;
788 use serde_json::{Map, Value};
789 use tracing::Level as tracing_level;
790 use tracing_core::callsite::rebuild_interest_cache;
791 use tracing_core::Subscriber;
792 use uuid::Uuid;
793
794 use crate::testing::log_values::build_metadata_testing::try_to_strings;
795 use crate::testing::log_values::test_value_as_str;
796
797 TestLogger::initialize_log_tracer();
798 rebuild_interest_cache();
799 let subscriber = TestSubscriber::new(tracing_level::TRACE);
800 tracing::subscriber::with_default(subscriber.clone(), || {
801 let correlation_id = Uuid::new_v4().as_hyphenated().to_string();
802 let span = tracing::error_span!("test_trace_logs", correlation_id).entered();
803
804 let current_span = subscriber.current_span();
807 assert!(current_span.is_known(), "Current span is unknown");
808 let current_span_metadata = current_span.into_inner().unwrap();
809 assert_eq!(
810 current_span_metadata.0.into_u64(),
811 1,
812 "Current span is not span 1"
813 );
814 assert_eq!(current_span_metadata.1.name(), "test_trace_logs");
815
816 let span_data = subscriber.get_span(1);
819 let span_attributes: &Map<String, Value> = span_data
820 .get("span")
821 .unwrap()
822 .get("attributes")
823 .unwrap()
824 .as_object()
825 .unwrap();
826
827 test_value_as_str(span_attributes, "correlation_id", correlation_id.as_str());
828
829 let mut binary_path = simple_guest_as_string().unwrap();
830 binary_path.push_str("does_not_exist");
831
832 let sbox = UninitializedSandbox::new(GuestBinary::FilePath(binary_path), None);
833 assert!(sbox.is_err());
834
835 let current_span = subscriber.current_span();
838 assert!(current_span.is_known(), "Current span is unknown");
839 let current_span_metadata = current_span.into_inner().unwrap();
840 assert_eq!(
841 current_span_metadata.0.into_u64(),
842 1,
843 "Current span is not span 1"
844 );
845
846 let span_metadata = subscriber.get_span_metadata(2);
847 assert_eq!(span_metadata.name(), "new");
848
849 let events = subscriber.get_events();
852 assert_eq!(events.len(), 15);
853
854 let mut count_matching_events = 0;
855
856 for json_value in events {
857 let event_values = json_value.as_object().unwrap().get("event").unwrap();
858 let metadata_values_map =
859 event_values.get("metadata").unwrap().as_object().unwrap();
860 let event_values_map = event_values.as_object().unwrap();
861
862 let expected_error_start = "Error(\"GuestBinary not found:";
863
864 let err_vals_res = try_to_strings([
865 (metadata_values_map, "level"),
866 (event_values_map, "error"),
867 (metadata_values_map, "module_path"),
868 (metadata_values_map, "target"),
869 ]);
870 if let Ok(err_vals) = err_vals_res {
871 if err_vals[0] == "ERROR"
872 && err_vals[1].starts_with(expected_error_start)
873 && err_vals[2] == "hyperlight_host::sandbox::uninitialized"
874 && err_vals[3] == "hyperlight_host::sandbox::uninitialized"
875 {
876 count_matching_events += 1;
877 }
878 }
879 }
880 assert!(
881 count_matching_events == 1,
882 "Unexpected number of matching events {}",
883 count_matching_events
884 );
885 span.exit();
886 subscriber.clear();
887 });
888 }
889
890 #[test]
891 #[ignore]
892 #[cfg(feature = "build-metadata")]
895 fn test_log_trace() {
896 use std::path::PathBuf;
897
898 use hyperlight_testing::logger::{Logger as TestLogger, LOGGER as TEST_LOGGER};
899 use log::Level;
900 use tracing_core::callsite::rebuild_interest_cache;
901
902 {
903 TestLogger::initialize_test_logger();
904 TEST_LOGGER.set_max_level(log::LevelFilter::Trace);
905
906 rebuild_interest_cache();
910
911 let mut invalid_binary_path = simple_guest_as_string().unwrap();
912 invalid_binary_path.push_str("does_not_exist");
913
914 let sbox = UninitializedSandbox::new(GuestBinary::FilePath(invalid_binary_path), None);
915 assert!(sbox.is_err());
916
917 let num_calls = TEST_LOGGER.num_log_calls();
931 assert_eq!(19, num_calls);
932
933 let logcall = TEST_LOGGER.get_log_call(0).unwrap();
936 assert_eq!(Level::Info, logcall.level);
937
938 assert!(logcall.args.starts_with("new; cfg"));
939 assert_eq!("hyperlight_host::sandbox::uninitialized", logcall.target);
940
941 let logcall = TEST_LOGGER.get_log_call(1).unwrap();
944 assert_eq!(Level::Trace, logcall.level);
945 assert_eq!(logcall.args, "-> new;");
946 assert_eq!("tracing::span::active", logcall.target);
947
948 let logcall = TEST_LOGGER.get_log_call(16).unwrap();
951 assert_eq!(Level::Error, logcall.level);
952 assert!(logcall
953 .args
954 .starts_with("error=Error(\"GuestBinary not found:"));
955 assert_eq!("hyperlight_host::sandbox::uninitialized", logcall.target);
956
957 let logcall = TEST_LOGGER.get_log_call(17).unwrap();
960 assert_eq!(Level::Trace, logcall.level);
961 assert_eq!(logcall.args, "<- new;");
962 assert_eq!("tracing::span::active", logcall.target);
963
964 let logcall = TEST_LOGGER.get_log_call(18).unwrap();
967 assert_eq!(Level::Trace, logcall.level);
968 assert_eq!(logcall.args, "-- new;");
969 assert_eq!("tracing::span", logcall.target);
970 }
971 {
972 TEST_LOGGER.clear_log_calls();
974 TEST_LOGGER.set_max_level(log::LevelFilter::Info);
975
976 let mut valid_binary_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
977 valid_binary_path.push("src");
978 valid_binary_path.push("sandbox");
979 valid_binary_path.push("initialized.rs");
980
981 let sbox = UninitializedSandbox::new(
982 GuestBinary::FilePath(valid_binary_path.into_os_string().into_string().unwrap()),
983 None,
984 );
985 assert!(sbox.is_err());
986
987 let num_calls = TEST_LOGGER.num_log_calls();
990 assert_eq!(2, num_calls);
991
992 let logcall = TEST_LOGGER.get_log_call(0).unwrap();
995 assert_eq!(Level::Info, logcall.level);
996
997 assert!(logcall.args.starts_with("new; cfg"));
998 assert_eq!("hyperlight_host::sandbox::uninitialized", logcall.target);
999
1000 let logcall = TEST_LOGGER.get_log_call(1).unwrap();
1003 assert_eq!(Level::Error, logcall.level);
1004 assert!(logcall
1005 .args
1006 .starts_with("error=Error(\"GuestBinary not found:"));
1007 assert_eq!("hyperlight_host::sandbox::uninitialized", logcall.target);
1008 }
1009 {
1010 TEST_LOGGER.clear_log_calls();
1011 TEST_LOGGER.set_max_level(log::LevelFilter::Error);
1012
1013 let sbox = {
1014 let res = UninitializedSandbox::new(
1015 GuestBinary::FilePath(simple_guest_as_string().unwrap()),
1016 None,
1017 );
1018 res.unwrap()
1019 };
1020 let _: Result<MultiUseSandbox> = sbox.evolve(Noop::default());
1021
1022 let num_calls = TEST_LOGGER.num_log_calls();
1023
1024 assert_eq!(0, num_calls);
1025 }
1026 }
1027
1028 #[test]
1029 fn test_invalid_path() {
1030 let invalid_path = "some/path/that/does/not/exist";
1031 let sbox = UninitializedSandbox::new(GuestBinary::FilePath(invalid_path.to_string()), None);
1032 println!("{:?}", sbox);
1033 #[cfg(target_os = "windows")]
1034 assert!(
1035 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)"))
1036 );
1037 #[cfg(target_os = "linux")]
1038 assert!(
1039 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)"))
1040 );
1041 }
1042}