1use std::fmt::Debug;
18use std::option::Option;
19use std::path::Path;
20use std::sync::{Arc, Mutex};
21
22use log::LevelFilter;
23use tracing::{Span, instrument};
24
25use super::host_funcs::{FunctionRegistry, default_writer_func};
26use super::mem_mgr::MemMgrWrapper;
27use super::uninitialized_evolve::evolve_impl_multi_use;
28use crate::func::host_functions::{HostFunction, register_host_function};
29use crate::func::{ParameterTuple, SupportedReturnType};
30#[cfg(feature = "build-metadata")]
31use crate::log_build_details;
32use crate::mem::exe::ExeInfo;
33use crate::mem::memory_region::{DEFAULT_GUEST_BLOB_MEM_FLAGS, MemoryRegionFlags};
34use crate::mem::mgr::{STACK_COOKIE_LEN, SandboxMemoryManager};
35use crate::mem::shared_mem::ExclusiveSharedMemory;
36use crate::sandbox::SandboxConfiguration;
37use crate::{MultiUseSandbox, Result, new_error};
38
39#[cfg(all(target_os = "linux", feature = "seccomp"))]
40const EXTRA_ALLOWED_SYSCALLS_FOR_WRITER_FUNC: &[super::ExtraAllowedSyscall] = &[
41 libc::SYS_mmap,
48 libc::SYS_brk,
49 libc::SYS_mprotect,
50 #[cfg(mshv)]
51 libc::SYS_close,
52];
53
54#[cfg(any(crashdump, gdb))]
55#[derive(Clone, Debug, Default)]
56pub(crate) struct SandboxRuntimeConfig {
57 #[cfg(crashdump)]
58 pub(crate) binary_path: Option<String>,
59 #[cfg(gdb)]
60 pub(crate) debug_info: Option<super::config::DebugInfo>,
61 #[cfg(crashdump)]
62 pub(crate) guest_core_dump: bool,
63}
64
65pub struct UninitializedSandbox {
77 pub(crate) host_funcs: Arc<Mutex<FunctionRegistry>>,
79 pub(crate) mgr: MemMgrWrapper<ExclusiveSharedMemory>,
81 pub(crate) max_guest_log_level: Option<LevelFilter>,
82 pub(crate) config: SandboxConfiguration,
83 #[cfg(any(crashdump, gdb))]
84 pub(crate) rt_cfg: SandboxRuntimeConfig,
85 pub(crate) load_info: crate::mem::exe::LoadInfo,
86}
87
88impl Debug for UninitializedSandbox {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 f.debug_struct("UninitializedSandbox")
91 .field("memory_layout", &self.mgr.unwrap_mgr().layout)
92 .finish()
93 }
94}
95
96impl UninitializedSandbox {
97 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
103 pub fn evolve(self) -> Result<MultiUseSandbox> {
104 evolve_impl_multi_use(self)
105 }
106}
107
108#[derive(Debug)]
110pub enum GuestBinary<'a> {
111 Buffer(&'a [u8]),
113 FilePath(String),
115}
116
117#[derive(Debug)]
119pub struct GuestBlob<'a> {
120 pub data: &'a [u8],
122 pub permissions: MemoryRegionFlags,
125}
126
127impl<'a> From<&'a [u8]> for GuestBlob<'a> {
128 fn from(data: &'a [u8]) -> Self {
129 GuestBlob {
130 data,
131 permissions: DEFAULT_GUEST_BLOB_MEM_FLAGS,
132 }
133 }
134}
135
136#[derive(Debug)]
141pub struct GuestEnvironment<'a, 'b> {
142 pub guest_binary: GuestBinary<'a>,
144 pub init_data: Option<GuestBlob<'b>>,
146}
147
148impl<'a, 'b> GuestEnvironment<'a, 'b> {
149 pub fn new(guest_binary: GuestBinary<'a>, init_data: Option<&'b [u8]>) -> Self {
151 GuestEnvironment {
152 guest_binary,
153 init_data: init_data.map(GuestBlob::from),
154 }
155 }
156}
157
158impl<'a> From<GuestBinary<'a>> for GuestEnvironment<'a, '_> {
159 fn from(guest_binary: GuestBinary<'a>) -> Self {
160 GuestEnvironment {
161 guest_binary,
162 init_data: None,
163 }
164 }
165}
166
167impl UninitializedSandbox {
168 #[instrument(
175 err(Debug),
176 skip(env),
177 parent = Span::current()
178 )]
179 pub fn new<'a, 'b>(
180 env: impl Into<GuestEnvironment<'a, 'b>>,
181 cfg: Option<SandboxConfiguration>,
182 ) -> Result<Self> {
183 #[cfg(feature = "build-metadata")]
184 log_build_details();
185
186 #[cfg(target_os = "windows")]
188 check_windows_version()?;
189
190 let env: GuestEnvironment<'_, '_> = env.into();
191 let guest_binary = env.guest_binary;
192 let guest_blob = env.init_data;
193
194 let guest_binary = match guest_binary {
196 GuestBinary::FilePath(binary_path) => {
197 let path = Path::new(&binary_path)
198 .canonicalize()
199 .map_err(|e| new_error!("GuestBinary not found: '{}': {}", binary_path, e))?
200 .into_os_string()
201 .into_string()
202 .map_err(|e| new_error!("Error converting OsString to String: {:?}", e))?;
203
204 GuestBinary::FilePath(path)
205 }
206 buffer @ GuestBinary::Buffer(_) => buffer,
207 };
208
209 let sandbox_cfg = cfg.unwrap_or_default();
210
211 #[cfg(any(crashdump, gdb))]
212 let rt_cfg = {
213 #[cfg(crashdump)]
214 let guest_core_dump = sandbox_cfg.get_guest_core_dump();
215
216 #[cfg(gdb)]
217 let debug_info = sandbox_cfg.get_guest_debug_info();
218
219 #[cfg(crashdump)]
220 let binary_path = if let GuestBinary::FilePath(ref path) = guest_binary {
221 Some(path.clone())
222 } else {
223 None
224 };
225
226 SandboxRuntimeConfig {
227 #[cfg(crashdump)]
228 binary_path,
229 #[cfg(gdb)]
230 debug_info,
231 #[cfg(crashdump)]
232 guest_core_dump,
233 }
234 };
235
236 let (mut mem_mgr_wrapper, load_info) = {
237 let (mut mgr, load_info) = UninitializedSandbox::load_guest_binary(
238 sandbox_cfg,
239 &guest_binary,
240 guest_blob.as_ref(),
241 )?;
242
243 let stack_guard = Self::create_stack_guard();
244 mgr.set_stack_guard(&stack_guard)?;
245 (MemMgrWrapper::new(mgr, stack_guard), load_info)
246 };
247
248 mem_mgr_wrapper.write_memory_layout()?;
249
250 if let Some(blob) = guest_blob {
252 mem_mgr_wrapper.write_init_data(blob.data)?;
253 }
254
255 let host_funcs = Arc::new(Mutex::new(FunctionRegistry::default()));
256
257 let mut sandbox = Self {
258 host_funcs,
259 mgr: mem_mgr_wrapper,
260 max_guest_log_level: None,
261 config: sandbox_cfg,
262 #[cfg(any(crashdump, gdb))]
263 rt_cfg,
264 load_info,
265 };
266
267 sandbox.register_print(default_writer_func)?;
269
270 crate::debug!("Sandbox created: {:#?}", sandbox);
271
272 Ok(sandbox)
273 }
274
275 #[instrument(skip_all, parent = Span::current(), level = "Trace")]
276 fn create_stack_guard() -> [u8; STACK_COOKIE_LEN] {
277 rand::random::<[u8; STACK_COOKIE_LEN]>()
278 }
279
280 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
291 pub(super) fn load_guest_binary(
292 cfg: SandboxConfiguration,
293 guest_binary: &GuestBinary,
294 guest_blob: Option<&GuestBlob>,
295 ) -> Result<(
296 SandboxMemoryManager<ExclusiveSharedMemory>,
297 crate::mem::exe::LoadInfo,
298 )> {
299 let exe_info = match guest_binary {
300 GuestBinary::FilePath(bin_path_str) => ExeInfo::from_file(bin_path_str)?,
301 GuestBinary::Buffer(buffer) => ExeInfo::from_buf(buffer)?,
302 };
303
304 SandboxMemoryManager::load_guest_binary_into_memory(cfg, exe_info, guest_blob)
305 }
306
307 pub fn set_max_guest_log_level(&mut self, log_level: LevelFilter) {
312 self.max_guest_log_level = Some(log_level);
313 }
314
315 pub fn register<Args: ParameterTuple, Output: SupportedReturnType>(
317 &mut self,
318 name: impl AsRef<str>,
319 host_func: impl Into<HostFunction<Output, Args>>,
320 ) -> Result<()> {
321 register_host_function(host_func, self, name.as_ref(), None)
322 }
323
324 #[cfg(all(feature = "seccomp", target_os = "linux"))]
329 pub fn register_with_extra_allowed_syscalls<
330 Args: ParameterTuple,
331 Output: SupportedReturnType,
332 >(
333 &mut self,
334 name: impl AsRef<str>,
335 host_func: impl Into<HostFunction<Output, Args>>,
336 extra_allowed_syscalls: impl IntoIterator<Item = crate::sandbox::ExtraAllowedSyscall>,
337 ) -> Result<()> {
338 let extra_allowed_syscalls: Vec<_> = extra_allowed_syscalls.into_iter().collect();
339 register_host_function(host_func, self, name.as_ref(), Some(extra_allowed_syscalls))
340 }
341
342 pub fn register_print(
348 &mut self,
349 print_func: impl Into<HostFunction<i32, (String,)>>,
350 ) -> Result<()> {
351 #[cfg(not(all(target_os = "linux", feature = "seccomp")))]
352 self.register("HostPrint", print_func)?;
353
354 #[cfg(all(target_os = "linux", feature = "seccomp"))]
355 self.register_with_extra_allowed_syscalls(
356 "HostPrint",
357 print_func,
358 EXTRA_ALLOWED_SYSCALLS_FOR_WRITER_FUNC.iter().copied(),
359 )?;
360
361 Ok(())
362 }
363
364 #[cfg(all(feature = "seccomp", target_os = "linux"))]
369 pub fn register_print_with_extra_allowed_syscalls(
370 &mut self,
371 print_func: impl Into<HostFunction<i32, (String,)>>,
372 extra_allowed_syscalls: impl IntoIterator<Item = crate::sandbox::ExtraAllowedSyscall>,
373 ) -> Result<()> {
374 #[cfg(all(target_os = "linux", feature = "seccomp"))]
375 self.register_with_extra_allowed_syscalls(
376 "HostPrint",
377 print_func,
378 EXTRA_ALLOWED_SYSCALLS_FOR_WRITER_FUNC
379 .iter()
380 .copied()
381 .chain(extra_allowed_syscalls),
382 )?;
383
384 Ok(())
385 }
386}
387#[cfg(target_os = "windows")]
390fn check_windows_version() -> Result<()> {
391 use windows_version::{OsVersion, is_server};
392 const WINDOWS_MAJOR: u32 = 10;
393 const WINDOWS_MINOR: u32 = 0;
394 const WINDOWS_PACK: u32 = 0;
395
396 if is_server() {
398 if OsVersion::current() < OsVersion::new(WINDOWS_MAJOR, WINDOWS_MINOR, WINDOWS_PACK, 20348)
399 {
400 return Err(new_error!(
401 "Hyperlight Requires Windows Server 2022 or newer"
402 ));
403 }
404 } else if OsVersion::current()
405 < OsVersion::new(WINDOWS_MAJOR, WINDOWS_MINOR, WINDOWS_PACK, 22000)
406 {
407 return Err(new_error!("Hyperlight Requires Windows 11 or newer"));
408 }
409 Ok(())
410}
411
412#[cfg(test)]
413mod tests {
414 use std::sync::Arc;
415 use std::sync::mpsc::channel;
416 use std::{fs, thread};
417
418 use crossbeam_queue::ArrayQueue;
419 use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterValue, ReturnValue};
420 use hyperlight_testing::simple_guest_as_string;
421
422 use crate::sandbox::SandboxConfiguration;
423 use crate::sandbox::uninitialized::{GuestBinary, GuestEnvironment};
424 use crate::{MultiUseSandbox, Result, UninitializedSandbox, new_error};
425
426 #[test]
427 fn test_load_extra_blob() {
428 let binary_path = simple_guest_as_string().unwrap();
429 let buffer = [0xde, 0xad, 0xbe, 0xef];
430 let guest_env =
431 GuestEnvironment::new(GuestBinary::FilePath(binary_path.clone()), Some(&buffer));
432
433 let uninitialized_sandbox = UninitializedSandbox::new(guest_env, None).unwrap();
434 let mut sandbox: MultiUseSandbox = uninitialized_sandbox.evolve().unwrap();
435
436 let res = sandbox
437 .call::<Vec<u8>>("ReadFromUserMemory", (4u64, buffer.to_vec()))
438 .expect("Failed to call ReadFromUserMemory");
439
440 assert_eq!(res, buffer.to_vec());
441 }
442
443 #[test]
444 fn test_new_sandbox() {
445 let binary_path = simple_guest_as_string().unwrap();
448 let sandbox = UninitializedSandbox::new(GuestBinary::FilePath(binary_path.clone()), None);
449 assert!(sandbox.is_ok());
450
451 let mut binary_path_does_not_exist = binary_path.clone();
454 binary_path_does_not_exist.push_str(".nonexistent");
455 let uninitialized_sandbox =
456 UninitializedSandbox::new(GuestBinary::FilePath(binary_path_does_not_exist), None);
457 assert!(uninitialized_sandbox.is_err());
458
459 let cfg = {
461 let mut cfg = SandboxConfiguration::default();
462 cfg.set_input_data_size(0x1000);
463 cfg.set_output_data_size(0x1000);
464 cfg.set_stack_size(0x1000);
465 cfg.set_heap_size(0x1000);
466 Some(cfg)
467 };
468
469 let uninitialized_sandbox =
470 UninitializedSandbox::new(GuestBinary::FilePath(binary_path.clone()), cfg);
471 assert!(uninitialized_sandbox.is_ok());
472
473 let uninitialized_sandbox =
474 UninitializedSandbox::new(GuestBinary::FilePath(binary_path), None).unwrap();
475
476 let _sandbox: MultiUseSandbox = uninitialized_sandbox.evolve().unwrap();
479
480 let binary_path = simple_guest_as_string().unwrap();
483 let sandbox =
484 UninitializedSandbox::new(GuestBinary::Buffer(&fs::read(binary_path).unwrap()), None);
485 assert!(sandbox.is_ok());
486
487 let binary_path = simple_guest_as_string().unwrap();
490 let mut bytes = fs::read(binary_path).unwrap();
491 let _ = bytes.split_off(100);
492 let sandbox = UninitializedSandbox::new(GuestBinary::Buffer(&bytes), None);
493 assert!(sandbox.is_err());
494 }
495
496 #[test]
497 fn test_load_guest_binary_manual() {
498 let cfg = SandboxConfiguration::default();
499
500 let simple_guest_path = simple_guest_as_string().unwrap();
501
502 UninitializedSandbox::load_guest_binary(
503 cfg,
504 &GuestBinary::FilePath(simple_guest_path),
505 None.as_ref(),
506 )
507 .unwrap();
508 }
509
510 #[test]
511 fn test_host_functions() {
512 let uninitialized_sandbox = || {
513 UninitializedSandbox::new(
514 GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
515 None,
516 )
517 .unwrap()
518 };
519
520 {
522 let mut usbox = uninitialized_sandbox();
523
524 usbox.register("test0", |arg: i32| Ok(arg + 1)).unwrap();
525
526 let sandbox: Result<MultiUseSandbox> = usbox.evolve();
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
549 usbox.register("test1", |a: i32, b: i32| Ok(a + b)).unwrap();
550
551 let sandbox: Result<MultiUseSandbox> = usbox.evolve();
552 assert!(sandbox.is_ok());
553 let sandbox = sandbox.unwrap();
554
555 let host_funcs = sandbox
556 ._host_funcs
557 .try_lock()
558 .map_err(|_| new_error!("Error locking"));
559
560 assert!(host_funcs.is_ok());
561
562 let res = host_funcs
563 .unwrap()
564 .call_host_function(
565 "test1",
566 vec![ParameterValue::Int(1), ParameterValue::Int(2)],
567 )
568 .unwrap();
569
570 assert_eq!(res, ReturnValue::Int(3));
571 }
572
573 {
575 let mut usbox = uninitialized_sandbox();
576
577 usbox
578 .register("test2", |msg: String| {
579 println!("test2 called: {}", msg);
580 Ok(())
581 })
582 .unwrap();
583
584 let sandbox: Result<MultiUseSandbox> = usbox.evolve();
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();
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 fn test_host_print() {
620 let (tx, rx) = channel();
625
626 let writer = move |msg| {
627 let _ = tx.send(msg);
628 Ok(0)
629 };
630
631 let mut sandbox = UninitializedSandbox::new(
632 GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
633 None,
634 )
635 .expect("Failed to create sandbox");
636
637 sandbox
638 .register_print(writer)
639 .expect("Failed to register host print function");
640
641 let host_funcs = sandbox
642 .host_funcs
643 .try_lock()
644 .map_err(|_| new_error!("Error locking"));
645
646 assert!(host_funcs.is_ok());
647
648 host_funcs.unwrap().host_print("test".to_string()).unwrap();
649
650 drop(sandbox);
651
652 let received_msgs: Vec<_> = rx.into_iter().collect();
653 assert_eq!(received_msgs, ["test"]);
654
655 fn fn_writer(msg: String) -> Result<i32> {
716 assert_eq!(msg, "test2");
717 Ok(0)
718 }
719
720 let mut sandbox = UninitializedSandbox::new(
721 GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
722 None,
723 )
724 .expect("Failed to create sandbox");
725
726 sandbox
727 .register_print(fn_writer)
728 .expect("Failed to register host print function");
729
730 let host_funcs = sandbox
731 .host_funcs
732 .try_lock()
733 .map_err(|_| new_error!("Error locking"));
734
735 assert!(host_funcs.is_ok());
736
737 host_funcs.unwrap().host_print("test2".to_string()).unwrap();
738
739 let mut test_host_print = TestHostPrint::new();
742
743 let writer_closure = move |s| test_host_print.write(s);
746
747 let mut sandbox = UninitializedSandbox::new(
748 GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
749 None,
750 )
751 .expect("Failed to create sandbox");
752
753 sandbox
754 .register_print(writer_closure)
755 .expect("Failed to register host print function");
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.unwrap().host_print("test3".to_string()).unwrap();
765 }
766
767 struct TestHostPrint {}
768
769 impl TestHostPrint {
770 fn new() -> Self {
771 TestHostPrint {}
772 }
773
774 fn write(&mut self, msg: String) -> Result<i32> {
775 assert_eq!(msg, "test3");
776 Ok(0)
777 }
778 }
779
780 #[test]
781 fn check_create_and_use_sandbox_on_different_threads() {
782 let unintializedsandbox_queue = Arc::new(ArrayQueue::<UninitializedSandbox>::new(10));
783 let sandbox_queue = Arc::new(ArrayQueue::<MultiUseSandbox>::new(10));
784
785 for i in 0..10 {
786 let simple_guest_path = simple_guest_as_string().expect("Guest Binary Missing");
787 let unintializedsandbox = {
788 let err_string = format!("failed to create UninitializedSandbox {i}");
789 let err_str = err_string.as_str();
790 UninitializedSandbox::new(GuestBinary::FilePath(simple_guest_path), None)
791 .expect(err_str)
792 };
793
794 {
795 let err_string = format!("Failed to push UninitializedSandbox {i}");
796 let err_str = err_string.as_str();
797
798 unintializedsandbox_queue
799 .push(unintializedsandbox)
800 .expect(err_str);
801 }
802 }
803
804 let thread_handles = (0..10)
805 .map(|i| {
806 let uq = unintializedsandbox_queue.clone();
807 let sq = sandbox_queue.clone();
808 thread::spawn(move || {
809 let uninitialized_sandbox = uq.pop().unwrap_or_else(|| {
810 panic!("Failed to pop UninitializedSandbox thread {}", i)
811 });
812
813 let host_funcs = uninitialized_sandbox
814 .host_funcs
815 .try_lock()
816 .map_err(|_| new_error!("Error locking"));
817
818 assert!(host_funcs.is_ok());
819
820 host_funcs
821 .unwrap()
822 .host_print(format!("Print from UninitializedSandbox on Thread {}\n", i))
823 .unwrap();
824
825 let sandbox = uninitialized_sandbox.evolve().unwrap_or_else(|_| {
826 panic!("Failed to initialize UninitializedSandbox thread {}", i)
827 });
828
829 sq.push(sandbox).unwrap_or_else(|_| {
830 panic!("Failed to push UninitializedSandbox thread {}", i)
831 })
832 })
833 })
834 .collect::<Vec<_>>();
835
836 for handle in thread_handles {
837 handle.join().unwrap();
838 }
839
840 let thread_handles = (0..10)
841 .map(|i| {
842 let sq = sandbox_queue.clone();
843 thread::spawn(move || {
844 let sandbox = sq
845 .pop()
846 .unwrap_or_else(|| panic!("Failed to pop Sandbox thread {}", i));
847
848 let host_funcs = sandbox
849 ._host_funcs
850 .try_lock()
851 .map_err(|_| new_error!("Error locking"));
852
853 assert!(host_funcs.is_ok());
854
855 host_funcs
856 .unwrap()
857 .host_print(format!("Print from Sandbox on Thread {}\n", i))
858 .unwrap();
859 })
860 })
861 .collect::<Vec<_>>();
862
863 for handle in thread_handles {
864 handle.join().unwrap();
865 }
866 }
867
868 #[test]
869 #[ignore]
875 #[cfg(feature = "build-metadata")]
876 fn test_trace_trace() {
877 use hyperlight_testing::logger::Logger as TestLogger;
878 use hyperlight_testing::tracing_subscriber::TracingSubscriber as TestSubscriber;
879 use serde_json::{Map, Value};
880 use tracing::Level as tracing_level;
881 use tracing_core::Subscriber;
882 use tracing_core::callsite::rebuild_interest_cache;
883 use uuid::Uuid;
884
885 use crate::testing::log_values::build_metadata_testing::try_to_strings;
886 use crate::testing::log_values::test_value_as_str;
887
888 TestLogger::initialize_log_tracer();
889 rebuild_interest_cache();
890 let subscriber = TestSubscriber::new(tracing_level::TRACE);
891 tracing::subscriber::with_default(subscriber.clone(), || {
892 let correlation_id = Uuid::new_v4().as_hyphenated().to_string();
893 let span = tracing::error_span!("test_trace_logs", correlation_id).entered();
894
895 let current_span = subscriber.current_span();
898 assert!(current_span.is_known(), "Current span is unknown");
899 let current_span_metadata = current_span.into_inner().unwrap();
900 assert_eq!(
901 current_span_metadata.0.into_u64(),
902 1,
903 "Current span is not span 1"
904 );
905 assert_eq!(current_span_metadata.1.name(), "test_trace_logs");
906
907 let span_data = subscriber.get_span(1);
910 let span_attributes: &Map<String, Value> = span_data
911 .get("span")
912 .unwrap()
913 .get("attributes")
914 .unwrap()
915 .as_object()
916 .unwrap();
917
918 test_value_as_str(span_attributes, "correlation_id", correlation_id.as_str());
919
920 let mut binary_path = simple_guest_as_string().unwrap();
921 binary_path.push_str("does_not_exist");
922
923 let sbox = UninitializedSandbox::new(GuestBinary::FilePath(binary_path), None);
924 assert!(sbox.is_err());
925
926 let current_span = subscriber.current_span();
929 assert!(current_span.is_known(), "Current span is unknown");
930 let current_span_metadata = current_span.into_inner().unwrap();
931 assert_eq!(
932 current_span_metadata.0.into_u64(),
933 1,
934 "Current span is not span 1"
935 );
936
937 let span_metadata = subscriber.get_span_metadata(2);
938 assert_eq!(span_metadata.name(), "new");
939
940 let events = subscriber.get_events();
943 assert_eq!(events.len(), 15);
944
945 let mut count_matching_events = 0;
946
947 for json_value in events {
948 let event_values = json_value.as_object().unwrap().get("event").unwrap();
949 let metadata_values_map =
950 event_values.get("metadata").unwrap().as_object().unwrap();
951 let event_values_map = event_values.as_object().unwrap();
952
953 let expected_error_start = "Error(\"GuestBinary not found:";
954
955 let err_vals_res = try_to_strings([
956 (metadata_values_map, "level"),
957 (event_values_map, "error"),
958 (metadata_values_map, "module_path"),
959 (metadata_values_map, "target"),
960 ]);
961 if let Ok(err_vals) = err_vals_res {
962 if err_vals[0] == "ERROR"
963 && err_vals[1].starts_with(expected_error_start)
964 && err_vals[2] == "hyperlight_host::sandbox::uninitialized"
965 && err_vals[3] == "hyperlight_host::sandbox::uninitialized"
966 {
967 count_matching_events += 1;
968 }
969 }
970 }
971 assert!(
972 count_matching_events == 1,
973 "Unexpected number of matching events {}",
974 count_matching_events
975 );
976 span.exit();
977 subscriber.clear();
978 });
979 }
980
981 #[test]
982 #[ignore]
983 #[cfg(feature = "build-metadata")]
986 fn test_log_trace() {
987 use std::path::PathBuf;
988
989 use hyperlight_testing::logger::{LOGGER as TEST_LOGGER, Logger as TestLogger};
990 use log::Level;
991 use tracing_core::callsite::rebuild_interest_cache;
992
993 {
994 TestLogger::initialize_test_logger();
995 TEST_LOGGER.set_max_level(log::LevelFilter::Trace);
996
997 rebuild_interest_cache();
1001
1002 let mut invalid_binary_path = simple_guest_as_string().unwrap();
1003 invalid_binary_path.push_str("does_not_exist");
1004
1005 let sbox = UninitializedSandbox::new(GuestBinary::FilePath(invalid_binary_path), None);
1006 assert!(sbox.is_err());
1007
1008 let num_calls = TEST_LOGGER.num_log_calls();
1022 assert_eq!(19, num_calls);
1023
1024 let logcall = TEST_LOGGER.get_log_call(0).unwrap();
1027 assert_eq!(Level::Info, logcall.level);
1028
1029 assert!(logcall.args.starts_with("new; cfg"));
1030 assert_eq!("hyperlight_host::sandbox::uninitialized", logcall.target);
1031
1032 let logcall = TEST_LOGGER.get_log_call(1).unwrap();
1035 assert_eq!(Level::Trace, logcall.level);
1036 assert_eq!(logcall.args, "-> new;");
1037 assert_eq!("tracing::span::active", logcall.target);
1038
1039 let logcall = TEST_LOGGER.get_log_call(16).unwrap();
1042 assert_eq!(Level::Error, logcall.level);
1043 assert!(
1044 logcall
1045 .args
1046 .starts_with("error=Error(\"GuestBinary not found:")
1047 );
1048 assert_eq!("hyperlight_host::sandbox::uninitialized", logcall.target);
1049
1050 let logcall = TEST_LOGGER.get_log_call(17).unwrap();
1053 assert_eq!(Level::Trace, logcall.level);
1054 assert_eq!(logcall.args, "<- new;");
1055 assert_eq!("tracing::span::active", logcall.target);
1056
1057 let logcall = TEST_LOGGER.get_log_call(18).unwrap();
1060 assert_eq!(Level::Trace, logcall.level);
1061 assert_eq!(logcall.args, "-- new;");
1062 assert_eq!("tracing::span", logcall.target);
1063 }
1064 {
1065 TEST_LOGGER.clear_log_calls();
1067 TEST_LOGGER.set_max_level(log::LevelFilter::Info);
1068
1069 let mut valid_binary_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
1070 valid_binary_path.push("src");
1071 valid_binary_path.push("sandbox");
1072 valid_binary_path.push("initialized.rs");
1073
1074 let sbox = UninitializedSandbox::new(
1075 GuestBinary::FilePath(valid_binary_path.into_os_string().into_string().unwrap()),
1076 None,
1077 );
1078 assert!(sbox.is_err());
1079
1080 let num_calls = TEST_LOGGER.num_log_calls();
1083 assert_eq!(2, num_calls);
1084
1085 let logcall = TEST_LOGGER.get_log_call(0).unwrap();
1088 assert_eq!(Level::Info, logcall.level);
1089
1090 assert!(logcall.args.starts_with("new; cfg"));
1091 assert_eq!("hyperlight_host::sandbox::uninitialized", logcall.target);
1092
1093 let logcall = TEST_LOGGER.get_log_call(1).unwrap();
1096 assert_eq!(Level::Error, logcall.level);
1097 assert!(
1098 logcall
1099 .args
1100 .starts_with("error=Error(\"GuestBinary not found:")
1101 );
1102 assert_eq!("hyperlight_host::sandbox::uninitialized", logcall.target);
1103 }
1104 {
1105 TEST_LOGGER.clear_log_calls();
1106 TEST_LOGGER.set_max_level(log::LevelFilter::Error);
1107
1108 let sbox = {
1109 let res = UninitializedSandbox::new(
1110 GuestBinary::FilePath(simple_guest_as_string().unwrap()),
1111 None,
1112 );
1113 res.unwrap()
1114 };
1115 let _: Result<MultiUseSandbox> = sbox.evolve();
1116
1117 let num_calls = TEST_LOGGER.num_log_calls();
1118
1119 assert_eq!(0, num_calls);
1120 }
1121 }
1122
1123 #[test]
1124 fn test_invalid_path() {
1125 let invalid_path = "some/path/that/does/not/exist";
1126 let sbox = UninitializedSandbox::new(GuestBinary::FilePath(invalid_path.to_string()), None);
1127 println!("{:?}", sbox);
1128 #[cfg(target_os = "windows")]
1129 assert!(
1130 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)"))
1131 );
1132 #[cfg(target_os = "linux")]
1133 assert!(
1134 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)"))
1135 );
1136 }
1137}