1use std::fmt::Debug;
18use std::option::Option;
19use std::path::Path;
20use std::sync::{Arc, Mutex};
21
22use tracing::{Span, instrument};
23use tracing_core::LevelFilter;
24
25use super::host_funcs::FunctionRegistry;
26use super::snapshot::Snapshot;
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::memory_region::{DEFAULT_GUEST_BLOB_MEM_FLAGS, MemoryRegionFlags};
33use crate::mem::mgr::SandboxMemoryManager;
34use crate::mem::shared_mem::{ExclusiveSharedMemory, SharedMemory};
35use crate::sandbox::SandboxConfiguration;
36use crate::{MultiUseSandbox, Result, new_error};
37
38#[cfg(any(crashdump, gdb))]
39#[derive(Clone, Debug, Default)]
40pub(crate) struct SandboxRuntimeConfig {
41 #[cfg(crashdump)]
42 pub(crate) binary_path: Option<String>,
43 #[cfg(gdb)]
44 pub(crate) debug_info: Option<super::config::DebugInfo>,
45 #[cfg(crashdump)]
46 pub(crate) guest_core_dump: bool,
47 #[cfg(crashdump)]
54 pub(crate) entry_point: Option<u64>,
55}
56
57pub struct UninitializedSandbox {
69 pub(crate) host_funcs: Arc<Mutex<FunctionRegistry>>,
71 pub(crate) mgr: SandboxMemoryManager<ExclusiveSharedMemory>,
73 pub(crate) max_guest_log_level: Option<LevelFilter>,
74 pub(crate) config: SandboxConfiguration,
75 #[cfg(any(crashdump, gdb))]
76 pub(crate) rt_cfg: SandboxRuntimeConfig,
77 pub(crate) load_info: crate::mem::exe::LoadInfo,
78 pub(crate) stack_top_gva: u64,
81 pub(crate) pending_file_mappings: Vec<super::file_mapping::PreparedFileMapping>,
84}
85
86impl Debug for UninitializedSandbox {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 f.debug_struct("UninitializedSandbox")
89 .field("memory_layout", &self.mgr.layout)
90 .finish()
91 }
92}
93
94#[derive(Debug)]
96pub enum GuestBinary<'a> {
97 Buffer(&'a [u8]),
99 FilePath(String),
101}
102impl<'a> GuestBinary<'a> {
103 pub fn canonicalize(&mut self) -> Result<()> {
111 if let GuestBinary::FilePath(p) = self {
112 let canon = Path::new(&p)
113 .canonicalize()
114 .map_err(|e| new_error!("GuestBinary not found: '{}': {}", p, e))?
115 .into_os_string()
116 .into_string()
117 .map_err(|e| new_error!("Error converting OsString to String: {:?}", e))?;
118 *self = GuestBinary::FilePath(canon)
119 }
120 Ok(())
121 }
122}
123
124#[derive(Debug)]
126pub struct GuestBlob<'a> {
127 pub data: &'a [u8],
129 pub permissions: MemoryRegionFlags,
132}
133
134impl<'a> From<&'a [u8]> for GuestBlob<'a> {
135 fn from(data: &'a [u8]) -> Self {
136 GuestBlob {
137 data,
138 permissions: DEFAULT_GUEST_BLOB_MEM_FLAGS,
139 }
140 }
141}
142
143#[derive(Debug)]
148pub struct GuestEnvironment<'a, 'b> {
149 pub guest_binary: GuestBinary<'a>,
151 pub init_data: Option<GuestBlob<'b>>,
153}
154
155impl<'a, 'b> GuestEnvironment<'a, 'b> {
156 pub fn new(guest_binary: GuestBinary<'a>, init_data: Option<&'b [u8]>) -> Self {
158 GuestEnvironment {
159 guest_binary,
160 init_data: init_data.map(GuestBlob::from),
161 }
162 }
163}
164
165impl<'a> From<GuestBinary<'a>> for GuestEnvironment<'a, '_> {
166 fn from(guest_binary: GuestBinary<'a>) -> Self {
167 GuestEnvironment {
168 guest_binary,
169 init_data: None,
170 }
171 }
172}
173
174impl UninitializedSandbox {
175 fn from_snapshot(
182 snapshot: Arc<Snapshot>,
183 cfg: Option<SandboxConfiguration>,
184 #[cfg(crashdump)] binary_path: Option<String>,
185 ) -> Result<Self> {
186 #[cfg(feature = "build-metadata")]
187 log_build_details();
188
189 #[cfg(target_os = "windows")]
191 check_windows_version()?;
192
193 let sandbox_cfg = cfg.unwrap_or_default();
194
195 #[cfg(any(crashdump, gdb))]
196 let rt_cfg = {
197 #[cfg(crashdump)]
198 let guest_core_dump = sandbox_cfg.get_guest_core_dump();
199
200 #[cfg(gdb)]
201 let debug_info = sandbox_cfg.get_guest_debug_info();
202
203 SandboxRuntimeConfig {
204 #[cfg(crashdump)]
205 binary_path,
206 #[cfg(gdb)]
207 debug_info,
208 #[cfg(crashdump)]
209 guest_core_dump,
210 #[cfg(crashdump)]
213 entry_point: None,
214 }
215 };
216
217 let mem_mgr_wrapper =
218 SandboxMemoryManager::<ExclusiveSharedMemory>::from_snapshot(snapshot.as_ref())?;
219
220 let host_funcs = Arc::new(Mutex::new(FunctionRegistry::with_default_host_print()));
221
222 let sandbox = Self {
223 host_funcs,
224 mgr: mem_mgr_wrapper,
225 max_guest_log_level: None,
226 config: sandbox_cfg,
227 #[cfg(any(crashdump, gdb))]
228 rt_cfg,
229 load_info: snapshot.load_info(),
230 stack_top_gva: snapshot.stack_top_gva(),
231 pending_file_mappings: Vec::new(),
232 };
233
234 crate::debug!("Sandbox created: {:#?}", sandbox);
235
236 Ok(sandbox)
237 }
238
239 #[instrument(
246 err(Debug),
247 skip(env),
248 parent = Span::current()
249 )]
250 pub fn new<'a, 'b>(
251 env: impl Into<GuestEnvironment<'a, 'b>>,
252 cfg: Option<SandboxConfiguration>,
253 ) -> Result<Self> {
254 let cfg = cfg.unwrap_or_default();
255 let env = env.into();
256 #[cfg(crashdump)]
257 let binary_path = match &env.guest_binary {
258 GuestBinary::FilePath(path) => Some(path.clone()),
259 GuestBinary::Buffer(_) => None,
260 };
261 let snapshot = Snapshot::from_env(env, cfg)?;
262 Self::from_snapshot(
263 Arc::new(snapshot),
264 Some(cfg),
265 #[cfg(crashdump)]
266 binary_path,
267 )
268 }
269
270 #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
276 pub fn evolve(self) -> Result<MultiUseSandbox> {
277 evolve_impl_multi_use(self)
278 }
279
280 #[instrument(err(Debug), skip(self, file_path, guest_base), parent = Span::current())]
291 pub fn map_file_cow(
292 &mut self,
293 file_path: &std::path::Path,
294 guest_base: u64,
295 ) -> crate::Result<u64> {
296 let shared_size = self.mgr.shared_mem.mem_size() as u64;
299 let base_addr = crate::mem::layout::SandboxMemoryLayout::BASE_ADDRESS as u64;
300
301 let prepared = super::file_mapping::prepare_file_cow(file_path, guest_base)?;
302
303 let mapping_end = guest_base
305 .checked_add(prepared.size as u64)
306 .ok_or_else(|| {
307 crate::HyperlightError::Error(format!(
308 "map_file_cow: guest address overflow: {:#x} + {:#x}",
309 guest_base, prepared.size
310 ))
311 })?;
312 let shared_end = base_addr.checked_add(shared_size).ok_or_else(|| {
313 crate::HyperlightError::Error("shared memory end overflow".to_string())
314 })?;
315 if guest_base < shared_end && mapping_end > base_addr {
316 return Err(crate::HyperlightError::Error(format!(
317 "map_file_cow: mapping [{:#x}..{:#x}) overlaps sandbox shared memory [{:#x}..{:#x})",
318 guest_base, mapping_end, base_addr, shared_end,
319 )));
320 }
321
322 let size = prepared.size as u64;
323
324 let new_start = guest_base;
326 let new_end = mapping_end;
327 for existing in &self.pending_file_mappings {
328 let ex_start = existing.guest_base;
329 let ex_end = ex_start.checked_add(existing.size as u64).ok_or_else(|| {
330 crate::HyperlightError::Error(format!(
331 "map_file_cow: existing mapping address overflow: {:#x} + {:#x}",
332 ex_start, existing.size
333 ))
334 })?;
335 if new_start < ex_end && new_end > ex_start {
336 return Err(crate::HyperlightError::Error(format!(
337 "map_file_cow: mapping [{:#x}..{:#x}) overlaps existing mapping [{:#x}..{:#x})",
338 new_start, new_end, ex_start, ex_end,
339 )));
340 }
341 }
342
343 self.pending_file_mappings.push(prepared);
344 Ok(size)
345 }
346
347 pub fn shared_mem_size(&self) -> usize {
352 self.mgr.shared_mem.mem_size()
353 }
354
355 pub fn set_max_guest_log_level(&mut self, log_level: LevelFilter) {
360 self.max_guest_log_level = Some(log_level);
361 }
362
363 pub fn register<Args: ParameterTuple, Output: SupportedReturnType>(
365 &mut self,
366 name: impl AsRef<str>,
367 host_func: impl Into<HostFunction<Output, Args>>,
368 ) -> Result<()> {
369 register_host_function(host_func, self, name.as_ref())
370 }
371
372 pub fn register_print(
378 &mut self,
379 print_func: impl Into<HostFunction<i32, (String,)>>,
380 ) -> Result<()> {
381 self.register("HostPrint", print_func)
382 }
383}
384#[cfg(target_os = "windows")]
387fn check_windows_version() -> Result<()> {
388 use windows_version::{OsVersion, is_server};
389 const WINDOWS_MAJOR: u32 = 10;
390 const WINDOWS_MINOR: u32 = 0;
391 const WINDOWS_PACK: u32 = 0;
392
393 if is_server() {
395 if OsVersion::current() < OsVersion::new(WINDOWS_MAJOR, WINDOWS_MINOR, WINDOWS_PACK, 20348)
396 {
397 return Err(new_error!(
398 "Hyperlight Requires Windows Server 2022 or newer"
399 ));
400 }
401 } else if OsVersion::current()
402 < OsVersion::new(WINDOWS_MAJOR, WINDOWS_MINOR, WINDOWS_PACK, 22000)
403 {
404 return Err(new_error!("Hyperlight Requires Windows 11 or newer"));
405 }
406 Ok(())
407}
408
409#[cfg(test)]
410mod tests {
411 use std::sync::Arc;
412 use std::sync::mpsc::channel;
413 use std::{fs, thread};
414
415 use crossbeam_queue::ArrayQueue;
416 use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterValue, ReturnValue};
417 use hyperlight_testing::simple_guest_as_string;
418
419 use crate::sandbox::SandboxConfiguration;
420 use crate::sandbox::uninitialized::{GuestBinary, GuestEnvironment};
421 use crate::{MultiUseSandbox, Result, UninitializedSandbox, new_error};
422
423 #[test]
424 fn test_load_extra_blob() {
425 let binary_path = simple_guest_as_string().unwrap();
426 let buffer = [0xde, 0xad, 0xbe, 0xef];
427 let guest_env =
428 GuestEnvironment::new(GuestBinary::FilePath(binary_path.clone()), Some(&buffer));
429
430 let uninitialized_sandbox = UninitializedSandbox::new(guest_env, None).unwrap();
431 let mut sandbox: MultiUseSandbox = uninitialized_sandbox.evolve().unwrap();
432
433 let res = sandbox
434 .call::<Vec<u8>>("ReadFromUserMemory", (4u64, buffer.to_vec()))
435 .expect("Failed to call ReadFromUserMemory");
436
437 assert_eq!(res, buffer.to_vec());
438 }
439
440 #[test]
441 fn test_new_sandbox() {
442 let binary_path = simple_guest_as_string().unwrap();
445 let sandbox = UninitializedSandbox::new(GuestBinary::FilePath(binary_path.clone()), None);
446 assert!(sandbox.is_ok());
447
448 let mut binary_path_does_not_exist = binary_path.clone();
451 binary_path_does_not_exist.push_str(".nonexistent");
452 let uninitialized_sandbox =
453 UninitializedSandbox::new(GuestBinary::FilePath(binary_path_does_not_exist), None);
454 assert!(uninitialized_sandbox.is_err());
455
456 let cfg = {
458 let mut cfg = SandboxConfiguration::default();
459 cfg.set_input_data_size(0x1000);
460 cfg.set_output_data_size(0x1000);
461 cfg.set_heap_size(0x1000);
462 Some(cfg)
463 };
464
465 let uninitialized_sandbox =
466 UninitializedSandbox::new(GuestBinary::FilePath(binary_path.clone()), cfg);
467 assert!(uninitialized_sandbox.is_ok());
468
469 let uninitialized_sandbox =
470 UninitializedSandbox::new(GuestBinary::FilePath(binary_path), None).unwrap();
471
472 let _sandbox: MultiUseSandbox = uninitialized_sandbox.evolve().unwrap();
475
476 let binary_path = simple_guest_as_string().unwrap();
479 let sandbox =
480 UninitializedSandbox::new(GuestBinary::Buffer(&fs::read(binary_path).unwrap()), None);
481 assert!(sandbox.is_ok());
482
483 let binary_path = simple_guest_as_string().unwrap();
486 let mut bytes = fs::read(binary_path).unwrap();
487 let _ = bytes.split_off(100);
488 let sandbox = UninitializedSandbox::new(GuestBinary::Buffer(&bytes), None);
489 assert!(sandbox.is_err());
490 }
491
492 #[test]
493 fn test_host_functions() {
494 let uninitialized_sandbox = || {
495 UninitializedSandbox::new(
496 GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
497 None,
498 )
499 .unwrap()
500 };
501
502 {
504 let mut usbox = uninitialized_sandbox();
505
506 usbox.register("test0", |arg: i32| Ok(arg + 1)).unwrap();
507
508 let sandbox: Result<MultiUseSandbox> = usbox.evolve();
509 assert!(sandbox.is_ok());
510 let sandbox = sandbox.unwrap();
511
512 let host_funcs = sandbox
513 .host_funcs
514 .try_lock()
515 .map_err(|_| new_error!("Error locking"));
516
517 assert!(host_funcs.is_ok());
518
519 let res = host_funcs
520 .unwrap()
521 .call_host_function("test0", vec![ParameterValue::Int(1)])
522 .unwrap();
523
524 assert_eq!(res, ReturnValue::Int(2));
525 }
526
527 {
529 let mut usbox = uninitialized_sandbox();
530
531 usbox.register("test1", |a: i32, b: i32| Ok(a + b)).unwrap();
532
533 let sandbox: Result<MultiUseSandbox> = usbox.evolve();
534 assert!(sandbox.is_ok());
535 let sandbox = sandbox.unwrap();
536
537 let host_funcs = sandbox
538 .host_funcs
539 .try_lock()
540 .map_err(|_| new_error!("Error locking"));
541
542 assert!(host_funcs.is_ok());
543
544 let res = host_funcs
545 .unwrap()
546 .call_host_function(
547 "test1",
548 vec![ParameterValue::Int(1), ParameterValue::Int(2)],
549 )
550 .unwrap();
551
552 assert_eq!(res, ReturnValue::Int(3));
553 }
554
555 {
557 let mut usbox = uninitialized_sandbox();
558
559 usbox
560 .register("test2", |msg: String| {
561 println!("test2 called: {}", msg);
562 Ok(())
563 })
564 .unwrap();
565
566 let sandbox: Result<MultiUseSandbox> = usbox.evolve();
567 assert!(sandbox.is_ok());
568 let sandbox = sandbox.unwrap();
569
570 let host_funcs = sandbox
571 .host_funcs
572 .try_lock()
573 .map_err(|_| new_error!("Error locking"));
574
575 assert!(host_funcs.is_ok());
576
577 let res = host_funcs.unwrap().call_host_function("test2", vec![]);
578 assert!(res.is_err());
579 }
580
581 {
583 let usbox = uninitialized_sandbox();
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("test4", vec![]);
596 assert!(res.is_err());
597 }
598 }
599
600 #[test]
601 fn test_host_print() {
602 let (tx, rx) = channel();
607
608 let writer = move |msg| {
609 let _ = tx.send(msg);
610 Ok(0)
611 };
612
613 let mut sandbox = UninitializedSandbox::new(
614 GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
615 None,
616 )
617 .expect("Failed to create sandbox");
618
619 sandbox
620 .register_print(writer)
621 .expect("Failed to register host print function");
622
623 let host_funcs = sandbox
624 .host_funcs
625 .try_lock()
626 .map_err(|_| new_error!("Error locking"));
627
628 assert!(host_funcs.is_ok());
629
630 host_funcs.unwrap().host_print("test".to_string()).unwrap();
631
632 drop(sandbox);
633
634 let received_msgs: Vec<_> = rx.into_iter().collect();
635 assert_eq!(received_msgs, ["test"]);
636
637 fn fn_writer(msg: String) -> Result<i32> {
698 assert_eq!(msg, "test2");
699 Ok(0)
700 }
701
702 let mut sandbox = UninitializedSandbox::new(
703 GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
704 None,
705 )
706 .expect("Failed to create sandbox");
707
708 sandbox
709 .register_print(fn_writer)
710 .expect("Failed to register host print function");
711
712 let host_funcs = sandbox
713 .host_funcs
714 .try_lock()
715 .map_err(|_| new_error!("Error locking"));
716
717 assert!(host_funcs.is_ok());
718
719 host_funcs.unwrap().host_print("test2".to_string()).unwrap();
720
721 let mut test_host_print = TestHostPrint::new();
724
725 let writer_closure = move |s| test_host_print.write(s);
728
729 let mut sandbox = UninitializedSandbox::new(
730 GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
731 None,
732 )
733 .expect("Failed to create sandbox");
734
735 sandbox
736 .register_print(writer_closure)
737 .expect("Failed to register host print function");
738
739 let host_funcs = sandbox
740 .host_funcs
741 .try_lock()
742 .map_err(|_| new_error!("Error locking"));
743
744 assert!(host_funcs.is_ok());
745
746 host_funcs.unwrap().host_print("test3".to_string()).unwrap();
747 }
748
749 struct TestHostPrint {}
750
751 impl TestHostPrint {
752 fn new() -> Self {
753 TestHostPrint {}
754 }
755
756 fn write(&mut self, msg: String) -> Result<i32> {
757 assert_eq!(msg, "test3");
758 Ok(0)
759 }
760 }
761
762 #[test]
763 fn check_create_and_use_sandbox_on_different_threads() {
764 let unintializedsandbox_queue = Arc::new(ArrayQueue::<UninitializedSandbox>::new(10));
765 let sandbox_queue = Arc::new(ArrayQueue::<MultiUseSandbox>::new(10));
766
767 for i in 0..10 {
768 let simple_guest_path = simple_guest_as_string().expect("Guest Binary Missing");
769 let unintializedsandbox = {
770 let err_string = format!("failed to create UninitializedSandbox {i}");
771 let err_str = err_string.as_str();
772 UninitializedSandbox::new(GuestBinary::FilePath(simple_guest_path), None)
773 .expect(err_str)
774 };
775
776 {
777 let err_string = format!("Failed to push UninitializedSandbox {i}");
778 let err_str = err_string.as_str();
779
780 unintializedsandbox_queue
781 .push(unintializedsandbox)
782 .expect(err_str);
783 }
784 }
785
786 let thread_handles = (0..10)
787 .map(|i| {
788 let uq = unintializedsandbox_queue.clone();
789 let sq = sandbox_queue.clone();
790 thread::spawn(move || {
791 let uninitialized_sandbox = uq.pop().unwrap_or_else(|| {
792 panic!("Failed to pop UninitializedSandbox thread {}", i)
793 });
794
795 let host_funcs = uninitialized_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
803 .unwrap()
804 .host_print(format!("Print from UninitializedSandbox on Thread {}\n", i))
805 .unwrap();
806
807 let sandbox = uninitialized_sandbox.evolve().unwrap_or_else(|_| {
808 panic!("Failed to initialize UninitializedSandbox thread {}", i)
809 });
810
811 sq.push(sandbox).unwrap_or_else(|_| {
812 panic!("Failed to push UninitializedSandbox thread {}", i)
813 })
814 })
815 })
816 .collect::<Vec<_>>();
817
818 for handle in thread_handles {
819 handle.join().unwrap();
820 }
821
822 let thread_handles = (0..10)
823 .map(|i| {
824 let sq = sandbox_queue.clone();
825 thread::spawn(move || {
826 let sandbox = sq
827 .pop()
828 .unwrap_or_else(|| panic!("Failed to pop Sandbox thread {}", i));
829
830 let host_funcs = sandbox
831 .host_funcs
832 .try_lock()
833 .map_err(|_| new_error!("Error locking"));
834
835 assert!(host_funcs.is_ok());
836
837 host_funcs
838 .unwrap()
839 .host_print(format!("Print from Sandbox on Thread {}\n", i))
840 .unwrap();
841 })
842 })
843 .collect::<Vec<_>>();
844
845 for handle in thread_handles {
846 handle.join().unwrap();
847 }
848 }
849
850 #[test]
866 #[cfg(feature = "build-metadata")]
867 fn test_trace_trace() {
868 use hyperlight_testing::tracing_subscriber::TracingSubscriber;
869 use tracing::Level;
870 use tracing_core::Subscriber;
871 use tracing_core::callsite::rebuild_interest_cache;
872 use uuid::Uuid;
873
874 fn get_span_attr<'a>(span: &'a serde_json::Value, key: &str) -> Option<&'a str> {
876 span.get("span")?.get("attributes")?.get(key)?.as_str()
877 }
878
879 fn get_event_field<'a>(event: &'a serde_json::Value, field: &str) -> Option<&'a str> {
881 event.get("event")?.get(field)?.as_str()
882 }
883
884 fn get_event_metadata<'a>(event: &'a serde_json::Value, field: &str) -> Option<&'a str> {
886 event.get("event")?.get("metadata")?.get(field)?.as_str()
887 }
888
889 let subscriber = TracingSubscriber::new(Level::TRACE);
890
891 tracing::subscriber::with_default(subscriber.clone(), || {
892 let bad_path = simple_guest_as_string().unwrap() + "does_not_exist";
897 let _ = UninitializedSandbox::new(GuestBinary::FilePath(bad_path.clone()), None);
898
899 rebuild_interest_cache();
904
905 subscriber.clear();
907
908 let correlation_id = Uuid::new_v4().to_string();
909 let _span = tracing::error_span!("test_trace_logs", %correlation_id).entered();
910
911 let (test_span_id, span_meta) = subscriber
913 .current_span()
914 .into_inner()
915 .expect("Should be inside a span");
916 assert_eq!(span_meta.name(), "test_trace_logs");
917
918 let span_data = subscriber.get_span(test_span_id.into_u64());
920 let recorded_id =
921 get_span_attr(&span_data, "correlation_id").expect("correlation_id not found");
922 assert_eq!(recorded_id, correlation_id);
923
924 let result = UninitializedSandbox::new(GuestBinary::FilePath(bad_path), None);
927 assert!(result.is_err(), "Sandbox creation should fail");
928
929 let (current_id, _) = subscriber
931 .current_span()
932 .into_inner()
933 .expect("Should still be inside a span");
934 assert_eq!(
935 current_id.into_u64(),
936 test_span_id.into_u64(),
937 "Should still be in the test span"
938 );
939
940 let all_spans = subscriber.get_all_spans();
943 let _new_span_entry = all_spans
944 .iter()
945 .find(|&(&id, _)| {
946 id != test_span_id.into_u64()
947 && subscriber.get_span_metadata(id).name() == "new"
948 })
949 .expect("Expected a span named 'new' from UninitializedSandbox::new");
950
951 let events = subscriber.get_events();
953 assert_eq!(events.len(), 1, "Expected exactly one error event");
954
955 let event = &events[0];
956 let level = get_event_metadata(event, "level").expect("event should have level");
957 let error = get_event_field(event, "error").expect("event should have error field");
958 let target = get_event_metadata(event, "target").expect("event should have target");
959 let module_path =
960 get_event_metadata(event, "module_path").expect("event should have module_path");
961
962 assert_eq!(level, "ERROR");
963 assert!(
964 error.contains("GuestBinary not found"),
965 "Error should mention 'GuestBinary not found', got: {error}"
966 );
967 assert_eq!(target, "hyperlight_host::sandbox::uninitialized");
968 assert_eq!(module_path, "hyperlight_host::sandbox::uninitialized");
969 });
970 }
971
972 #[test]
973 #[ignore]
974 #[cfg(feature = "build-metadata")]
977 fn test_log_trace() {
978 use std::path::PathBuf;
979
980 use hyperlight_testing::logger::{LOGGER as TEST_LOGGER, Logger as TestLogger};
981 use log::Level;
982 use tracing_core::callsite::rebuild_interest_cache;
983
984 {
985 TestLogger::initialize_test_logger();
986 TEST_LOGGER.set_max_level(log::LevelFilter::Trace);
987
988 rebuild_interest_cache();
992
993 let mut invalid_binary_path = simple_guest_as_string().unwrap();
994 invalid_binary_path.push_str("does_not_exist");
995
996 let sbox = UninitializedSandbox::new(GuestBinary::FilePath(invalid_binary_path), None);
997 assert!(sbox.is_err());
998
999 let num_calls = TEST_LOGGER.num_log_calls();
1013 assert_eq!(13, num_calls);
1014
1015 let logcall = TEST_LOGGER.get_log_call(0).unwrap();
1018 assert_eq!(Level::Info, logcall.level);
1019
1020 assert!(logcall.args.starts_with("new; cfg"));
1021 assert_eq!("hyperlight_host::sandbox::uninitialized", logcall.target);
1022
1023 let logcall = TEST_LOGGER.get_log_call(1).unwrap();
1026 assert_eq!(Level::Trace, logcall.level);
1027 assert_eq!(logcall.args, "-> new;");
1028 assert_eq!("tracing::span::active", logcall.target);
1029
1030 let logcall = TEST_LOGGER.get_log_call(10).unwrap();
1033 assert_eq!(Level::Error, logcall.level);
1034 assert!(
1035 logcall
1036 .args
1037 .starts_with("error=Error(\"GuestBinary not found:")
1038 );
1039 assert_eq!("hyperlight_host::sandbox::uninitialized", logcall.target);
1040
1041 let logcall = TEST_LOGGER.get_log_call(11).unwrap();
1044 assert_eq!(Level::Trace, logcall.level);
1045 assert_eq!(logcall.args, "<- new;");
1046 assert_eq!("tracing::span::active", logcall.target);
1047
1048 let logcall = TEST_LOGGER.get_log_call(12).unwrap();
1051 assert_eq!(Level::Trace, logcall.level);
1052 assert_eq!(logcall.args, "-- new;");
1053 assert_eq!("tracing::span", logcall.target);
1054 }
1055 {
1056 TEST_LOGGER.clear_log_calls();
1058 TEST_LOGGER.set_max_level(log::LevelFilter::Info);
1059
1060 let mut valid_binary_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
1061 valid_binary_path.push("src");
1062 valid_binary_path.push("sandbox");
1063 valid_binary_path.push("initialized.rs");
1064
1065 let sbox = UninitializedSandbox::new(
1066 GuestBinary::FilePath(valid_binary_path.into_os_string().into_string().unwrap()),
1067 None,
1068 );
1069 assert!(sbox.is_err());
1070
1071 let num_calls = TEST_LOGGER.num_log_calls();
1074 assert_eq!(2, num_calls);
1075
1076 let logcall = TEST_LOGGER.get_log_call(0).unwrap();
1079 assert_eq!(Level::Info, logcall.level);
1080
1081 assert!(logcall.args.starts_with("new; cfg"));
1082 assert_eq!("hyperlight_host::sandbox::uninitialized", logcall.target);
1083
1084 let logcall = TEST_LOGGER.get_log_call(1).unwrap();
1087 assert_eq!(Level::Error, logcall.level);
1088 assert!(
1089 logcall
1090 .args
1091 .starts_with("error=Error(\"GuestBinary not found:")
1092 );
1093 assert_eq!("hyperlight_host::sandbox::uninitialized", logcall.target);
1094 }
1095 {
1096 TEST_LOGGER.clear_log_calls();
1097 TEST_LOGGER.set_max_level(log::LevelFilter::Error);
1098
1099 let sbox = {
1100 let res = UninitializedSandbox::new(
1101 GuestBinary::FilePath(simple_guest_as_string().unwrap()),
1102 None,
1103 );
1104 res.unwrap()
1105 };
1106 let _: Result<MultiUseSandbox> = sbox.evolve();
1107
1108 let num_calls = TEST_LOGGER.num_log_calls();
1109
1110 assert_eq!(0, num_calls);
1111 }
1112 }
1113
1114 #[test]
1115 fn test_invalid_path() {
1116 let invalid_path = "some/path/that/does/not/exist";
1117 let sbox = UninitializedSandbox::new(GuestBinary::FilePath(invalid_path.to_string()), None);
1118 println!("{:?}", sbox);
1119 #[cfg(target_os = "windows")]
1120 assert!(
1121 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)"))
1122 );
1123 #[cfg(target_os = "linux")]
1124 assert!(
1125 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)"))
1126 );
1127 }
1128
1129 #[test]
1130 fn test_from_snapshot_various_configurations() {
1131 use crate::sandbox::snapshot::Snapshot;
1132
1133 let binary_path = simple_guest_as_string().unwrap();
1134
1135 {
1137 let env = GuestEnvironment::new(GuestBinary::FilePath(binary_path.clone()), None);
1138
1139 let snapshot = Arc::new(
1140 Snapshot::from_env(env, Default::default())
1141 .expect("Failed to create snapshot with default config"),
1142 );
1143
1144 let sandbox1 = UninitializedSandbox::from_snapshot(
1146 snapshot.clone(),
1147 None,
1148 #[cfg(crashdump)]
1149 Some(binary_path.clone()),
1150 )
1151 .expect("Failed to create first sandbox from snapshot");
1152
1153 let sandbox2 = UninitializedSandbox::from_snapshot(
1155 snapshot.clone(),
1156 None,
1157 #[cfg(crashdump)]
1158 Some(binary_path.clone()),
1159 )
1160 .expect("Failed to create second sandbox from snapshot");
1161
1162 let _evolved1: MultiUseSandbox = sandbox1.evolve().expect("Failed to evolve sandbox1");
1164 let _evolved2: MultiUseSandbox = sandbox2.evolve().expect("Failed to evolve sandbox2");
1165 }
1166
1167 {
1169 let mut cfg = SandboxConfiguration::default();
1170 cfg.set_heap_size(16 * 1024 * 1024); let env = GuestEnvironment::new(GuestBinary::FilePath(binary_path.clone()), None);
1173
1174 let snapshot = Arc::new(
1175 Snapshot::from_env(env, cfg)
1176 .expect("Failed to create snapshot with custom heap size"),
1177 );
1178
1179 let sandbox = UninitializedSandbox::from_snapshot(
1180 snapshot,
1181 None,
1182 #[cfg(crashdump)]
1183 Some(binary_path.clone()),
1184 )
1185 .expect("Failed to create sandbox from snapshot with custom heap");
1186
1187 let _evolved: MultiUseSandbox = sandbox.evolve().expect("Failed to evolve sandbox");
1188 }
1189
1190 {
1192 let mut cfg = SandboxConfiguration::default();
1193 cfg.set_scratch_size(256 * 1024); let env = GuestEnvironment::new(GuestBinary::FilePath(binary_path.clone()), None);
1196
1197 let snapshot = Arc::new(
1198 Snapshot::from_env(env, cfg)
1199 .expect("Failed to create snapshot with custom stack size"),
1200 );
1201
1202 let sandbox = UninitializedSandbox::from_snapshot(
1203 snapshot,
1204 None,
1205 #[cfg(crashdump)]
1206 Some(binary_path.clone()),
1207 )
1208 .expect("Failed to create sandbox from snapshot with custom stack");
1209
1210 let _evolved: MultiUseSandbox = sandbox.evolve().expect("Failed to evolve sandbox");
1211 }
1212
1213 {
1215 let mut cfg = SandboxConfiguration::default();
1216 cfg.set_input_data_size(64 * 1024); cfg.set_output_data_size(64 * 1024); let env = GuestEnvironment::new(GuestBinary::FilePath(binary_path.clone()), None);
1220
1221 let snapshot = Arc::new(
1222 Snapshot::from_env(env, cfg)
1223 .expect("Failed to create snapshot with custom buffer sizes"),
1224 );
1225
1226 let sandbox = UninitializedSandbox::from_snapshot(
1227 snapshot,
1228 None,
1229 #[cfg(crashdump)]
1230 Some(binary_path.clone()),
1231 )
1232 .expect("Failed to create sandbox from snapshot with custom buffers");
1233
1234 let _evolved: MultiUseSandbox = sandbox.evolve().expect("Failed to evolve sandbox");
1235 }
1236
1237 {
1239 let mut cfg = SandboxConfiguration::default();
1240 cfg.set_heap_size(32 * 1024 * 1024); cfg.set_scratch_size(256 * 1024 * 2); cfg.set_input_data_size(128 * 1024); cfg.set_output_data_size(128 * 1024); let env = GuestEnvironment::new(GuestBinary::FilePath(binary_path.clone()), None);
1246
1247 let snapshot = Arc::new(
1248 Snapshot::from_env(env, cfg)
1249 .expect("Failed to create snapshot with all custom settings"),
1250 );
1251
1252 let sandbox1 = UninitializedSandbox::from_snapshot(
1254 snapshot.clone(),
1255 None,
1256 #[cfg(crashdump)]
1257 Some(binary_path.clone()),
1258 )
1259 .expect("Failed to create sandbox1 from fully customized snapshot");
1260 let sandbox2 = UninitializedSandbox::from_snapshot(
1261 snapshot.clone(),
1262 None,
1263 #[cfg(crashdump)]
1264 Some(binary_path.clone()),
1265 )
1266 .expect("Failed to create sandbox2 from fully customized snapshot");
1267 let sandbox3 = UninitializedSandbox::from_snapshot(
1268 snapshot.clone(),
1269 None,
1270 #[cfg(crashdump)]
1271 Some(binary_path.clone()),
1272 )
1273 .expect("Failed to create sandbox3 from fully customized snapshot");
1274
1275 let _evolved1: MultiUseSandbox = sandbox1.evolve().expect("Failed to evolve sandbox1");
1276 let _evolved2: MultiUseSandbox = sandbox2.evolve().expect("Failed to evolve sandbox2");
1277 let _evolved3: MultiUseSandbox = sandbox3.evolve().expect("Failed to evolve sandbox3");
1278 }
1279
1280 {
1282 let binary_bytes = fs::read(&binary_path).expect("Failed to read binary file");
1283
1284 let snapshot = Arc::new(
1285 Snapshot::from_env(GuestBinary::Buffer(&binary_bytes), Default::default())
1286 .expect("Failed to create snapshot from buffer"),
1287 );
1288
1289 let sandbox = UninitializedSandbox::from_snapshot(
1290 snapshot,
1291 None,
1292 #[cfg(crashdump)]
1293 None,
1294 )
1295 .expect("Failed to create sandbox from buffer-based snapshot");
1296
1297 let _evolved: MultiUseSandbox = sandbox.evolve().expect("Failed to evolve sandbox");
1298 }
1299
1300 {
1302 let env = GuestEnvironment::new(GuestBinary::FilePath(binary_path.clone()), None);
1303
1304 let snapshot = Arc::new(
1305 Snapshot::from_env(env, Default::default()).expect("Failed to create snapshot"),
1306 );
1307
1308 let mut sandbox = UninitializedSandbox::from_snapshot(
1309 snapshot,
1310 None,
1311 #[cfg(crashdump)]
1312 Some(binary_path.clone()),
1313 )
1314 .expect("Failed to create sandbox from snapshot");
1315
1316 sandbox
1318 .register("CustomAdd", |a: i32, b: i32| Ok(a + b))
1319 .expect("Failed to register custom function");
1320
1321 let evolved: MultiUseSandbox = sandbox.evolve().expect("Failed to evolve sandbox");
1322
1323 let host_funcs = evolved
1325 .host_funcs
1326 .try_lock()
1327 .expect("Failed to lock host funcs");
1328
1329 let result = host_funcs
1330 .call_host_function(
1331 "CustomAdd",
1332 vec![ParameterValue::Int(10), ParameterValue::Int(20)],
1333 )
1334 .expect("Failed to call CustomAdd");
1335
1336 assert_eq!(result, ReturnValue::Int(30));
1337 }
1338
1339 {
1341 let init_data = [0xCA, 0xFE, 0xBA, 0xBE];
1342 let guest_env =
1343 GuestEnvironment::new(GuestBinary::FilePath(binary_path.clone()), Some(&init_data));
1344
1345 let snapshot = Arc::new(
1346 Snapshot::from_env(guest_env, Default::default())
1347 .expect("Failed to create snapshot with init data"),
1348 );
1349
1350 let sandbox = UninitializedSandbox::from_snapshot(
1351 snapshot,
1352 None,
1353 #[cfg(crashdump)]
1354 Some(binary_path.clone()),
1355 )
1356 .expect("Failed to create sandbox from snapshot with init data");
1357
1358 let _evolved: MultiUseSandbox = sandbox.evolve().expect("Failed to evolve sandbox");
1359 }
1360
1361 {
1363 let env = GuestEnvironment::new(GuestBinary::FilePath(binary_path.clone()), None);
1364 let orig_snapshot = Arc::new(
1365 Snapshot::from_env(env, Default::default())
1366 .expect("Failed to create snapshot with default config"),
1367 );
1368 let orig_sandbox = UninitializedSandbox::from_snapshot(
1369 orig_snapshot,
1370 None,
1371 #[cfg(crashdump)]
1372 Some(binary_path.clone()),
1373 )
1374 .expect("Failed to create orig_sandbox");
1375 let mut initialized_sandbox = orig_sandbox
1376 .evolve()
1377 .expect("Failed to evolve orig_sandbox");
1378 let new_snapshot = initialized_sandbox
1379 .snapshot()
1380 .expect("Failed to create new_snapshot");
1381 let new_sandbox = UninitializedSandbox::from_snapshot(
1382 new_snapshot,
1383 None,
1384 #[cfg(crashdump)]
1385 Some(binary_path.clone()),
1386 )
1387 .expect("Failed to create new_sandbox");
1388 let _evolved = new_sandbox.evolve().expect("Failed to evolve new_sandbox");
1389 }
1390 }
1391}