1mod config;
21mod digest;
22mod fsutil;
23mod media_types;
24pub(crate) mod reference;
25
26use std::path::{Path, PathBuf};
27
28use hyperlight_common::flatbuffer_wrappers::host_function_details::HostFunctionDetails;
29use hyperlight_common::vmem::PAGE_SIZE;
30use oci_spec::image::{
31 Descriptor, DescriptorBuilder, ImageIndex, ImageIndexBuilder, ImageManifest,
32 ImageManifestBuilder, MediaType, SCHEMA_VERSION,
33};
34
35use self::config::{Arch, CpuVendor, HostFunction, Hypervisor, MemoryLayout, OciSnapshotConfig};
36use self::digest::{Digest256, oci_digest, parse_oci_digest, verify_blob_bytes, verify_blob_file};
37use self::fsutil::{put_blob, put_blob_if_absent, read_bounded, replace_file_atomic};
38use self::media_types::{
39 ANNOTATION_ARCH, ANNOTATION_CPU, ANNOTATION_HYPERVISOR, ANNOTATION_REF_NAME, MT_CONFIG_CURRENT,
40 MT_CONFIG_V1, MT_SNAPSHOT_CURRENT, MT_SNAPSHOT_V1, SNAPSHOT_ABI_VERSION,
41};
42use self::reference::{OciDigest, OciReference, OciTag};
43use super::{NextAction, Snapshot};
44use crate::mem::layout::SandboxMemoryLayout;
45use crate::mem::memory_region::MemoryRegionFlags;
46use crate::mem::shared_mem::{ReadonlySharedMemory, SharedMemory};
47
48const OCI_LAYOUT_VERSION: &str = "1.0.0";
49
50const MAX_JSON_BLOB_SIZE: u64 = 1024 * 1024;
54
55fn check_json_blob_size(what: &str, len: usize) -> crate::Result<()> {
59 if len as u64 > MAX_JSON_BLOB_SIZE {
60 return Err(crate::new_error!(
61 "{} of {} bytes exceeds the {} byte maximum for a snapshot artifact",
62 what,
63 len,
64 MAX_JSON_BLOB_SIZE
65 ));
66 }
67 Ok(())
68}
69
70fn select_manifest<'a>(
77 index: &'a ImageIndex,
78 reference: &OciReference,
79 path: &Path,
80) -> crate::Result<&'a Descriptor> {
81 match reference {
82 OciReference::Tag(tag) => {
83 let mut matching = index.manifests().iter().filter(|d| {
84 d.annotations()
85 .as_ref()
86 .and_then(|a| a.get(ANNOTATION_REF_NAME))
87 .map(|s| s.as_str() == tag.as_str())
88 .unwrap_or(false)
89 });
90 match (matching.next(), matching.next()) {
91 (None, _) => {
92 let known: Vec<&str> = index
93 .manifests()
94 .iter()
95 .filter_map(|d| {
96 d.annotations()
97 .as_ref()
98 .and_then(|a| a.get(ANNOTATION_REF_NAME))
99 .map(|s| s.as_str())
100 })
101 .collect();
102 Err(crate::new_error!(
103 "no manifest tagged {:?} in OCI layout {:?}. Available tags: {:?}",
104 tag.as_str(),
105 path,
106 known
107 ))
108 }
109 (Some(_), Some(_)) => Err(crate::new_error!(
110 "OCI layout {:?} has multiple manifests tagged {:?}; tags must be unique",
111 path,
112 tag.as_str()
113 )),
114 (Some(d), None) => Ok(d),
115 }
116 }
117 OciReference::Digest(digest) => index
118 .manifests()
119 .iter()
120 .find(|d| d.digest().to_string() == digest.as_str())
121 .ok_or_else(|| {
122 crate::new_error!(
123 "no manifest with digest {} in OCI layout {:?}",
124 digest.as_str(),
125 path
126 )
127 }),
128 }
129}
130
131fn read_layout_marker(path: &Path) -> crate::Result<()> {
132 let layout_bytes = read_bounded(&path.join("oci-layout"), MAX_JSON_BLOB_SIZE)
133 .map_err(|e| crate::new_error!("failed to read oci-layout: {}", e))?;
134 let layout_json: serde_json::Value = serde_json::from_slice(&layout_bytes)
135 .map_err(|e| crate::new_error!("oci-layout is not valid JSON: {}", e))?;
136 let v = layout_json
137 .get("imageLayoutVersion")
138 .and_then(|v| v.as_str())
139 .ok_or_else(|| crate::new_error!("oci-layout missing imageLayoutVersion field"))?;
140 if v != OCI_LAYOUT_VERSION {
141 return Err(crate::new_error!(
142 "unsupported OCI image layout version {:?} (expected {:?})",
143 v,
144 OCI_LAYOUT_VERSION
145 ));
146 }
147 Ok(())
148}
149
150fn load_manifest(
151 path: &Path,
152 blobs_dir: &Path,
153 reference: &OciReference,
154 verify_blobs: bool,
155) -> crate::Result<ImageManifest> {
156 let index_bytes = read_bounded(&path.join("index.json"), MAX_JSON_BLOB_SIZE)
157 .map_err(|e| crate::new_error!("failed to read index.json: {}", e))?;
158 let index: ImageIndex = serde_json::from_slice(&index_bytes)
159 .map_err(|e| crate::new_error!("failed to parse index.json: {}", e))?;
160 let manifest_desc = select_manifest(&index, reference, path)?;
161 if !matches!(manifest_desc.media_type(), MediaType::ImageManifest) {
162 return Err(crate::new_error!(
163 "manifest descriptor for {} has unexpected media type {:?} (expected {:?})",
164 reference,
165 manifest_desc.media_type().to_string(),
166 MediaType::ImageManifest.to_string()
167 ));
168 }
169 let manifest_hex = parse_oci_digest(manifest_desc.digest())?;
170 let manifest_path = blobs_dir.join(&manifest_hex);
171 let manifest_bytes = read_bounded(&manifest_path, MAX_JSON_BLOB_SIZE)?;
172 if manifest_bytes.len() as u64 != manifest_desc.size() {
173 return Err(crate::new_error!(
174 "OCI manifest size mismatch: descriptor says {}, file is {}",
175 manifest_desc.size(),
176 manifest_bytes.len()
177 ));
178 }
179 if verify_blobs {
180 verify_blob_bytes("manifest", &manifest_bytes, &manifest_hex)?;
181 }
182 let manifest: ImageManifest = serde_json::from_slice(&manifest_bytes)
183 .map_err(|e| crate::new_error!("failed to parse OCI manifest JSON: {}", e))?;
184 if manifest.schema_version() != SCHEMA_VERSION {
185 return Err(crate::new_error!(
186 "unsupported OCI manifest schemaVersion {} (expected {})",
187 manifest.schema_version(),
188 SCHEMA_VERSION
189 ));
190 }
191 Ok(manifest)
192}
193
194fn load_config(
195 blobs_dir: &Path,
196 cfg_desc: &Descriptor,
197 verify_blobs: bool,
198) -> crate::Result<OciSnapshotConfig> {
199 let cfg_hex = parse_oci_digest(cfg_desc.digest())?;
200 let cfg_path = blobs_dir.join(&cfg_hex);
201 let cfg_bytes = read_bounded(&cfg_path, MAX_JSON_BLOB_SIZE)?;
202 if cfg_bytes.len() as u64 != cfg_desc.size() {
203 return Err(crate::new_error!(
204 "config blob size mismatch: descriptor says {}, file is {}",
205 cfg_desc.size(),
206 cfg_bytes.len()
207 ));
208 }
209 if verify_blobs {
210 verify_blob_bytes("config", &cfg_bytes, &cfg_hex)?;
211 }
212 let cfg: OciSnapshotConfig = serde_json::from_slice(&cfg_bytes)
213 .map_err(|e| crate::new_error!("failed to parse Hyperlight config JSON: {}", e))?;
214 cfg.validate_for_load()?;
215 Ok(cfg)
216}
217
218fn open_snapshot_blob(
219 blobs_dir: &Path,
220 snap_desc: &Descriptor,
221 expected_blob_len: u64,
222 verify_blobs: bool,
223) -> crate::Result<std::fs::File> {
224 let snap_hex = parse_oci_digest(snap_desc.digest())?;
225 let snap_path = blobs_dir.join(&snap_hex);
226
227 let mut snap_file = self::fsutil::open_no_follow(&snap_path)?;
228
229 let snap_file_len = snap_file
230 .metadata()
231 .map_err(|e| crate::new_error!("failed to stat snapshot blob: {}", e))?
232 .len();
233 if snap_file_len != expected_blob_len {
234 return Err(crate::new_error!(
235 "snapshot blob size mismatch: file is {} bytes, expected {} (memory_size)",
236 snap_file_len,
237 expected_blob_len,
238 ));
239 }
240 if snap_file_len != snap_desc.size() {
241 return Err(crate::new_error!(
242 "snapshot blob size {} disagrees with OCI descriptor size {}",
243 snap_file_len,
244 snap_desc.size()
245 ));
246 }
247 if verify_blobs {
248 verify_blob_file("snapshot", &mut snap_file, &snap_hex)?;
249 }
250 Ok(snap_file)
251}
252
253impl Snapshot {
254 pub fn save(&self, path: impl AsRef<Path>, tag: &OciTag) -> crate::Result<OciDigest> {
321 let path = path.as_ref();
322
323 let cfg = self.build_config()?;
326 let cfg_bytes = serde_json::to_vec_pretty(&cfg)
327 .map_err(|e| crate::new_error!("failed to serialise config JSON: {}", e))?;
328 check_json_blob_size("config blob", cfg_bytes.len())?;
329
330 match path.parent() {
334 Some(p) if !p.as_os_str().is_empty() => {
335 let parent_meta = std::fs::metadata(p).map_err(|e| {
336 crate::new_error!("save: parent directory {:?} not accessible: {}", p, e)
337 })?;
338 if !parent_meta.is_dir() {
339 return Err(crate::new_error!(
340 "save: parent of {:?} is not a directory",
341 path
342 ));
343 }
344 }
345 _ => {}
346 }
347 match std::fs::create_dir(path) {
348 Ok(()) => {}
349 Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {
350 let meta = std::fs::metadata(path)
351 .map_err(|e| crate::new_error!("save: failed to stat {:?}: {}", path, e))?;
352 if !meta.is_dir() {
353 return Err(crate::new_error!(
354 "save: {:?} exists and is not a directory",
355 path
356 ));
357 }
358 }
359 Err(e) => {
360 return Err(crate::new_error!(
361 "save: failed to create layout dir {:?}: {}",
362 path,
363 e
364 ));
365 }
366 }
367
368 let layout_marker = path.join("oci-layout");
373 let marker_existed = layout_marker
374 .try_exists()
375 .map_err(|e| crate::new_error!("save: failed to stat {:?}: {}", layout_marker, e))?;
376 if marker_existed {
377 let bytes = read_bounded(&layout_marker, MAX_JSON_BLOB_SIZE).map_err(|e| {
378 crate::new_error!("save: failed to read existing oci-layout: {}", e)
379 })?;
380 let v: serde_json::Value = serde_json::from_slice(&bytes).map_err(|e| {
381 crate::new_error!("save: existing oci-layout is not valid JSON: {}", e)
382 })?;
383 match v.get("imageLayoutVersion").and_then(|s| s.as_str()) {
384 Some(s) if s == OCI_LAYOUT_VERSION => {}
385 Some(other) => {
386 return Err(crate::new_error!(
387 "save: existing imageLayoutVersion {:?} is unsupported (expected {:?})",
388 other,
389 OCI_LAYOUT_VERSION
390 ));
391 }
392 None => {
393 return Err(crate::new_error!(
394 "save: existing oci-layout is missing imageLayoutVersion"
395 ));
396 }
397 }
398 }
399
400 let index_path = path.join("index.json");
401 let index_existed = index_path
402 .try_exists()
403 .map_err(|e| crate::new_error!("save: failed to stat {:?}: {}", index_path, e))?;
404 let mut manifests: Vec<Descriptor> = if index_existed {
405 let bytes = read_bounded(&index_path, MAX_JSON_BLOB_SIZE).map_err(|e| {
406 crate::new_error!("save: failed to read existing index.json: {}", e)
407 })?;
408 let existing: ImageIndex = serde_json::from_slice(&bytes).map_err(|e| {
409 crate::new_error!(
410 "save: existing index.json is not a valid OCI image index: {}",
411 e
412 )
413 })?;
414 existing.manifests().to_vec()
415 } else {
416 Vec::new()
417 };
418
419 let new_desc = self.write_blobs_and_build_descriptor(path, tag, &cfg, &cfg_bytes)?;
420 let written_digest = OciDigest::from_oci_spec_digest(new_desc.digest());
421
422 manifests.retain(|d| {
426 d.annotations()
427 .as_ref()
428 .and_then(|a| a.get(ANNOTATION_REF_NAME))
429 .map(|s| s.as_str() != tag.as_str())
430 .unwrap_or(true)
431 });
432 manifests.push(new_desc);
433
434 let index = ImageIndexBuilder::default()
435 .schema_version(SCHEMA_VERSION)
436 .media_type(MediaType::ImageIndex)
437 .manifests(manifests)
438 .build()
439 .map_err(|e| crate::new_error!("failed to build OCI index: {}", e))?;
440 let index_bytes = serde_json::to_vec_pretty(&index)
441 .map_err(|e| crate::new_error!("failed to serialise OCI index: {}", e))?;
442 check_json_blob_size("index.json", index_bytes.len())?;
443
444 if !marker_existed {
448 let layout_bytes = serde_json::to_vec(&serde_json::json!({
449 "imageLayoutVersion": OCI_LAYOUT_VERSION,
450 }))
451 .map_err(|e| crate::new_error!("failed to serialise oci-layout: {}", e))?;
452 replace_file_atomic(&layout_marker, &layout_bytes)?;
453 }
454
455 replace_file_atomic(&index_path, &index_bytes)?;
457
458 Ok(written_digest)
459 }
460
461 fn write_blobs_and_build_descriptor(
462 &self,
463 dir: &Path,
464 tag: &OciTag,
465 cfg: &OciSnapshotConfig,
466 cfg_bytes: &[u8],
467 ) -> crate::Result<Descriptor> {
468 let memory_bytes = self.memory.as_slice();
469 let memory_size = memory_bytes.len();
470 if memory_size == 0 || !memory_size.is_multiple_of(PAGE_SIZE) {
471 return Err(crate::new_error!(
472 "snapshot memory size {} must be a non-zero multiple of PAGE_SIZE",
473 memory_size
474 ));
475 }
476
477 let blobs_dir = dir.join("blobs").join("sha256");
478 std::fs::create_dir_all(&blobs_dir).map_err(|e| {
479 crate::new_error!("failed to create OCI blobs dir {:?}: {}", blobs_dir, e)
480 })?;
481
482 let snapshot_digest = Digest256::from_bytes(memory_bytes);
484 put_blob_if_absent(&blobs_dir, &snapshot_digest, memory_bytes)?;
485
486 let cfg_digest = Digest256::from_bytes(cfg_bytes);
488 put_blob(&blobs_dir, &cfg_digest, cfg_bytes)?;
489
490 let config_descriptor = DescriptorBuilder::default()
492 .media_type(MediaType::Other(MT_CONFIG_CURRENT.to_string()))
493 .digest(oci_digest(&cfg_digest)?)
494 .size(cfg_bytes.len() as u64)
495 .build()
496 .map_err(|e| crate::new_error!("failed to build config descriptor: {}", e))?;
497 let snapshot_descriptor = DescriptorBuilder::default()
498 .media_type(MediaType::Other(MT_SNAPSHOT_CURRENT.to_string()))
499 .digest(oci_digest(&snapshot_digest)?)
500 .size(memory_size as u64)
501 .build()
502 .map_err(|e| crate::new_error!("failed to build snapshot descriptor: {}", e))?;
503 let manifest = ImageManifestBuilder::default()
508 .schema_version(SCHEMA_VERSION)
509 .media_type(MediaType::ImageManifest)
510 .artifact_type(MediaType::Other(MT_CONFIG_CURRENT.to_string()))
511 .config(config_descriptor)
512 .layers(vec![snapshot_descriptor])
513 .build()
514 .map_err(|e| crate::new_error!("failed to build OCI manifest: {}", e))?;
515 let manifest_bytes = serde_json::to_vec_pretty(&manifest)
516 .map_err(|e| crate::new_error!("failed to serialise OCI manifest: {}", e))?;
517 check_json_blob_size("manifest blob", manifest_bytes.len())?;
518 let manifest_digest = Digest256::from_bytes(&manifest_bytes);
519 put_blob(&blobs_dir, &manifest_digest, &manifest_bytes)?;
520
521 let mut anns = std::collections::HashMap::new();
522 anns.insert(ANNOTATION_REF_NAME.to_string(), tag.as_str().to_string());
523 anns.insert(ANNOTATION_ARCH.to_string(), cfg.arch.as_str().to_string());
524 anns.insert(
525 ANNOTATION_HYPERVISOR.to_string(),
526 cfg.hypervisor.as_str().to_string(),
527 );
528 anns.insert(
529 ANNOTATION_CPU.to_string(),
530 cfg.cpu_vendor.as_str().to_string(),
531 );
532 DescriptorBuilder::default()
533 .media_type(MediaType::ImageManifest)
534 .digest(oci_digest(&manifest_digest)?)
535 .size(manifest_bytes.len() as u64)
536 .annotations(anns)
537 .build()
538 .map_err(|e| crate::new_error!("failed to build manifest descriptor: {}", e))
539 }
540
541 fn build_config(&self) -> crate::Result<OciSnapshotConfig> {
542 let (entrypoint_addr, sregs) = match (self.entrypoint, self.sregs.as_ref()) {
543 (NextAction::Call(addr), Some(sregs)) => (addr, sregs),
544 (NextAction::Call(_), None) => {
545 return Err(crate::new_error!(
546 "snapshot inconsistent: Call entrypoint must have sregs"
547 ));
548 }
549 (NextAction::Initialise(_), _) => {
550 return Err(crate::new_error!(
551 "pre-init snapshots cannot be persisted. Only a snapshot taken after the guest has run can be saved to disk"
552 ));
553 }
554 #[cfg(test)]
555 (NextAction::None, _) => {
556 return Err(crate::new_error!(
557 "snapshot with NextAction::None cannot be persisted"
558 ));
559 }
560 };
561
562 let host_functions = match &self.host_functions.host_functions {
563 Some(v) => v.iter().map(HostFunction::from).collect(),
564 None => Vec::new(),
565 };
566
567 let l = &self.layout;
568 Ok(OciSnapshotConfig {
569 hyperlight_version: env!("CARGO_PKG_VERSION").to_string(),
570 arch: Arch::current(),
571 abi_version: SNAPSHOT_ABI_VERSION,
572 hypervisor: Hypervisor::current()
573 .ok_or_else(|| crate::new_error!("no hypervisor available to tag snapshot"))?,
574 cpu_vendor: CpuVendor::current(),
575 stack_top_gva: self.stack_top_gva,
576 entrypoint_addr,
577 sregs: *sregs,
578 layout: MemoryLayout {
579 input_data_size: l.input_data_size,
580 output_data_size: l.output_data_size,
581 heap_size: l.heap_size,
582 code_size: l.code_size,
583 init_data_size: l.init_data_size,
584 init_data_permissions: l.init_data_permissions.map(|f| f.bits()),
585 scratch_size: l.get_scratch_size(),
586 snapshot_size: l.snapshot_size,
587 pt_size: l.pt_size,
588 },
589 memory_size: self.memory.mem_size() as u64,
590 host_functions,
591 snapshot_generation: self.snapshot_generation,
592 })
593 }
594
595 pub fn load(path: impl AsRef<Path>, reference: impl Into<OciReference>) -> crate::Result<Self> {
661 Self::load_inner(path.as_ref(), &reference.into(), false)
662 }
663
664 pub fn checked_load(
677 path: impl AsRef<Path>,
678 reference: impl Into<OciReference>,
679 ) -> crate::Result<Self> {
680 Self::load_inner(path.as_ref(), &reference.into(), true)
681 }
682
683 fn load_inner(
684 path: &Path,
685 reference: &OciReference,
686 verify_blobs: bool,
687 ) -> crate::Result<Self> {
688 let meta = std::fs::metadata(path)
689 .map_err(|e| crate::new_error!("load failed to stat {:?}: {}", path, e))?;
690 if !meta.is_dir() {
691 return Err(crate::new_error!("load path {:?} is not a directory", path));
692 }
693
694 let blobs_dir: PathBuf = path.join("blobs").join("sha256");
695
696 read_layout_marker(path)?;
698
699 let manifest = load_manifest(path, &blobs_dir, reference, verify_blobs)?;
707 let cfg_desc = manifest.config();
708 let cfg_media = cfg_desc.media_type().to_string();
711 match cfg_media.as_str() {
712 MT_CONFIG_V1 => {}
713 other => {
714 return Err(crate::new_error!(
715 "unexpected config media type {:?} (supported: {:?})",
716 other,
717 MT_CONFIG_V1
718 ));
719 }
720 }
721 match manifest.artifact_type() {
728 Some(at) if at.to_string() == cfg_media => {}
729 Some(at) => {
730 return Err(crate::new_error!(
731 "OCI manifest artifactType {:?} does not match config media type {:?}",
732 at.to_string(),
733 cfg_media
734 ));
735 }
736 None => {
737 return Err(crate::new_error!(
738 "OCI manifest is missing required artifactType (expected {:?})",
739 cfg_media
740 ));
741 }
742 }
743 let layers = manifest.layers();
744 if layers.len() != 1 {
745 return Err(crate::new_error!(
746 "expected exactly one OCI layer (the snapshot), found {}",
747 layers.len()
748 ));
749 }
750 let snap_desc = &layers[0];
751 let snap_media = snap_desc.media_type().to_string();
752 match snap_media.as_str() {
753 MT_SNAPSHOT_V1 => {}
754 other => {
755 return Err(crate::new_error!(
756 "unexpected snapshot layer media type {:?} (supported: {:?})",
757 other,
758 MT_SNAPSHOT_V1
759 ));
760 }
761 }
762
763 let cfg = load_config(&blobs_dir, cfg_desc, verify_blobs)?;
765
766 let snap_file = open_snapshot_blob(&blobs_dir, snap_desc, cfg.memory_size, verify_blobs)?;
770
771 let mut sbox_cfg = crate::sandbox::SandboxConfiguration::default();
773 sbox_cfg.set_input_data_size(cfg.layout.input_data_size);
774 sbox_cfg.set_output_data_size(cfg.layout.output_data_size);
775 sbox_cfg.set_heap_size(cfg.layout.heap_size as u64);
776 sbox_cfg.set_scratch_size(cfg.layout.scratch_size);
777 let init_data_perms = match cfg.layout.init_data_permissions {
778 None => None,
779 Some(bits) => Some(MemoryRegionFlags::from_bits(bits).ok_or_else(|| {
780 crate::new_error!(
781 "snapshot init_data_permissions {:#x} contains unknown flag bits",
782 bits
783 )
784 })?),
785 };
786 let mut layout = SandboxMemoryLayout::new(
787 sbox_cfg,
788 cfg.layout.code_size,
789 cfg.layout.init_data_size,
790 init_data_perms,
791 )?;
792 if let Some(pt) = cfg.layout.pt_size {
794 layout.set_pt_size(pt)?;
795 }
796 layout.set_snapshot_size(cfg.layout.snapshot_size);
797
798 let required_memory_size = layout.get_memory_size()? as u64;
806 if (layout.snapshot_size as u64) < required_memory_size {
807 return Err(crate::new_error!(
808 "snapshot snapshot_size ({}) is smaller than the layout size ({})",
809 layout.snapshot_size,
810 required_memory_size
811 ));
812 }
813
814 let memory = ReadonlySharedMemory::from_file(&snap_file, layout.snapshot_size)?;
825
826 if memory.mem_size() as u64 != cfg.memory_size {
833 return Err(crate::new_error!(
834 "mapped snapshot size ({}) does not match config memory_size ({}); the blob may have changed during loading",
835 memory.mem_size(),
836 cfg.memory_size
837 ));
838 }
839
840 let entrypoint = NextAction::Call(cfg.entrypoint_addr);
842
843 let snapshot_generation = cfg.snapshot_generation;
845 let host_funcs_vec: Vec<
846 hyperlight_common::flatbuffer_wrappers::host_function_definition::HostFunctionDefinition,
847 > = cfg.host_functions.into_iter().map(Into::into).collect();
848 let host_functions = if host_funcs_vec.is_empty() {
849 HostFunctionDetails {
850 host_functions: None,
851 }
852 } else {
853 HostFunctionDetails {
854 host_functions: Some(host_funcs_vec),
855 }
856 };
857
858 Ok(Snapshot {
859 layout,
860 memory,
861 load_info: crate::mem::exe::LoadInfo::dummy(),
862 stack_top_gva: cfg.stack_top_gva,
863 sregs: Some(cfg.sregs),
864 entrypoint,
865 snapshot_generation,
866 host_functions,
867 })
868 }
869}