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::uninitialized_evolve::evolve_impl_multi_use;
27use crate::func::host_functions::{HostFunction, register_host_function};
28use crate::func::{ParameterTuple, SupportedReturnType};
29#[cfg(feature = "build-metadata")]
30use crate::log_build_details;
31use crate::mem::exe::ExeInfo;
32use crate::mem::memory_region::{DEFAULT_GUEST_BLOB_MEM_FLAGS, MemoryRegionFlags};
33use crate::mem::mgr::SandboxMemoryManager;
34use crate::mem::shared_mem::ExclusiveSharedMemory;
35use crate::sandbox::SandboxConfiguration;
36use crate::{MultiUseSandbox, Result, new_error};
37
38#[cfg(seccomp)]
39const EXTRA_ALLOWED_SYSCALLS_FOR_WRITER_FUNC: &[super::ExtraAllowedSyscall] = &[
40 libc::SYS_mmap,
47 libc::SYS_brk,
48 libc::SYS_mprotect,
49 #[cfg(mshv)]
50 libc::SYS_close,
51];
52
53#[cfg(any(crashdump, gdb))]
54#[derive(Clone, Debug, Default)]
55pub(crate) struct SandboxRuntimeConfig {
56 #[cfg(crashdump)]
57 pub(crate) binary_path: Option<String>,
58 #[cfg(gdb)]
59 pub(crate) debug_info: Option<super::config::DebugInfo>,
60 #[cfg(crashdump)]
61 pub(crate) guest_core_dump: bool,
62}
63
64pub struct UninitializedSandbox {
76 pub(crate) host_funcs: Arc<Mutex<FunctionRegistry>>,
78 pub(crate) mgr: SandboxMemoryManager<ExclusiveSharedMemory>,
80 pub(crate) max_guest_log_level: Option<LevelFilter>,
81 pub(crate) config: SandboxConfiguration,
82 #[cfg(any(crashdump, gdb))]
83 pub(crate) rt_cfg: SandboxRuntimeConfig,
84 pub(crate) load_info: crate::mem::exe::LoadInfo,
85}
86
87impl Debug for UninitializedSandbox {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 f.debug_struct("UninitializedSandbox")
90 .field("memory_layout", &self.mgr.layout)
91 .finish()
92 }
93}
94
95#[derive(Debug)]
97pub enum GuestBinary<'a> {
98 Buffer(&'a [u8]),
100 FilePath(String),
102}
103
104#[derive(Debug)]
106pub struct GuestBlob<'a> {
107 pub data: &'a [u8],
109 pub permissions: MemoryRegionFlags,
112}
113
114impl<'a> From<&'a [u8]> for GuestBlob<'a> {
115 fn from(data: &'a [u8]) -> Self {
116 GuestBlob {
117 data,
118 permissions: DEFAULT_GUEST_BLOB_MEM_FLAGS,
119 }
120 }
121}
122
123#[derive(Debug)]
128pub struct GuestEnvironment<'a, 'b> {
129 pub guest_binary: GuestBinary<'a>,
131 pub init_data: Option<GuestBlob<'b>>,
133}
134
135impl<'a, 'b> GuestEnvironment<'a, 'b> {
136 pub fn new(guest_binary: GuestBinary<'a>, init_data: Option<&'b [u8]>) -> Self {
138 GuestEnvironment {
139 guest_binary,
140 init_data: init_data.map(GuestBlob::from),
141 }
142 }
143}
144
145impl<'a> From<GuestBinary<'a>> for GuestEnvironment<'a, '_> {
146 fn from(guest_binary: GuestBinary<'a>) -> Self {
147 GuestEnvironment {
148 guest_binary,
149 init_data: None,
150 }
151 }
152}
153
154impl UninitializedSandbox {
155 #[instrument(
162 err(Debug),
163 skip(env),
164 parent = Span::current()
165 )]
166 pub fn new<'a, 'b>(
167 env: impl Into<GuestEnvironment<'a, 'b>>,
168 cfg: Option<SandboxConfiguration>,
169 ) -> Result<Self> {
170 #[cfg(feature = "build-metadata")]
171 log_build_details();
172
173 #[cfg(target_os = "windows")]
175 check_windows_version()?;
176
177 let env: GuestEnvironment<'_, '_> = env.into();
178 let guest_binary = env.guest_binary;
179 let guest_blob = env.init_data;
180
181 let guest_binary = match guest_binary {
183 GuestBinary::FilePath(binary_path) => {
184 let path = Path::new(&binary_path)
185 .canonicalize()
186 .map_err(|e| new_error!("GuestBinary not found: '{}': {}", binary_path, e))?
187 .into_os_string()
188 .into_string()
189 .map_err(|e| new_error!("Error converting OsString to String: {:?}", e))?;
190
191 GuestBinary::FilePath(path)
192 }
193 buffer @ GuestBinary::Buffer(_) => buffer,
194 };
195
196 let sandbox_cfg = cfg.unwrap_or_default();
197
198 #[cfg(any(crashdump, gdb))]
199 let rt_cfg = {
200 #[cfg(crashdump)]
201 let guest_core_dump = sandbox_cfg.get_guest_core_dump();
202
203 #[cfg(gdb)]
204 let debug_info = sandbox_cfg.get_guest_debug_info();
205
206 #[cfg(crashdump)]
207 let binary_path = if let GuestBinary::FilePath(ref path) = guest_binary {
208 Some(path.clone())
209 } else {
210 None
211 };
212
213 SandboxRuntimeConfig {
214 #[cfg(crashdump)]
215 binary_path,
216 #[cfg(gdb)]
217 debug_info,
218 #[cfg(crashdump)]
219 guest_core_dump,
220 }
221 };
222
223 let (mut mem_mgr_wrapper, load_info) = UninitializedSandbox::load_guest_binary(
224 sandbox_cfg,
225 &guest_binary,
226 guest_blob.as_ref(),
227 )?;
228
229 mem_mgr_wrapper.write_memory_layout()?;
230
231 if let Some(blob) = guest_blob {
233 mem_mgr_wrapper.write_init_data(blob.data)?;
234 }
235
236 let host_funcs = Arc::new(Mutex::new(FunctionRegistry::default()));
237
238 let mut sandbox = Self {
239 host_funcs,
240 mgr: mem_mgr_wrapper,
241 max_guest_log_level: None,
242 config: sandbox_cfg,
243 #[cfg(any(crashdump, gdb))]
244 rt_cfg,
245 load_info,
246 };
247
248 sandbox.register_print(default_writer_func)?;
250
251 crate::debug!("Sandbox created: {:#?}", sandbox);
252
253 Ok(sandbox)
254 }
255
256 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
262 pub fn evolve(self) -> Result<MultiUseSandbox> {
263 evolve_impl_multi_use(self)
264 }
265
266 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
277 pub(super) fn load_guest_binary(
278 cfg: SandboxConfiguration,
279 guest_binary: &GuestBinary,
280 guest_blob: Option<&GuestBlob>,
281 ) -> Result<(
282 SandboxMemoryManager<ExclusiveSharedMemory>,
283 crate::mem::exe::LoadInfo,
284 )> {
285 let 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 SandboxMemoryManager::load_guest_binary_into_memory(cfg, exe_info, guest_blob)
291 }
292
293 pub fn set_max_guest_log_level(&mut self, log_level: LevelFilter) {
298 self.max_guest_log_level = Some(log_level);
299 }
300
301 pub fn register<Args: ParameterTuple, Output: SupportedReturnType>(
303 &mut self,
304 name: impl AsRef<str>,
305 host_func: impl Into<HostFunction<Output, Args>>,
306 ) -> Result<()> {
307 register_host_function(host_func, self, name.as_ref(), None)
308 }
309
310 #[cfg(seccomp)]
315 pub fn register_with_extra_allowed_syscalls<
316 Args: ParameterTuple,
317 Output: SupportedReturnType,
318 >(
319 &mut self,
320 name: impl AsRef<str>,
321 host_func: impl Into<HostFunction<Output, Args>>,
322 extra_allowed_syscalls: impl IntoIterator<Item = crate::sandbox::ExtraAllowedSyscall>,
323 ) -> Result<()> {
324 let extra_allowed_syscalls: Vec<_> = extra_allowed_syscalls.into_iter().collect();
325 register_host_function(host_func, self, name.as_ref(), Some(extra_allowed_syscalls))
326 }
327
328 pub fn register_print(
334 &mut self,
335 print_func: impl Into<HostFunction<i32, (String,)>>,
336 ) -> Result<()> {
337 #[cfg(not(seccomp))]
338 self.register("HostPrint", print_func)?;
339
340 #[cfg(seccomp)]
341 self.register_with_extra_allowed_syscalls(
342 "HostPrint",
343 print_func,
344 EXTRA_ALLOWED_SYSCALLS_FOR_WRITER_FUNC.iter().copied(),
345 )?;
346
347 Ok(())
348 }
349
350 #[cfg(seccomp)]
355 pub fn register_print_with_extra_allowed_syscalls(
356 &mut self,
357 print_func: impl Into<HostFunction<i32, (String,)>>,
358 extra_allowed_syscalls: impl IntoIterator<Item = crate::sandbox::ExtraAllowedSyscall>,
359 ) -> Result<()> {
360 #[cfg(seccomp)]
361 self.register_with_extra_allowed_syscalls(
362 "HostPrint",
363 print_func,
364 EXTRA_ALLOWED_SYSCALLS_FOR_WRITER_FUNC
365 .iter()
366 .copied()
367 .chain(extra_allowed_syscalls),
368 )?;
369
370 Ok(())
371 }
372}
373#[cfg(target_os = "windows")]
376fn check_windows_version() -> Result<()> {
377 use windows_version::{OsVersion, is_server};
378 const WINDOWS_MAJOR: u32 = 10;
379 const WINDOWS_MINOR: u32 = 0;
380 const WINDOWS_PACK: u32 = 0;
381
382 if is_server() {
384 if OsVersion::current() < OsVersion::new(WINDOWS_MAJOR, WINDOWS_MINOR, WINDOWS_PACK, 20348)
385 {
386 return Err(new_error!(
387 "Hyperlight Requires Windows Server 2022 or newer"
388 ));
389 }
390 } else if OsVersion::current()
391 < OsVersion::new(WINDOWS_MAJOR, WINDOWS_MINOR, WINDOWS_PACK, 22000)
392 {
393 return Err(new_error!("Hyperlight Requires Windows 11 or newer"));
394 }
395 Ok(())
396}
397
398#[cfg(test)]
399mod tests {
400 use std::sync::Arc;
401 use std::sync::mpsc::channel;
402 use std::{fs, thread};
403
404 use crossbeam_queue::ArrayQueue;
405 use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterValue, ReturnValue};
406 use hyperlight_testing::simple_guest_as_string;
407
408 use crate::sandbox::SandboxConfiguration;
409 use crate::sandbox::uninitialized::{GuestBinary, GuestEnvironment};
410 use crate::{MultiUseSandbox, Result, UninitializedSandbox, new_error};
411
412 #[test]
413 fn test_load_extra_blob() {
414 let binary_path = simple_guest_as_string().unwrap();
415 let buffer = [0xde, 0xad, 0xbe, 0xef];
416 let guest_env =
417 GuestEnvironment::new(GuestBinary::FilePath(binary_path.clone()), Some(&buffer));
418
419 let uninitialized_sandbox = UninitializedSandbox::new(guest_env, None).unwrap();
420 let mut sandbox: MultiUseSandbox = uninitialized_sandbox.evolve().unwrap();
421
422 let res = sandbox
423 .call::<Vec<u8>>("ReadFromUserMemory", (4u64, buffer.to_vec()))
424 .expect("Failed to call ReadFromUserMemory");
425
426 assert_eq!(res, buffer.to_vec());
427 }
428
429 #[test]
430 fn test_new_sandbox() {
431 let binary_path = simple_guest_as_string().unwrap();
434 let sandbox = UninitializedSandbox::new(GuestBinary::FilePath(binary_path.clone()), None);
435 assert!(sandbox.is_ok());
436
437 let mut binary_path_does_not_exist = binary_path.clone();
440 binary_path_does_not_exist.push_str(".nonexistent");
441 let uninitialized_sandbox =
442 UninitializedSandbox::new(GuestBinary::FilePath(binary_path_does_not_exist), None);
443 assert!(uninitialized_sandbox.is_err());
444
445 let cfg = {
447 let mut cfg = SandboxConfiguration::default();
448 cfg.set_input_data_size(0x1000);
449 cfg.set_output_data_size(0x1000);
450 cfg.set_stack_size(0x1000);
451 cfg.set_heap_size(0x1000);
452 Some(cfg)
453 };
454
455 let uninitialized_sandbox =
456 UninitializedSandbox::new(GuestBinary::FilePath(binary_path.clone()), cfg);
457 assert!(uninitialized_sandbox.is_ok());
458
459 let uninitialized_sandbox =
460 UninitializedSandbox::new(GuestBinary::FilePath(binary_path), None).unwrap();
461
462 let _sandbox: MultiUseSandbox = uninitialized_sandbox.evolve().unwrap();
465
466 let binary_path = simple_guest_as_string().unwrap();
469 let sandbox =
470 UninitializedSandbox::new(GuestBinary::Buffer(&fs::read(binary_path).unwrap()), None);
471 assert!(sandbox.is_ok());
472
473 let binary_path = simple_guest_as_string().unwrap();
476 let mut bytes = fs::read(binary_path).unwrap();
477 let _ = bytes.split_off(100);
478 let sandbox = UninitializedSandbox::new(GuestBinary::Buffer(&bytes), None);
479 assert!(sandbox.is_err());
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 None.as_ref(),
492 )
493 .unwrap();
494 }
495
496 #[test]
497 fn test_host_functions() {
498 let uninitialized_sandbox = || {
499 UninitializedSandbox::new(
500 GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
501 None,
502 )
503 .unwrap()
504 };
505
506 {
508 let mut usbox = uninitialized_sandbox();
509
510 usbox.register("test0", |arg: i32| Ok(arg + 1)).unwrap();
511
512 let sandbox: Result<MultiUseSandbox> = usbox.evolve();
513 assert!(sandbox.is_ok());
514 let sandbox = sandbox.unwrap();
515
516 let host_funcs = sandbox
517 ._host_funcs
518 .try_lock()
519 .map_err(|_| new_error!("Error locking"));
520
521 assert!(host_funcs.is_ok());
522
523 let res = host_funcs
524 .unwrap()
525 .call_host_function("test0", vec![ParameterValue::Int(1)])
526 .unwrap();
527
528 assert_eq!(res, ReturnValue::Int(2));
529 }
530
531 {
533 let mut usbox = uninitialized_sandbox();
534
535 usbox.register("test1", |a: i32, b: i32| Ok(a + b)).unwrap();
536
537 let sandbox: Result<MultiUseSandbox> = usbox.evolve();
538 assert!(sandbox.is_ok());
539 let sandbox = sandbox.unwrap();
540
541 let host_funcs = sandbox
542 ._host_funcs
543 .try_lock()
544 .map_err(|_| new_error!("Error locking"));
545
546 assert!(host_funcs.is_ok());
547
548 let res = host_funcs
549 .unwrap()
550 .call_host_function(
551 "test1",
552 vec![ParameterValue::Int(1), ParameterValue::Int(2)],
553 )
554 .unwrap();
555
556 assert_eq!(res, ReturnValue::Int(3));
557 }
558
559 {
561 let mut usbox = uninitialized_sandbox();
562
563 usbox
564 .register("test2", |msg: String| {
565 println!("test2 called: {}", msg);
566 Ok(())
567 })
568 .unwrap();
569
570 let sandbox: Result<MultiUseSandbox> = usbox.evolve();
571 assert!(sandbox.is_ok());
572 let sandbox = sandbox.unwrap();
573
574 let host_funcs = sandbox
575 ._host_funcs
576 .try_lock()
577 .map_err(|_| new_error!("Error locking"));
578
579 assert!(host_funcs.is_ok());
580
581 let res = host_funcs.unwrap().call_host_function("test2", vec![]);
582 assert!(res.is_err());
583 }
584
585 {
587 let usbox = uninitialized_sandbox();
588 let sandbox: Result<MultiUseSandbox> = usbox.evolve();
589 assert!(sandbox.is_ok());
590 let sandbox = sandbox.unwrap();
591
592 let host_funcs = sandbox
593 ._host_funcs
594 .try_lock()
595 .map_err(|_| new_error!("Error locking"));
596
597 assert!(host_funcs.is_ok());
598
599 let res = host_funcs.unwrap().call_host_function("test4", vec![]);
600 assert!(res.is_err());
601 }
602 }
603
604 #[test]
605 fn test_host_print() {
606 let (tx, rx) = channel();
611
612 let writer = move |msg| {
613 let _ = tx.send(msg);
614 Ok(0)
615 };
616
617 let mut sandbox = UninitializedSandbox::new(
618 GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
619 None,
620 )
621 .expect("Failed to create sandbox");
622
623 sandbox
624 .register_print(writer)
625 .expect("Failed to register host print function");
626
627 let host_funcs = sandbox
628 .host_funcs
629 .try_lock()
630 .map_err(|_| new_error!("Error locking"));
631
632 assert!(host_funcs.is_ok());
633
634 host_funcs.unwrap().host_print("test".to_string()).unwrap();
635
636 drop(sandbox);
637
638 let received_msgs: Vec<_> = rx.into_iter().collect();
639 assert_eq!(received_msgs, ["test"]);
640
641 fn fn_writer(msg: String) -> Result<i32> {
702 assert_eq!(msg, "test2");
703 Ok(0)
704 }
705
706 let mut sandbox = UninitializedSandbox::new(
707 GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
708 None,
709 )
710 .expect("Failed to create sandbox");
711
712 sandbox
713 .register_print(fn_writer)
714 .expect("Failed to register host print function");
715
716 let host_funcs = sandbox
717 .host_funcs
718 .try_lock()
719 .map_err(|_| new_error!("Error locking"));
720
721 assert!(host_funcs.is_ok());
722
723 host_funcs.unwrap().host_print("test2".to_string()).unwrap();
724
725 let mut test_host_print = TestHostPrint::new();
728
729 let writer_closure = move |s| test_host_print.write(s);
732
733 let mut sandbox = UninitializedSandbox::new(
734 GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
735 None,
736 )
737 .expect("Failed to create sandbox");
738
739 sandbox
740 .register_print(writer_closure)
741 .expect("Failed to register host print function");
742
743 let host_funcs = sandbox
744 .host_funcs
745 .try_lock()
746 .map_err(|_| new_error!("Error locking"));
747
748 assert!(host_funcs.is_ok());
749
750 host_funcs.unwrap().host_print("test3".to_string()).unwrap();
751 }
752
753 struct TestHostPrint {}
754
755 impl TestHostPrint {
756 fn new() -> Self {
757 TestHostPrint {}
758 }
759
760 fn write(&mut self, msg: String) -> Result<i32> {
761 assert_eq!(msg, "test3");
762 Ok(0)
763 }
764 }
765
766 #[test]
767 fn check_create_and_use_sandbox_on_different_threads() {
768 let unintializedsandbox_queue = Arc::new(ArrayQueue::<UninitializedSandbox>::new(10));
769 let sandbox_queue = Arc::new(ArrayQueue::<MultiUseSandbox>::new(10));
770
771 for i in 0..10 {
772 let simple_guest_path = simple_guest_as_string().expect("Guest Binary Missing");
773 let unintializedsandbox = {
774 let err_string = format!("failed to create UninitializedSandbox {i}");
775 let err_str = err_string.as_str();
776 UninitializedSandbox::new(GuestBinary::FilePath(simple_guest_path), None)
777 .expect(err_str)
778 };
779
780 {
781 let err_string = format!("Failed to push UninitializedSandbox {i}");
782 let err_str = err_string.as_str();
783
784 unintializedsandbox_queue
785 .push(unintializedsandbox)
786 .expect(err_str);
787 }
788 }
789
790 let thread_handles = (0..10)
791 .map(|i| {
792 let uq = unintializedsandbox_queue.clone();
793 let sq = sandbox_queue.clone();
794 thread::spawn(move || {
795 let uninitialized_sandbox = uq.pop().unwrap_or_else(|| {
796 panic!("Failed to pop UninitializedSandbox thread {}", i)
797 });
798
799 let host_funcs = uninitialized_sandbox
800 .host_funcs
801 .try_lock()
802 .map_err(|_| new_error!("Error locking"));
803
804 assert!(host_funcs.is_ok());
805
806 host_funcs
807 .unwrap()
808 .host_print(format!("Print from UninitializedSandbox on Thread {}\n", i))
809 .unwrap();
810
811 let sandbox = uninitialized_sandbox.evolve().unwrap_or_else(|_| {
812 panic!("Failed to initialize UninitializedSandbox thread {}", i)
813 });
814
815 sq.push(sandbox).unwrap_or_else(|_| {
816 panic!("Failed to push UninitializedSandbox thread {}", i)
817 })
818 })
819 })
820 .collect::<Vec<_>>();
821
822 for handle in thread_handles {
823 handle.join().unwrap();
824 }
825
826 let thread_handles = (0..10)
827 .map(|i| {
828 let sq = sandbox_queue.clone();
829 thread::spawn(move || {
830 let sandbox = sq
831 .pop()
832 .unwrap_or_else(|| panic!("Failed to pop Sandbox thread {}", i));
833
834 let host_funcs = sandbox
835 ._host_funcs
836 .try_lock()
837 .map_err(|_| new_error!("Error locking"));
838
839 assert!(host_funcs.is_ok());
840
841 host_funcs
842 .unwrap()
843 .host_print(format!("Print from Sandbox on Thread {}\n", i))
844 .unwrap();
845 })
846 })
847 .collect::<Vec<_>>();
848
849 for handle in thread_handles {
850 handle.join().unwrap();
851 }
852 }
853
854 #[test]
855 #[ignore]
861 #[cfg(feature = "build-metadata")]
862 fn test_trace_trace() {
863 use hyperlight_testing::logger::Logger as TestLogger;
864 use hyperlight_testing::tracing_subscriber::TracingSubscriber as TestSubscriber;
865 use serde_json::{Map, Value};
866 use tracing::Level as tracing_level;
867 use tracing_core::Subscriber;
868 use tracing_core::callsite::rebuild_interest_cache;
869 use uuid::Uuid;
870
871 use crate::testing::log_values::build_metadata_testing::try_to_strings;
872 use crate::testing::log_values::test_value_as_str;
873
874 TestLogger::initialize_log_tracer();
875 rebuild_interest_cache();
876 let subscriber = TestSubscriber::new(tracing_level::TRACE);
877 tracing::subscriber::with_default(subscriber.clone(), || {
878 let correlation_id = Uuid::new_v4().as_hyphenated().to_string();
879 let span = tracing::error_span!("test_trace_logs", correlation_id).entered();
880
881 let current_span = subscriber.current_span();
884 assert!(current_span.is_known(), "Current span is unknown");
885 let current_span_metadata = current_span.into_inner().unwrap();
886 assert_eq!(
887 current_span_metadata.0.into_u64(),
888 1,
889 "Current span is not span 1"
890 );
891 assert_eq!(current_span_metadata.1.name(), "test_trace_logs");
892
893 let span_data = subscriber.get_span(1);
896 let span_attributes: &Map<String, Value> = span_data
897 .get("span")
898 .unwrap()
899 .get("attributes")
900 .unwrap()
901 .as_object()
902 .unwrap();
903
904 test_value_as_str(span_attributes, "correlation_id", correlation_id.as_str());
905
906 let mut binary_path = simple_guest_as_string().unwrap();
907 binary_path.push_str("does_not_exist");
908
909 let sbox = UninitializedSandbox::new(GuestBinary::FilePath(binary_path), None);
910 assert!(sbox.is_err());
911
912 let current_span = subscriber.current_span();
915 assert!(current_span.is_known(), "Current span is unknown");
916 let current_span_metadata = current_span.into_inner().unwrap();
917 assert_eq!(
918 current_span_metadata.0.into_u64(),
919 1,
920 "Current span is not span 1"
921 );
922
923 let span_metadata = subscriber.get_span_metadata(2);
924 assert_eq!(span_metadata.name(), "new");
925
926 let events = subscriber.get_events();
929 assert_eq!(events.len(), 15);
930
931 let mut count_matching_events = 0;
932
933 for json_value in events {
934 let event_values = json_value.as_object().unwrap().get("event").unwrap();
935 let metadata_values_map =
936 event_values.get("metadata").unwrap().as_object().unwrap();
937 let event_values_map = event_values.as_object().unwrap();
938
939 let expected_error_start = "Error(\"GuestBinary not found:";
940
941 let err_vals_res = try_to_strings([
942 (metadata_values_map, "level"),
943 (event_values_map, "error"),
944 (metadata_values_map, "module_path"),
945 (metadata_values_map, "target"),
946 ]);
947 if let Ok(err_vals) = err_vals_res
948 && err_vals[0] == "ERROR"
949 && err_vals[1].starts_with(expected_error_start)
950 && err_vals[2] == "hyperlight_host::sandbox::uninitialized"
951 && err_vals[3] == "hyperlight_host::sandbox::uninitialized"
952 {
953 count_matching_events += 1;
954 }
955 }
956 assert!(
957 count_matching_events == 1,
958 "Unexpected number of matching events {}",
959 count_matching_events
960 );
961 span.exit();
962 subscriber.clear();
963 });
964 }
965
966 #[test]
967 #[ignore]
968 #[cfg(feature = "build-metadata")]
971 fn test_log_trace() {
972 use std::path::PathBuf;
973
974 use hyperlight_testing::logger::{LOGGER as TEST_LOGGER, Logger as TestLogger};
975 use log::Level;
976 use tracing_core::callsite::rebuild_interest_cache;
977
978 {
979 TestLogger::initialize_test_logger();
980 TEST_LOGGER.set_max_level(log::LevelFilter::Trace);
981
982 rebuild_interest_cache();
986
987 let mut invalid_binary_path = simple_guest_as_string().unwrap();
988 invalid_binary_path.push_str("does_not_exist");
989
990 let sbox = UninitializedSandbox::new(GuestBinary::FilePath(invalid_binary_path), None);
991 assert!(sbox.is_err());
992
993 let num_calls = TEST_LOGGER.num_log_calls();
1007 assert_eq!(19, num_calls);
1008
1009 let logcall = TEST_LOGGER.get_log_call(0).unwrap();
1012 assert_eq!(Level::Info, logcall.level);
1013
1014 assert!(logcall.args.starts_with("new; cfg"));
1015 assert_eq!("hyperlight_host::sandbox::uninitialized", logcall.target);
1016
1017 let logcall = TEST_LOGGER.get_log_call(1).unwrap();
1020 assert_eq!(Level::Trace, logcall.level);
1021 assert_eq!(logcall.args, "-> new;");
1022 assert_eq!("tracing::span::active", logcall.target);
1023
1024 let logcall = TEST_LOGGER.get_log_call(16).unwrap();
1027 assert_eq!(Level::Error, logcall.level);
1028 assert!(
1029 logcall
1030 .args
1031 .starts_with("error=Error(\"GuestBinary not found:")
1032 );
1033 assert_eq!("hyperlight_host::sandbox::uninitialized", logcall.target);
1034
1035 let logcall = TEST_LOGGER.get_log_call(17).unwrap();
1038 assert_eq!(Level::Trace, logcall.level);
1039 assert_eq!(logcall.args, "<- new;");
1040 assert_eq!("tracing::span::active", logcall.target);
1041
1042 let logcall = TEST_LOGGER.get_log_call(18).unwrap();
1045 assert_eq!(Level::Trace, logcall.level);
1046 assert_eq!(logcall.args, "-- new;");
1047 assert_eq!("tracing::span", logcall.target);
1048 }
1049 {
1050 TEST_LOGGER.clear_log_calls();
1052 TEST_LOGGER.set_max_level(log::LevelFilter::Info);
1053
1054 let mut valid_binary_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
1055 valid_binary_path.push("src");
1056 valid_binary_path.push("sandbox");
1057 valid_binary_path.push("initialized.rs");
1058
1059 let sbox = UninitializedSandbox::new(
1060 GuestBinary::FilePath(valid_binary_path.into_os_string().into_string().unwrap()),
1061 None,
1062 );
1063 assert!(sbox.is_err());
1064
1065 let num_calls = TEST_LOGGER.num_log_calls();
1068 assert_eq!(2, num_calls);
1069
1070 let logcall = TEST_LOGGER.get_log_call(0).unwrap();
1073 assert_eq!(Level::Info, logcall.level);
1074
1075 assert!(logcall.args.starts_with("new; cfg"));
1076 assert_eq!("hyperlight_host::sandbox::uninitialized", logcall.target);
1077
1078 let logcall = TEST_LOGGER.get_log_call(1).unwrap();
1081 assert_eq!(Level::Error, logcall.level);
1082 assert!(
1083 logcall
1084 .args
1085 .starts_with("error=Error(\"GuestBinary not found:")
1086 );
1087 assert_eq!("hyperlight_host::sandbox::uninitialized", logcall.target);
1088 }
1089 {
1090 TEST_LOGGER.clear_log_calls();
1091 TEST_LOGGER.set_max_level(log::LevelFilter::Error);
1092
1093 let sbox = {
1094 let res = UninitializedSandbox::new(
1095 GuestBinary::FilePath(simple_guest_as_string().unwrap()),
1096 None,
1097 );
1098 res.unwrap()
1099 };
1100 let _: Result<MultiUseSandbox> = sbox.evolve();
1101
1102 let num_calls = TEST_LOGGER.num_log_calls();
1103
1104 assert_eq!(0, num_calls);
1105 }
1106 }
1107
1108 #[test]
1109 fn test_invalid_path() {
1110 let invalid_path = "some/path/that/does/not/exist";
1111 let sbox = UninitializedSandbox::new(GuestBinary::FilePath(invalid_path.to_string()), None);
1112 println!("{:?}", sbox);
1113 #[cfg(target_os = "windows")]
1114 assert!(
1115 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)"))
1116 );
1117 #[cfg(target_os = "linux")]
1118 assert!(
1119 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)"))
1120 );
1121 }
1122}