Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 31 additions & 3 deletions verifier/src/verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,10 @@ fn collect_rtmr_mismatch(
// Bump whenever expected RTMR computation changes so stale entries get ignored.
// v2: edk2-stable202505 OVMF RTMR[0] layout (added 4 events, reshaped BootOrder
// and Boot0000); the legacy 13-event log no longer matches any in-field image.
const MEASUREMENT_CACHE_VERSION: u32 = 2;
// v3: resolve OVMF variant from the image's metadata.json (explicit field, or
// `version`) when vm_config doesn't declare one; previous versions silently
// fell back to image-name parsing which fails for `dstack-X.Y.Z-<hash>` dirs.
const MEASUREMENT_CACHE_VERSION: u32 = 3;

#[derive(Clone, Serialize, Deserialize)]
struct CachedMeasurement {
Expand All @@ -134,6 +137,11 @@ struct ImagePaths {
kernel_path: PathBuf,
initrd_path: PathBuf,
kernel_cmdline: String,
/// OVMF variant resolved from the image's metadata.json: the explicit
/// `ovmf_variant` field when present, otherwise derived from `version`.
/// `None` only when metadata.json declares neither — callers should then
/// fall back to image-name heuristics.
image_ovmf_variant: Option<dstack_types::OvmfVariant>,
}

pub struct CvmVerifier {
Expand Down Expand Up @@ -248,15 +256,25 @@ impl CvmVerifier {
kernel_path: &Path,
initrd_path: &Path,
kernel_cmdline: &str,
image_ovmf_variant: Option<dstack_types::OvmfVariant>,
) -> Result<TdxMeasurementDetails> {
let firmware = fw_path.display().to_string();
let kernel = kernel_path.display().to_string();
let initrd = initrd_path.display().to_string();

// Prefer the explicit variant the image declared; fall back to parsing
// the version out of the image name for pre-`ovmf_variant` deployments.
// Resolve OVMF variant in priority order:
// 1. `vm_config.ovmf_variant` — explicit declaration from the VMM
// (only emitted by VMM 0.5.11+; deployments persisted by older
// VMMs are `None` and need a fallback).
// 2. `image_ovmf_variant` — resolved from the downloaded image's
// metadata.json (explicit field, or derived from `version`).
// This catches the common case of an old VMM serving a new image.
// 3. `ovmf_variant_for_image` on `vm_config.image` — last-resort
// parsing of the image directory name, kept only for legacy
// images whose metadata.json predates the `version` field.
let ovmf_variant = vm_config
.ovmf_variant
.or(image_ovmf_variant)
.unwrap_or_else(|| dstack_mr::ovmf_variant_for_image(vm_config.image.as_deref()));

let details = dstack_mr::Machine::builder()
Expand Down Expand Up @@ -295,13 +313,15 @@ impl CvmVerifier {
kernel_path: &Path,
initrd_path: &Path,
kernel_cmdline: &str,
image_ovmf_variant: Option<dstack_types::OvmfVariant>,
) -> Result<TdxMeasurements> {
self.compute_measurement_details(
vm_config,
fw_path,
kernel_path,
initrd_path,
kernel_cmdline,
image_ovmf_variant,
)
.map(|details| details.measurements)
}
Expand All @@ -313,6 +333,7 @@ impl CvmVerifier {
kernel_path: &Path,
initrd_path: &Path,
kernel_cmdline: &str,
image_ovmf_variant: Option<dstack_types::OvmfVariant>,
) -> Result<TdxMeasurements> {
let cache_key = Self::vm_config_cache_key(vm_config)?;

Expand All @@ -326,6 +347,7 @@ impl CvmVerifier {
kernel_path,
initrd_path,
kernel_cmdline,
image_ovmf_variant,
)?;

if let Err(e) = self.store_measurements_in_cache(&cache_key, &measurements) {
Expand Down Expand Up @@ -367,13 +389,17 @@ impl CvmVerifier {
let fw_path = image_dir.join(&image_info.bios);
let kernel_path = image_dir.join(&image_info.kernel);
let initrd_path = image_dir.join(&image_info.initrd);
let image_ovmf_variant = image_info
.ovmf_variant
.or_else(|| dstack_mr::ovmf_variant_for_version(&image_info.version).ok());
let kernel_cmdline = image_info.cmdline + " initrd=initrd";

Ok(ImagePaths {
fw_path,
kernel_path,
initrd_path,
kernel_cmdline,
image_ovmf_variant,
})
}

Expand All @@ -394,6 +420,7 @@ impl CvmVerifier {
&image_paths.kernel_path,
&image_paths.initrd_path,
&image_paths.kernel_cmdline,
image_paths.image_ovmf_variant,
)
}

Expand Down Expand Up @@ -556,6 +583,7 @@ impl CvmVerifier {
&image_paths.kernel_path,
&image_paths.initrd_path,
&image_paths.kernel_cmdline,
image_paths.image_ovmf_variant,
)
.context("Failed to compute expected measurements")?;

Expand Down
1 change: 1 addition & 0 deletions vmm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ load_config.workspace = true
key-provider-client.workspace = true
dstack-port-forward.workspace = true
dstack-types.workspace = true
dstack-mr.workspace = true
hex_fmt.workspace = true
lspci.workspace = true
base64.workspace = true
Expand Down
11 changes: 10 additions & 1 deletion vmm/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1188,7 +1188,16 @@ fn make_vm_config(cfg: &Config, manifest: &Manifest, image: &Image) -> Result<se
host_share_mode: cfg.cvm.host_share_mode.clone(),
hotplug_off: cfg.cvm.qemu_hotplug_off,
image: Some(manifest.image.clone()),
ovmf_variant: image.info.ovmf_variant,
// Prefer the explicit field from metadata.json; fall back to deriving
// the variant from the image's `version` string. Without this fallback,
// pre-0.5.11 OS images (whose metadata.json predates the ovmf_variant
// field) ship with `None` and force verifiers to guess from the image
// directory name — which fails for the standard `dstack-X.Y.Z-<hash>`
// naming convention.
ovmf_variant: image
.info
.ovmf_variant
.or_else(|| dstack_mr::ovmf_variant_for_version(&image.info.version).ok()),
})?;
// For backward compatibility
config["spec_version"] = serde_json::Value::from(1);
Expand Down
Loading