diff --git a/nvx-rs/README.md b/nvx-rs/README.md index fddbb10a8..61f801f6e 100644 --- a/nvx-rs/README.md +++ b/nvx-rs/README.md @@ -125,7 +125,7 @@ changes only when the module's purpose does. | [`nvx::exec`](#nvxexec) | The execution engine: the streams, credit, and outcome of an execution's two sides, without I/O, for any framing | | [`nvx::host::runtime`](#nvxhostruntime) | `Runtime`: what every launch shares, the runtime's files and their identity, the hypervisor and its probe, and the launch options | | [`nvx::host::vm`](#nvxhostvm) | A private VM directory, cold and capture preparation, owned cold-launch command, verified detached launch, confirmed stop, and caller-saved reattachment | -| [`nvx::host::snapshot`](#nvxhostsnapshot) | Strict private snapshot directories, verified metadata, VM compatibility, captured NIC addresses, and time eligibility | +| [`nvx::host::snapshot`](#nvxhostsnapshot) | Strict private snapshots, post-exit capture finishing, verified metadata, VM compatibility, captured NIC addresses, and time eligibility | | [`nvx::host::config`](#nvxhostconfig) | `VmConfig` and `CaptureConfig`: pure configuration, cold-boot kernel/OpenVMM arguments, portable length checks, and capture identity | | [`nvx::host::restore`](#nvxhostrestore) | A caller-driven restore to Ready without I/O or a clock, and its peer-verified restore-ready listener | | [`nvx::host::artifacts`](#nvxhostartifacts) | Locating the OpenVMM executable and the guest artifacts that a VM runs, and their runtime identity | @@ -619,8 +619,8 @@ responsive shell input, and a verified clean exit; it downloads no artifact. ### `nvx::host::snapshot` `SnapshotKey` and `SnapshotManifest` own the compatibility key and strict -OpenVMM manifest reader. `SnapshotManifest::time` exposes the capture's -`TimeContract`, whose `eligibility` checks its restore window at a `HostTime`; +OpenVMM manifest reader. `SnapshotManifest::time` and `SnapshotKey::time` expose +the capture's `TimeContract`, whose `eligibility` checks its restore window at a `HostTime`; `HostTime::now` samples the host's clock and identities. The existing `host::template` names remain aliases of these same types. @@ -629,6 +629,12 @@ The key's canonical encoding and identity use the snapshot names and omit and no reader or migration of the prior encoding is supported. `CaptureClass` still records the legacy capture inputs, including its guest build. +The persisted key includes the complete per-snapshot time contract and must +agree with the manifest's time. Its compatibility identity excludes capture +anchors, host and boot identities, lineage and cut-to-anchor time: captures +at different times can have different key bytes and the same `KeyIdentity`. +The declared rates and time ABI remain compatibility inputs. + `Snapshot::open` checks exactly `key.bin`, `snapshot.json` and `openvmm/{manifest.bin,memory.bin,state.bin}`. Directories are private, files are regular and private to their owner, artifact lengths equal the strict @@ -676,6 +682,15 @@ effective devices independently of runtime paths or network policy. `vm::Prepared::capture_context` returns it for the caller to clone and serialize; NVX owns no capture-context store or persistence format. +`finish_capture(&VmDir, &CaptureContext, &Exited)` requires the capture VM's +proved exit and successful outcome, checks the OpenVMM snapshot against the +context, and atomically writes `key.bin` and informational `snapshot.json` +beside `openvmm/`. It returns `CapturedSnapshot { key }`. The caller moves +only those three entries into its own private, immutable snapshot directory; +logs, outcomes and other VM files remain where they are. A failure can be +corrected and retried while the VM directory and capture context stay intact, +including when one metadata file was already written. + ### `nvx::host::config` `VmConfig`: the configuration of a VM as one value, its processors, its memory diff --git a/nvx-rs/public-api/linux.txt b/nvx-rs/public-api/linux.txt index 9d90dd239..7ca14db6c 100644 --- a/nvx-rs/public-api/linux.txt +++ b/nvx-rs/public-api/linux.txt @@ -3038,6 +3038,8 @@ field nvx::host::snapshot::CaptureClass::runtime field nvx::host::snapshot::CaptureClass::scratch method nvx::host::snapshot::CaptureClass::to_bytes struct nvx::host::snapshot::CaptureContext +struct nvx::host::snapshot::CapturedSnapshot +field nvx::host::snapshot::CapturedSnapshot::key struct nvx::host::snapshot::ClassIdentity method nvx::host::snapshot::ClassIdentity::as_bytes method nvx::host::snapshot::ClassIdentity::from_bytes @@ -3115,6 +3117,7 @@ method nvx::host::snapshot::SnapshotKey::processors method nvx::host::snapshot::SnapshotKey::runtime method nvx::host::snapshot::SnapshotKey::scratch method nvx::host::snapshot::SnapshotKey::speaks_current_contracts +method nvx::host::snapshot::SnapshotKey::time method nvx::host::snapshot::SnapshotKey::to_bytes struct nvx::host::snapshot::SnapshotManifest method nvx::host::snapshot::SnapshotManifest::created_at @@ -3138,6 +3141,7 @@ field nvx::host::snapshot::TimeContract::host_id field nvx::host::snapshot::TimeContract::lineage field nvx::host::snapshot::TimeContract::tsc_frequency_hz field nvx::host::snapshot::TimeContract::tsc_tolerance_ppm +fn nvx::host::snapshot::finish_capture mod nvx::host::store struct nvx::host::store::ArtifactIdentity method nvx::host::store::ArtifactIdentity::as_str @@ -3695,6 +3699,7 @@ method nvx::host::template::TemplateKey::processors method nvx::host::template::TemplateKey::runtime method nvx::host::template::TemplateKey::scratch method nvx::host::template::TemplateKey::speaks_current_contracts +method nvx::host::template::TemplateKey::time method nvx::host::template::TemplateKey::to_bytes struct nvx::host::template::TemplateManifest method nvx::host::template::TemplateManifest::created_at diff --git a/nvx-rs/public-api/windows.txt b/nvx-rs/public-api/windows.txt index bb4241b0b..312906a37 100644 --- a/nvx-rs/public-api/windows.txt +++ b/nvx-rs/public-api/windows.txt @@ -1624,6 +1624,8 @@ field nvx::host::snapshot::CaptureClass::runtime field nvx::host::snapshot::CaptureClass::scratch method nvx::host::snapshot::CaptureClass::to_bytes struct nvx::host::snapshot::CaptureContext +struct nvx::host::snapshot::CapturedSnapshot +field nvx::host::snapshot::CapturedSnapshot::key struct nvx::host::snapshot::ClassIdentity method nvx::host::snapshot::ClassIdentity::as_bytes method nvx::host::snapshot::ClassIdentity::from_bytes @@ -1701,6 +1703,7 @@ method nvx::host::snapshot::SnapshotKey::processors method nvx::host::snapshot::SnapshotKey::runtime method nvx::host::snapshot::SnapshotKey::scratch method nvx::host::snapshot::SnapshotKey::speaks_current_contracts +method nvx::host::snapshot::SnapshotKey::time method nvx::host::snapshot::SnapshotKey::to_bytes struct nvx::host::snapshot::SnapshotManifest method nvx::host::snapshot::SnapshotManifest::created_at @@ -1724,6 +1727,7 @@ field nvx::host::snapshot::TimeContract::host_id field nvx::host::snapshot::TimeContract::lineage field nvx::host::snapshot::TimeContract::tsc_frequency_hz field nvx::host::snapshot::TimeContract::tsc_tolerance_ppm +fn nvx::host::snapshot::finish_capture mod nvx::host::store struct nvx::host::store::ArtifactIdentity method nvx::host::store::ArtifactIdentity::as_str @@ -2280,6 +2284,7 @@ method nvx::host::template::TemplateKey::processors method nvx::host::template::TemplateKey::runtime method nvx::host::template::TemplateKey::scratch method nvx::host::template::TemplateKey::speaks_current_contracts +method nvx::host::template::TemplateKey::time method nvx::host::template::TemplateKey::to_bytes struct nvx::host::template::TemplateManifest method nvx::host::template::TemplateManifest::created_at diff --git a/nvx-rs/src/host/mod.rs b/nvx-rs/src/host/mod.rs index 40a7be2d7..de25eda26 100644 --- a/nvx-rs/src/host/mod.rs +++ b/nvx-rs/src/host/mod.rs @@ -105,3 +105,4 @@ pub mod template; pub(crate) mod tests; pub mod transport; pub mod vm; +mod vm_directory; diff --git a/nvx-rs/src/host/snapshot.rs b/nvx-rs/src/host/snapshot.rs index f464c1958..6f65cefbf 100644 --- a/nvx-rs/src/host/snapshot.rs +++ b/nvx-rs/src/host/snapshot.rs @@ -4,6 +4,9 @@ //! description. The caller keeps the directory and its entries private and immutable while //! using the opened snapshot. //! +//! [`finish_capture`] validates an exited capture and writes its key and informational +//! description without publishing or removing any VM-directory entry. +//! //! [`SnapshotKey`] derives a compatibility key from a capture's class and manifest. //! [`SnapshotManifest::read`] reads the manifest that OpenVMM wrote, and //! [`TimeContract::eligibility`] checks its restore window at a [`HostTime`]. @@ -12,9 +15,10 @@ //! # Canonical encoding //! //! A key and a class each have one canonical encoding, which [`SnapshotKey::to_bytes`] and -//! [`CaptureClass::to_bytes`] write, and whose SHA-256 digest, after a domain of its own, is its -//! identity, a [`KeyIdentity`] or a [`ClassIdentity`]. The encoding starts with its version, and -//! each value in it has exactly one encoding: +//! [`CaptureClass::to_bytes`] write. A key's [`KeyIdentity`] digests its compatibility prefix, +//! excluding the appended capture-time anchors and identities; a [`ClassIdentity`] digests the +//! complete class encoding. Both use SHA-256 after a domain of their own. Each encoding starts +//! with its version, and each value in it has exactly one encoding: //! //! - An integer is its fixed number of little-endian bytes: one for a `u8`, two for a `u16`, four //! for a `u32`, and eight for a `u64`. @@ -29,6 +33,7 @@ //! in `tests/vectors/snapshot-key-v1.txt` fix version 1 of a key's encoding and of a class's. mod canonical; +mod capture; pub(super) mod eligibility; pub(super) mod key; pub(super) mod manifest; @@ -54,6 +59,8 @@ use crate::host::{filesystem, platform, sandbox, seal}; use crate::net::Ipv4Cidr; use crate::{Error, Hypervisor, Result}; +pub use self::capture::{CapturedSnapshot, finish_capture}; +pub(super) use self::capture::{check_exit, check_snapshot}; pub use self::eligibility::{Downtime, DowntimeSource, Eligibility, HostTime, MAX_DOWNTIME_NS}; pub use self::key::{CaptureClass, ClassIdentity, Invariants, KeyIdentity, SnapshotKey}; pub use self::manifest::{ @@ -67,6 +74,9 @@ pub const DESCRIPTION_FILE: &str = "snapshot.json"; /// The directory of OpenVMM's manifest, RAM and saved state. pub const OPENVMM_DIR: &str = "openvmm"; +/// OpenVMM's exact platform-snapshot inventory, in name order. +pub(super) const SNAPSHOT_ARTIFACTS: [&str; 3] = [MANIFEST_NAME, "memory.bin", "state.bin"]; + /// The checked configuration and effective devices of a blockless platform capture. /// /// [`crate::host::vm::Prepared::capture_context`] returns it after verifying the actual sparse @@ -129,30 +139,10 @@ impl Snapshot { pub fn open(dir: &Path) -> Result { let dir = absolute(&normalized(dir)).map_err(|error| entry_error(dir, error))?; check_directory(&dir, &[KEY_FILE, DESCRIPTION_FILE, OPENVMM_DIR])?; - let openvmm = dir.join(OPENVMM_DIR); - check_directory(&openvmm, &[MANIFEST_NAME, "memory.bin", "state.bin"])?; - for path in [ - dir.join(KEY_FILE), - dir.join(DESCRIPTION_FILE), - openvmm.join(MANIFEST_NAME), - ] { + for path in [dir.join(KEY_FILE), dir.join(DESCRIPTION_FILE)] { check_file(&path)?; } - let manifest = SnapshotManifest::read(&openvmm)?; - for (name, expected) in [ - ("memory.bin", manifest.memory_len()), - ("state.bin", manifest.state_len()), - ] { - let path = openvmm.join(name); - let found = check_file(&path)?.len(); - if found != expected { - return Err(Error::io(format!( - "the snapshot artifact {} is {found} bytes long, not the {expected} bytes \ - that its manifest records", - path.display() - ))); - } - } + let manifest = check_openvmm(&dir.join(OPENVMM_DIR))?; let path = dir.join(KEY_FILE); let bytes = regular::open_regular(&path) .and_then(|file| { @@ -372,6 +362,15 @@ fn restore_geometry(path: &Path, description: &str) -> Result { sandbox::block_geometry(&file, path, description) } +/// Adds the snapshot format's per-file owner-only checks to the legacy capture checks. +fn check_openvmm(path: &Path) -> Result { + let manifest = check_snapshot(path)?; + for name in SNAPSHOT_ARTIFACTS { + check_file(&path.join(name))?; + } + Ok(manifest) +} + /// Names the failing snapshot entry while retaining its I/O error. fn entry_error(path: &Path, error: io::Error) -> Error { Error::io(format!("cannot examine snapshot entry {}", path.display())).with_source(error) diff --git a/nvx-rs/src/host/snapshot/canonical.rs b/nvx-rs/src/host/snapshot/canonical.rs index e2f5aa52a..c300c8cd4 100644 --- a/nvx-rs/src/host/snapshot/canonical.rs +++ b/nvx-rs/src/host/snapshot/canonical.rs @@ -1,5 +1,5 @@ -//! The canonical encoding of a template's compatibility key and of a capture class: the bytes -//! whose SHA-256 digest is their identity. +//! The canonical encoding of a snapshot key and of a capture class. The key's compatibility +//! prefix, and the complete class encoding, are the bytes whose SHA-256 digest is their identity. //! //! Every value has exactly one encoding, and the reader refuses any other: //! diff --git a/nvx-rs/src/host/snapshot/capture.rs b/nvx-rs/src/host/snapshot/capture.rs new file mode 100644 index 000000000..fc1e51733 --- /dev/null +++ b/nvx-rs/src/host/snapshot/capture.rs @@ -0,0 +1,230 @@ +//! Post-exit capture validation and the two metadata files beside OpenVMM's snapshot. + +use std::fs; +use std::io; +use std::path::Path; + +use serde::Deserialize; +use serde_json::json; + +use super::{ + CaptureContext, DESCRIPTION_FILE, KEY_FILE, MANIFEST_NAME, OPENVMM_DIR, SNAPSHOT_ARTIFACTS, + SnapshotKey, SnapshotManifest, check_openvmm, +}; +use crate::host::platform; +use crate::host::process::Exited; +use crate::host::store::files::{sync_dir, write_private}; +use crate::host::store::regular::{open_regular, read_at_most}; +use crate::host::vm_directory::{OUTCOME, VmDir}; +use crate::{Error, ErrorKind, Result}; + +/// Longest outcome report of a capture's VM that the capture reads, in bytes. +const MAX_REPORT_LEN: u64 = 1024 * 1024; + +/// The metadata of a finished capture, whose files the caller may now publish. +#[derive(Debug)] +pub struct CapturedSnapshot { + /// The checked key written to [`KEY_FILE`]. + pub key: SnapshotKey, +} + +/// Finishes a capture after its VM has provably exited. +/// +/// `exited` must prove the exit of the VM that produced `dir` and `context`; the caller keeps +/// that association. Exit proof alone does not establish success: the outcome report must +/// record a successful capture, and the private OpenVMM snapshot must pass the existing strict +/// manifest, artifact and capture-context checks. +/// +/// Writes [`KEY_FILE`] and the informational [`DESCRIPTION_FILE`] beside [`OPENVMM_DIR`]. +/// The caller moves only those three entries into its own private snapshot directory and keeps +/// them immutable while using it. No other VM-directory entry is moved, removed or published. +/// A failure may be corrected and retried while the directory and capture context remain intact; +/// either metadata file may already have been replaced. +/// +/// Fails with [`ErrorKind::Exited`] for an absent or unsuccessful capture outcome, with +/// [`ErrorKind::Io`] for invalid files or a context/manifest mismatch, and otherwise as +/// [`SnapshotManifest::read`] and the checked context's configuration do. +/// +/// A caller cannot finish without exit proof: +/// +/// ```compile_fail +/// use nvx::host::{snapshot::{CaptureContext, finish_capture}, vm::VmDir}; +/// +/// fn without_exit(dir: &VmDir, context: &CaptureContext) { +/// finish_capture(dir, context); +/// } +/// ``` +pub fn finish_capture( + dir: &VmDir, + context: &CaptureContext, + _exited: &Exited, +) -> Result { + VmDir::open(dir.path())?; + check_exit(&dir.path().join(OUTCOME))?; + let manifest = check_openvmm(&dir.path().join(OPENVMM_DIR))?; + let key = SnapshotKey::from_capture(context, &manifest)?; + let time = manifest.time(); + let description = json!({ + "key": key.identity().to_string(), + "runtime": key.runtime(), + "hypervisor": key.hypervisor(), + "cpuProfile": key.cpu_profile(), + "capture": context.capture, + "nic": context.nic, + "console": context.console, + "createdAt": manifest.created_at(), + "openvmmVersion": manifest.openvmm_version(), + "memoryLen": manifest.memory_len(), + "stateLen": manifest.state_len(), + "time": { + "tscFrequencyHz": time.tsc_frequency_hz, + "tscTolerancePpm": time.tsc_tolerance_ppm, + "apicFrequencyHz": time.apic_frequency_hz, + "captureTsc": time.capture_tsc, + "captureUtcNs": time.capture_utc_ns, + "captureMonotonicNs": time.capture_monotonic_ns, + "hostClock": time.host_clock.as_str(), + "hostId": time.host_id.iter().map(|byte| format!("{byte:02x}")).collect::(), + "bootId": time.boot_id, + "lineage": time.lineage, + "cutToAnchorNs": time.cut_to_anchor_ns, + }, + }); + let description = serde_json::to_vec_pretty(&description).map_err(|error| { + Error::other("cannot encode the snapshot description").with_source(error) + })?; + write_private(&dir.path().join(KEY_FILE), &key.to_bytes())?; + write_private(&dir.path().join(DESCRIPTION_FILE), &description)?; + sync_dir(dir.path())?; + Ok(CapturedSnapshot { key }) +} + +/// The outcome report of a VM, as OpenVMM writes it, of which a capture reads the outcome. +#[derive(Deserialize)] +struct OutcomeReport { + /// The outcome of the VM. + outcome: Outcome, +} + +/// The outcome of a VM, as its outcome report records it. +#[derive(Deserialize)] +struct Outcome { + /// The operation of the VM: `capture` for a VM that OpenVMM captures. + operation: String, + /// The category of the outcome: `success` for a capture that OpenVMM published. + category: String, + /// The status code of the outcome: 0 for a capture that OpenVMM published. + status_code: i64, +} + +/// Checks that the outcome report records a capture that OpenVMM published successfully. +pub(in crate::host) fn check_exit(path: &Path) -> Result<()> { + let description = format!("the outcome report {}", path.display()); + let file = match open_regular(path) { + Ok(file) => file, + Err(error) if error.kind() == io::ErrorKind::NotFound => { + return Err(Error::exited(format!( + "the VM of the capture ended without {description}" + ))); + } + Err(error) => { + return Err(Error::io(format!("cannot read {description}")).with_source(error)); + } + }; + let bytes = read_at_most(file, MAX_REPORT_LEN, "an outcome report") + .map_err(|error| Error::io(format!("cannot read {description}")).with_source(error))?; + let report: OutcomeReport = serde_json::from_slice(&bytes) + .map_err(|error| Error::io(format!("{description} is malformed")).with_source(error))?; + let Outcome { + operation, + category, + status_code, + } = report.outcome; + if operation != "capture" || category != "success" || status_code != 0 { + return Err(Error::exited(format!( + "the VM of the capture ended without a capture: {description} records the outcome \ + {category} with status {status_code} of the operation {operation}" + ))); + } + Ok(()) +} + +/// Preserves the legacy capture's directory, strict manifest, regular artifact and length checks. +pub(in crate::host) fn check_snapshot(snapshot: &Path) -> Result { + let what = "the snapshot of the capture"; + let problem = platform::check_snapshot_dir(snapshot).map_err(|error| { + Error::io(format!("cannot open {what} {}", snapshot.display())).with_source(error) + })?; + if let Some(problem) = problem { + return Err(Error::io(format!( + "{what} {} {problem}", + snapshot.display() + ))); + } + let manifest = SnapshotManifest::read(snapshot).map_err(|error| { + if error.kind() != ErrorKind::NotFound { + return error; + } + let (_, message, source) = error.into_parts(); + let error = Error::io(message); + match source { + Some(source) => error.with_source(source), + None => error, + } + })?; + let mut names = fs::read_dir(snapshot) + .and_then(|entries| { + entries + .map(|entry| { + entry?.file_name().into_string().map_err(|name| { + io::Error::other(format!("the snapshot entry {name:?} is not Unicode")) + }) + }) + .collect::>>() + }) + .map_err(|error| super::entry_error(snapshot, error))?; + names.sort(); + if names != SNAPSHOT_ARTIFACTS { + return Err(Error::io(format!( + "{what} {} holds {}, not exactly the manifest, RAM, and saved state of a platform-tier \ + snapshot", + snapshot.display(), + names.join(", ") + ))); + } + for (name, length) in [ + (MANIFEST_NAME, None), + ("memory.bin", Some(manifest.memory_len())), + ("state.bin", Some(manifest.state_len())), + ] { + let path = snapshot.join(name); + let refused = |problem: String| { + Error::io(format!( + "the artifact {} of {what} {problem}", + path.display() + )) + }; + let file = + open_regular(&path).map_err(|error| refused(format!("cannot be used: {error}")))?; + if let Some(problem) = platform::private_file_problem(&file) + .map_err(|error| refused(format!("cannot be checked: {error}")))? + { + return Err(refused(problem)); + } + let found = file + .metadata() + .map_err(|error| refused(format!("cannot be examined: {error}")))? + .len(); + if let Some(length) = length + && found != length + { + return Err(refused(format!( + "is {found} bytes long, not the {length} bytes that its manifest records" + ))); + } + } + Ok(manifest) +} + +#[cfg(test)] +mod tests; diff --git a/nvx-rs/src/host/snapshot/capture/tests.rs b/nvx-rs/src/host/snapshot/capture/tests.rs new file mode 100644 index 000000000..93b534f66 --- /dev/null +++ b/nvx-rs/src/host/snapshot/capture/tests.rs @@ -0,0 +1,469 @@ +//! Finishing emulated capture output with genuine OS exit proof, without launching a VM. + +#![cfg(any(target_os = "linux", windows))] + +use std::io::Read; +use std::process::{Child, Command, Stdio}; +use std::time::{Duration, Instant}; + +use serde_json::Value; + +use super::*; +use crate::Hypervisor; +use crate::host::config::{Capacity, CaptureConfig, ConsoleAttachment, DiskCapture, NicConfig}; +use crate::host::process::{BootId, Opened, Process, ProcessRef}; +use crate::host::snapshot::Snapshot; +use crate::host::snapshot::tests::{MIB, ManifestSpec, SnapshotFixture, class}; +use crate::host::vm::create_private_dir; + +/// A producer kept alive through its stdin until its parent records its real identity. +#[test] +fn exit_helper() { + if std::env::var_os("NVX_FINISH_CAPTURE_EXIT_HELPER").is_some() { + std::io::stdin().read_to_end(&mut Vec::new()).unwrap(); + } +} + +/// Reaps only the child that this test owns, including when a proof assertion fails. +struct OwnedChild(Child); + +impl Drop for OwnedChild { + fn drop(&mut self) { + if self.0.try_wait().unwrap().is_none() { + self.0.kill().unwrap(); + } + self.0.wait().unwrap(); + } +} + +/// Waits for an owned emulated producer's actual exit, never forging a process reference. +fn exited() -> Exited { + let mut command = Command::new(std::env::current_exe().unwrap()); + command + .args(["--exact", "host::snapshot::capture::tests::exit_helper"]) + .env("NVX_FINISH_CAPTURE_EXIT_HELPER", "1") + .stdin(Stdio::piped()) + .stdout(Stdio::null()); + let mut child = OwnedChild(crate::host::tests::spawn_locked(&mut command).unwrap()); + let reference = ProcessRef { + pid: child.0.id(), + start_time: platform::process_start_time(child.0.id()).unwrap().unwrap(), + boot: BootId::current().unwrap(), + }; + let Opened::Running(process) = Process::open(&reference).unwrap() else { + panic!("the owned producer is waiting for its parent"); + }; + assert!(process.wait(Instant::now()).unwrap().is_none()); + drop(child.0.stdin.take()); + let proof = process + .wait(Instant::now() + Duration::from_secs(10)) + .unwrap() + .expect("the owned producer exited"); + assert!(child.0.wait().unwrap().success()); + proof +} + +/// A private VM directory containing OpenVMM's output but neither NVX metadata file. +struct Fixture { + /// Keeps the sparse snapshot's private temporary directory alive. + _snapshot: SnapshotFixture, + /// The caller-owned VM directory. + dir: VmDir, + /// The checked inputs that the emulated capture used. + context: CaptureContext, +} + +impl Fixture { + /// Uses the existing strict snapshot fixture and removes only its already-finished metadata. + fn new() -> Self { + let snapshot = SnapshotFixture::new(); + for name in [KEY_FILE, DESCRIPTION_FILE] { + fs::remove_file(snapshot.dir.join(name)).unwrap(); + } + let dir = VmDir::open(&snapshot.dir).unwrap(); + let class = class(); + let context = CaptureContext { + capture: CaptureConfig { + disks: DiskCapture::Fresh { + image: class.distro, + scratch: class.scratch, + }, + processors: Capacity { + boot: u64::from(class.boot_processors), + capacity: u64::from(class.processors), + }, + memory: Capacity { + boot: class.memory / MIB, + capacity: class.memory_capacity / MIB, + }, + invariants: class.invariants, + }, + nic: None, + console: ConsoleAttachment::Disconnected, + runtime: class.runtime, + hypervisor: class.hypervisor, + cpu_profile: Some(class.cpu_profile), + }; + let fixture = Self { + _snapshot: snapshot, + dir, + context, + }; + fixture.outcome("capture", "success", 0); + fixture + } + + /// Writes OpenVMM's capture outcome without any launch state. + fn outcome(&self, operation: &str, category: &str, status: i64) { + write_private( + &self.dir.path().join(OUTCOME), + &serde_json::to_vec(&json!({ + "outcome": { + "operation": operation, + "category": category, + "status_code": status, + }, + })) + .unwrap(), + ) + .unwrap(); + } + + /// Returns one of OpenVMM's artifact paths. + fn artifact(&self, name: &str) -> std::path::PathBuf { + self.dir.path().join(OPENVMM_DIR).join(name) + } + + /// Calls the public finalizer with the fixture's checked inputs. + fn finish(&self, exited: &Exited) -> Result { + finish_capture(&self.dir, &self.context, exited) + } + + /// Checks that validation has not written either metadata file. + fn unfinished(&self) { + assert!(!self.dir.path().join(KEY_FILE).exists()); + assert!(!self.dir.path().join(DESCRIPTION_FILE).exists()); + } +} + +#[test] +fn finish_writes_matching_metadata_and_only_its_three_entries_are_published() { + let mut fixture = Fixture::new(); + fixture.context.capture.invariants = super::super::Invariants::new() + .with("isolation.test", "kept") + .unwrap(); + let proof = exited(); + let outcome = fs::read(fixture.dir.path().join(OUTCOME)).unwrap(); + for (name, bytes) in [ + ("openvmm.log", b"VM log".as_slice()), + ("caller.txt", b"caller data"), + ] { + write_private(&fixture.dir.path().join(name), bytes).unwrap(); + } + let captured = fixture.finish(&proof).unwrap(); + let manifest = SnapshotManifest::read(&fixture.dir.path().join(OPENVMM_DIR)).unwrap(); + let key = + SnapshotKey::from_bytes(&fs::read(fixture.dir.path().join(KEY_FILE)).unwrap()).unwrap(); + assert_eq!(key, captured.key); + assert_eq!(key.time(), manifest.time()); + assert_eq!(key.runtime(), &fixture.context.runtime); + assert_eq!(key.invariants(), &fixture.context.capture.invariants); + key.check_manifest(&manifest).unwrap(); + let description: Value = + serde_json::from_slice(&fs::read(fixture.dir.path().join(DESCRIPTION_FILE)).unwrap()) + .unwrap(); + assert_eq!(description["key"], key.identity().to_string()); + assert_eq!( + description["runtime"], + serde_json::to_value(key.runtime()).unwrap() + ); + assert_eq!( + description["capture"], + serde_json::to_value(&fixture.context.capture).unwrap() + ); + assert_eq!(description["memoryLen"], manifest.memory_len()); + assert_eq!(description["stateLen"], manifest.state_len()); + assert_eq!( + description["time"]["captureTsc"], + manifest.time().capture_tsc + ); + assert_eq!( + description["time"]["captureUtcNs"], + manifest.time().capture_utc_ns + ); + assert_eq!( + description["time"]["bootId"], + manifest.time().boot_id.to_string() + ); + let published = fixture.dir.path().parent().unwrap().join("published"); + create_private_dir(&published).unwrap(); + for name in [KEY_FILE, DESCRIPTION_FILE, OPENVMM_DIR] { + fs::rename(fixture.dir.path().join(name), published.join(name)).unwrap(); + } + assert_eq!(Snapshot::open(&published).unwrap().key(), &captured.key); + assert_eq!(fs::read_dir(&published).unwrap().count(), 3); + assert_eq!(fs::read(fixture.dir.path().join(OUTCOME)).unwrap(), outcome); + assert_eq!( + fs::read(fixture.dir.path().join("openvmm.log")).unwrap(), + b"VM log" + ); + assert_eq!( + fs::read(fixture.dir.path().join("caller.txt")).unwrap(), + b"caller data" + ); + assert!(Snapshot::open(fixture.dir.path()).is_err()); +} + +#[test] +fn finish_refuses_absent_and_unsuccessful_capture_outcomes() { + let proof = exited(); + let fixture = Fixture::new(); + fs::remove_file(fixture.dir.path().join(OUTCOME)).unwrap(); + assert_eq!( + fixture.finish(&proof).unwrap_err().kind(), + ErrorKind::Exited + ); + fixture.unfinished(); + for (operation, category, status) in [ + ("run", "success", 0), + ("capture", "guest-exit", 3), + ("capture", "vmm-failure", 1), + ("capture", "success", 1), + ("capture", "failure", 0), + ] { + fixture.outcome(operation, category, status); + assert_eq!( + fixture.finish(&proof).unwrap_err().kind(), + ErrorKind::Exited + ); + fixture.unfinished(); + } +} + +#[test] +fn finish_uses_the_bounded_regular_outcome_reader() { + let proof = exited(); + let fixture = Fixture::new(); + let path = fixture.dir.path().join(OUTCOME); + for bytes in [ + b"not JSON".to_vec(), + br#"{"outcome":{"category":"success","status_code":0}}"#.to_vec(), + vec![b' '; MAX_REPORT_LEN as usize + 1], + ] { + write_private(&path, &bytes).unwrap(); + assert_eq!(fixture.finish(&proof).unwrap_err().kind(), ErrorKind::Io); + fixture.unfinished(); + } + fs::remove_file(&path).unwrap(); + fs::create_dir(&path).unwrap(); + assert_eq!(fixture.finish(&proof).unwrap_err().kind(), ErrorKind::Io); + fixture.unfinished(); +} + +#[test] +fn finish_refuses_every_missing_or_extra_openvmm_artifact() { + let proof = exited(); + for missing in [ + None, + Some(MANIFEST_NAME), + Some("memory.bin"), + Some("state.bin"), + ] { + let fixture = Fixture::new(); + match missing { + Some(name) => fs::remove_file(fixture.artifact(name)).unwrap(), + None => fs::rename( + fixture.dir.path().join(OPENVMM_DIR), + fixture.dir.path().join("saved-openvmm"), + ) + .unwrap(), + } + assert_eq!(fixture.finish(&proof).unwrap_err().kind(), ErrorKind::Io); + fixture.unfinished(); + } + let fixture = Fixture::new(); + write_private(&fixture.artifact("extra"), b"unexpected").unwrap(); + assert_eq!(fixture.finish(&proof).unwrap_err().kind(), ErrorKind::Io); + fixture.unfinished(); +} + +#[test] +fn finish_refuses_irregular_artifacts_and_both_recorded_length_mismatches() { + let proof = exited(); + for name in SNAPSHOT_ARTIFACTS { + let fixture = Fixture::new(); + fs::remove_file(fixture.artifact(name)).unwrap(); + fs::create_dir(fixture.artifact(name)).unwrap(); + assert_eq!(fixture.finish(&proof).unwrap_err().kind(), ErrorKind::Io); + fixture.unfinished(); + } + for name in ["memory.bin", "state.bin"] { + for difference in [-1, 1] { + let fixture = Fixture::new(); + let file = fs::OpenOptions::new() + .write(true) + .open(fixture.artifact(name)) + .unwrap(); + let length = file.metadata().unwrap().len(); + file.set_len(length.checked_add_signed(difference).unwrap()) + .unwrap(); + drop(file); + assert_eq!(fixture.finish(&proof).unwrap_err().kind(), ErrorKind::Io); + fixture.unfinished(); + } + } +} + +#[test] +fn finish_keeps_the_strict_manifest_checks() { + let proof = exited(); + let fixture = Fixture::new(); + for bytes in [ + vec![0x80], + vec![0; super::super::MAX_MANIFEST_LEN as usize + 1], + ] { + write_private(&fixture.artifact(MANIFEST_NAME), &bytes).unwrap(); + assert_eq!(fixture.finish(&proof).unwrap_err().kind(), ErrorKind::Io); + fixture.unfinished(); + } + let mut spec = ManifestSpec::template(); + spec.tier = "workload".to_owned(); + write_private(&fixture.artifact(MANIFEST_NAME), &spec.encode()).unwrap(); + assert_eq!(fixture.finish(&proof).unwrap_err().kind(), ErrorKind::Io); + fixture.unfinished(); +} + +#[test] +fn finish_checks_the_context_against_the_effective_capture() { + let proof = exited(); + let changes: &[fn(&mut CaptureContext)] = &[ + |context| context.hypervisor = Hypervisor::Mshv, + |context| context.cpu_profile = Some("amd.zen4.v1".to_owned()), + |context| context.capture.processors.capacity = 4, + |context| context.capture.processors.boot = 1, + |context| context.capture.memory.boot = 256, + |context| context.capture.memory.capacity = 1024, + |context| { + let DiskCapture::Fresh { image, .. } = &mut context.capture.disks; + image.length *= 2; + }, + |context| { + let DiskCapture::Fresh { scratch, .. } = &mut context.capture.disks; + scratch.physical_block_size *= 2; + }, + |context| { + context.nic = Some(NicConfig { + guest_network: crate::net::Ipv4Cidr::parse("10.0.0.2/24").unwrap(), + }); + }, + |context| context.runtime.control_contract.clear(), + |context| context.capture.memory.capacity = u64::MAX - 127, + ]; + for change in changes { + let mut fixture = Fixture::new(); + change(&mut fixture.context); + assert!(fixture.finish(&proof).is_err()); + fixture.unfinished(); + } +} + +#[test] +fn a_correctable_failure_and_a_partial_metadata_write_can_both_be_retried() { + let proof = exited(); + let fixture = Fixture::new(); + let state = fixture.artifact("state.bin"); + let length = fs::metadata(&state).unwrap().len(); + fs::OpenOptions::new() + .write(true) + .open(&state) + .unwrap() + .set_len(length - 1) + .unwrap(); + assert!(fixture.finish(&proof).is_err()); + fixture.unfinished(); + fs::OpenOptions::new() + .write(true) + .open(&state) + .unwrap() + .set_len(length) + .unwrap(); + let description = fixture.dir.path().join(DESCRIPTION_FILE); + fs::create_dir(&description).unwrap(); + assert!(fixture.finish(&proof).is_err()); + assert!(fixture.dir.path().join(KEY_FILE).is_file()); + assert!(description.is_dir()); + fs::remove_dir(&description).unwrap(); + let captured = fixture.finish(&proof).unwrap(); + assert_eq!(fixture.finish(&proof).unwrap().key, captured.key); + assert!(Snapshot::open(fixture.dir.path()).is_err()); + let published = fixture.dir.path().parent().unwrap().join("published"); + create_private_dir(&published).unwrap(); + for name in [KEY_FILE, DESCRIPTION_FILE, OPENVMM_DIR] { + fs::rename(fixture.dir.path().join(name), published.join(name)).unwrap(); + } + assert_eq!(Snapshot::open(&published).unwrap().key(), &captured.key); + let names = fs::read_dir(fixture.dir.path()) + .unwrap() + .map(|entry| entry.unwrap().file_name()) + .collect::>(); + assert_eq!(names, [std::ffi::OsString::from(OUTCOME)]); +} + +#[cfg(target_os = "linux")] +#[test] +fn finish_refuses_shared_snapshot_entries_links_fifos_and_sockets() { + use std::os::unix::fs::{PermissionsExt, symlink}; + use std::os::unix::net::UnixListener; + + let proof = exited(); + for name in SNAPSHOT_ARTIFACTS { + let fixture = Fixture::new(); + let path = fixture.artifact(name); + fs::set_permissions(&path, fs::Permissions::from_mode(0o640)).unwrap(); + assert!(fixture.finish(&proof).is_err()); + fs::set_permissions(&path, fs::Permissions::from_mode(0o600)).unwrap(); + let saved = fixture.dir.path().join("saved"); + fs::rename(&path, &saved).unwrap(); + symlink(&saved, &path).unwrap(); + assert!(fixture.finish(&proof).is_err()); + fs::remove_file(&path).unwrap(); + nix::unistd::mkfifo(&path, nix::sys::stat::Mode::S_IRUSR).unwrap(); + assert!(fixture.finish(&proof).is_err()); + fs::remove_file(&path).unwrap(); + let _socket = UnixListener::bind(&path).unwrap(); + assert!(fixture.finish(&proof).is_err()); + fixture.unfinished(); + } + let fixture = Fixture::new(); + fs::set_permissions( + fixture.dir.path().join(OPENVMM_DIR), + fs::Permissions::from_mode(0o750), + ) + .unwrap(); + assert!(fixture.finish(&proof).is_err()); + fixture.unfinished(); +} + +#[cfg(windows)] +#[test] +fn finish_refuses_shared_snapshot_acls_and_reparse_artifacts() { + use crate::host::platform::tests::{set_access, user_sid}; + + let proof = exited(); + let shared = format!("D:P(A;;FA;;;{})(A;;FR;;;WD)", user_sid()); + for name in SNAPSHOT_ARTIFACTS { + let fixture = Fixture::new(); + let path = fixture.artifact(name); + set_access(&path, &shared).unwrap(); + assert!(fixture.finish(&proof).is_err()); + fs::remove_file(&path).unwrap(); + crate::host::tests::junction(&path, fixture.dir.path()); + assert!(fixture.finish(&proof).is_err()); + fixture.unfinished(); + fs::remove_dir(&path).unwrap(); + } + let fixture = Fixture::new(); + set_access(&fixture.dir.path().join(OPENVMM_DIR), &shared).unwrap(); + assert!(fixture.finish(&proof).is_err()); + fixture.unfinished(); +} diff --git a/nvx-rs/src/host/snapshot/key.rs b/nvx-rs/src/host/snapshot/key.rs index e0c29175c..c4b5b9b4e 100644 --- a/nvx-rs/src/host/snapshot/key.rs +++ b/nvx-rs/src/host/snapshot/key.rs @@ -6,19 +6,21 @@ use std::net::{Ipv4Addr, Ipv6Addr}; use sha2::{Digest, Sha256}; +use super::CaptureContext; use super::canonical::{CanonicalError, Reader, Writer}; use super::manifest::{ AddressSpace, BlockGeometry, CPUID_ENTRY_LEN, CpuProfile, Device, DeviceRange, - FILESYSTEM_SLOT_VERSION, Interrupts, Ipv6Identity, MAX_COMMAND_LINE_LEN, MAX_CPUID_ENTRIES, - MAX_DEVICE_RANGES, MAX_DEVICE_WORDS, MAX_DEVICES, MAX_MEMORY_RANGES, MAX_PROCESSORS, - MAX_TEXT_LEN, Machine, Memory, MemoryRange, Nic, PROCESSOR_COUNTS, PolicyIdentity, Rates, - SnapshotManifest, Topology, parse_profile_id, + FILESYSTEM_SLOT_VERSION, HostClock, Interrupts, Ipv6Identity, MAX_COMMAND_LINE_LEN, + MAX_CPUID_ENTRIES, MAX_DEVICE_RANGES, MAX_DEVICE_WORDS, MAX_DEVICES, MAX_MEMORY_RANGES, + MAX_PROCESSORS, MAX_TEXT_LEN, Machine, Memory, MemoryRange, Nic, PROCESSOR_COUNTS, + PolicyIdentity, Rates, SnapshotManifest, TimeContract, Topology, parse_profile_id, }; use crate::activation; use crate::contract; use crate::error::{Error, Result}; -use crate::host::config::check_setting; +use crate::host::config::{DiskCapture, check_setting}; use crate::host::platform::parse_hex32; +use crate::host::process::BootId; use crate::host::sandbox::SandboxNetwork; use crate::host::sandbox::mount::MOUNT_TAG; use crate::host::store::{ArtifactIdentity, MAX_IDENTITY_LEN, RuntimeIdentity}; @@ -203,8 +205,9 @@ macro_rules! identity { } identity!( - /// The identity of a [`SnapshotKey`]: the SHA-256 digest of its canonical encoding, after the - /// domain `nvx snapshot key` and a NUL, so that it names a key of one encoding version alone. + /// The compatibility identity of a [`SnapshotKey`]: the SHA-256 digest of its canonical + /// compatibility prefix, after the domain `nvx snapshot key` and a NUL. The persisted + /// capture-time anchors do not contribute to it. /// /// Its text form, which `Display` writes and `FromStr` parses, is 64 lowercase hexadecimal /// digits; parsing also accepts uppercase ones. It converts to and from the @@ -301,9 +304,9 @@ fn guest_network_of(nic: &Nic) -> String { /// /// A capture whose manifest matches its class makes a template whose [`SnapshotKey`] combines /// them: [`SnapshotKey::new`] checks that the machine of the manifest is the class's, and takes -/// the rest of the key from it. Captures of one class make templates of one key, unless something -/// that the class does not fix changed between them, such as the host's CPU, which its effective -/// CPUID and its rates follow. +/// the rest of the key from it. Captures of one class have one compatibility identity unless +/// something that the class does not fix changes, such as the host's CPU, which its effective +/// CPUID and its rates follow. Each key also retains its own capture's time contract. /// /// Its [`identity`](Self::identity) names the class: the SHA-256 digest of its canonical encoding, /// which holds every field, and the versions of the contracts that the key holds. @@ -472,12 +475,12 @@ impl CaptureClass { /// the template replaces with its clone's own; and the template's [`Invariants`], its other /// invariant isolation and resource settings. /// -/// A capture's own metadata is not part of it, so the generations of one key differ in it: when -/// the capture was taken, its time contract's anchor, host, boot, and lineage counter, and the -/// lengths of its artifacts. [`SnapshotManifest`] keeps them. Nothing of a clone is part of it -/// either: its image, upper, export, network policy and settings, identity, and activation -/// targets. So a template with a NIC serves the clones of every network policy at its guest's -/// address. +/// The complete capture [`TimeContract`] is retained and encoded with the key, including its +/// anchor, host, boot and lineage counter, but those per-capture fields are not part of the +/// compatibility identity. The capture's creation time and artifact lengths remain in +/// [`SnapshotManifest`]. Nothing of a clone is part of the key: its image, upper, export, network +/// policy and settings, identity, or activation targets. So a template with a NIC serves the +/// clones of every network policy at its guest's address. /// /// The key comes from metadata alone: the artifacts' identities that their install metadata /// records, the class's inputs, and the capture's manifest. It never reads or digests a binary, @@ -485,7 +488,8 @@ impl CaptureClass { /// makes templates of a new key: the templates of the old one are not recertified. /// /// Its [`identity`](Self::identity) is the SHA-256 digest of its -/// [canonical encoding](Self::to_bytes), after a domain that no other digest of this crate shares. +/// canonical compatibility prefix, after a domain that no other digest of this crate shares. +/// [`to_bytes`](Self::to_bytes) appends the per-capture time fields to that prefix. #[derive(Debug, Clone, PartialEq, Eq)] pub struct SnapshotKey { /// The runtime: OpenVMM, the guest kernel and initramfs, and the boot and control contracts. @@ -498,15 +502,43 @@ pub struct SnapshotKey { filesystem_tag: String, /// The invariant settings beyond the machine. invariants: Invariants, + /// The complete time contract of this particular capture. + time: TimeContract, } pub(in crate::host) use SnapshotKey as TemplateKey; +/// The checked inputs shared by a legacy capture class and a caller-owned capture context. +struct CaptureInputs<'a> { + /// The runtime's artifact and contract identities. + runtime: &'a RuntimeIdentity, + /// The requested backend. + hypervisor: Hypervisor, + /// The requested CPU profile. + cpu_profile: &'a str, + /// The processor capacity. + processors: u64, + /// The processors online at boot. + boot_processors: u64, + /// The RAM at boot, in bytes. + memory: u64, + /// The RAM capacity, in bytes. + memory_capacity: u64, + /// The placeholder image's geometry. + distro: &'a BlockGeometry, + /// The dormant scratch's geometry. + scratch: &'a BlockGeometry, + /// The resolved NIC address, without a network policy. + guest_network: Option, + /// The caller's invariant settings. + invariants: &'a Invariants, +} + impl SnapshotKey { /// Version of the canonical encoding of a key, its first two bytes. /// - /// Version 1 omits the guest build and holds the snapshot's NIC, if it has one, with the - /// identity of the complete + /// Version 1 omits the guest build, appends the complete capture-time contract, and holds + /// the snapshot's NIC, if it has one, with the identity of the complete /// deny-all policy under which the capture attached it, but no network policy of its clones. /// It has not been released, so a template store that an earlier build created is not /// supported: delete it and create a new one. There is no migration. @@ -541,6 +573,65 @@ impl SnapshotKey { /// requested. pub fn new(class: &CaptureClass, manifest: &SnapshotManifest) -> Result { class.check()?; + let attached = !class.network_arguments()?.is_empty(); + Self::from_inputs( + CaptureInputs { + runtime: &class.runtime, + hypervisor: class.hypervisor, + cpu_profile: &class.cpu_profile, + processors: u64::from(class.processors), + boot_processors: u64::from(class.boot_processors), + memory: class.memory, + memory_capacity: class.memory_capacity, + distro: &class.distro, + scratch: &class.scratch, + guest_network: class + .network + .as_ref() + .filter(|_| attached) + .map(|network| network.guest_network().to_owned()), + invariants: &class.invariants, + }, + manifest, + ) + } + + /// Derives the key from the checked creation context and OpenVMM's validated manifest. + pub(super) fn from_capture( + context: &CaptureContext, + manifest: &SnapshotManifest, + ) -> Result { + context.capture.check()?; + context.runtime.check()?; + let cpu_profile = context.cpu_profile.as_deref().unwrap_or("auto"); + if let Some(problem) = cpu_profile_problem(cpu_profile) { + return Err(Error::unsupported(problem)); + } + let bytes = |mib: u64| { + mib.checked_mul(1024 * 1024) + .ok_or_else(|| Error::unsupported("capture memory in MiB overflows bytes")) + }; + let DiskCapture::Fresh { image, scratch } = &context.capture.disks; + Self::from_inputs( + CaptureInputs { + runtime: &context.runtime, + hypervisor: context.hypervisor, + cpu_profile, + processors: context.capture.processors.capacity, + boot_processors: context.capture.processors.boot, + memory: bytes(context.capture.memory.boot)?, + memory_capacity: bytes(context.capture.memory.capacity)?, + distro: image, + scratch, + guest_network: context.nic.map(|nic| nic.guest_network.to_string()), + invariants: &context.capture.invariants, + }, + manifest, + ) + } + + /// Checks the manifest's machine against the capture's inputs and takes its effective shape. + fn from_inputs(class: CaptureInputs<'_>, manifest: &SnapshotManifest) -> Result { let machine = &manifest.machine; let mismatch = |found: String, requested: String| { Err(Error::io(format!( @@ -557,22 +648,27 @@ impl SnapshotKey { let profile = machine.cpu.id(); // `auto` selects the built-in profile of the host's CPU, or falls back to a profile that // the host derives, whose generation is `host`, when no built-in profile serves it. - let selected = cpu_profile_matches(&machine.cpu, &class.cpu_profile); + let selected = cpu_profile_matches(&machine.cpu, class.cpu_profile); if !selected { return mismatch( format!("its CPU profile is {profile}"), - class.cpu_profile.clone(), + class.cpu_profile.to_owned(), ); } let boot_processors = match machine.boot_processors { 0 => machine.processors, count => count, }; - if (machine.processors, boot_processors) != (class.processors, class.boot_processors) { + if (u64::from(machine.processors), u64::from(boot_processors)) + != (class.processors, class.boot_processors) + { let describe = |processors, online| format!("{processors} processors, {online} online at boot"); return mismatch( - format!("it has {}", describe(machine.processors, boot_processors)), + format!( + "it has {}", + describe(u64::from(machine.processors), u64::from(boot_processors)) + ), describe(class.processors, class.boot_processors), ); } @@ -588,8 +684,8 @@ impl SnapshotKey { ); } for (role, found, requested) in [ - ("distro", &machine.distro, &class.distro), - ("scratch", &machine.scratch, &class.scratch), + ("distro", &machine.distro, class.distro), + ("scratch", &machine.scratch, class.scratch), ] { if found != requested { return mismatch( @@ -598,12 +694,7 @@ impl SnapshotKey { ); } } - let attached = !class.network_arguments()?.is_empty(); - let requested_network = class - .network - .as_ref() - .filter(|_| attached) - .map(|network| network.guest_network().to_owned()); + let requested_network = class.guest_network; // A network that `SandboxNetwork` accepts has the canonical form that this writes. let found_network = machine.nic.as_ref().map(guest_network_of); if found_network != requested_network { @@ -619,12 +710,18 @@ impl SnapshotKey { machine: machine.clone(), filesystem_tag: MOUNT_TAG.to_owned(), invariants: class.invariants.clone(), + time: manifest.time().clone(), }) } - /// Returns the identity of the key. + /// Returns the compatibility identity, excluding this capture's time anchors and identities. + /// + /// The rates and time ABI remain compatibility inputs. Different capture times may have + /// different [`to_bytes`](Self::to_bytes) with the same identity. pub fn identity(&self) -> KeyIdentity { - KeyIdentity(digest(KEY_DOMAIN, &self.to_bytes())) + let mut writer = Writer::default(); + self.write_compatibility(&mut writer); + KeyIdentity(digest(KEY_DOMAIN, &writer.finish())) } /// Returns the runtime: the identities of OpenVMM, the guest kernel, and the guest initramfs, @@ -642,6 +739,7 @@ impl SnapshotKey { /// Checks the manifest's complete machine, the current key contracts and the filesystem tag. pub(super) fn check_manifest(&self, manifest: &SnapshotManifest) -> Result<()> { if self.machine != manifest.machine + || &self.time != manifest.time() || !self.speaks_current_contracts() || self.filesystem_tag != MOUNT_TAG { @@ -657,6 +755,14 @@ impl SnapshotKey { self.machine.hypervisor } + /// Returns this snapshot's complete capture-time contract, as recorded by its manifest. + /// + /// Its capture anchors, host, boot and lineage are persisted, but do not change the + /// compatibility [`identity`](Self::identity). + pub fn time(&self) -> &TimeContract { + &self.time + } + /// Returns the ID of the CPU profile, `VENDOR.GENERATION.vREVISION`. pub fn cpu_profile(&self) -> String { self.machine.cpu.id() @@ -737,8 +843,10 @@ impl SnapshotKey { &self.invariants } - /// Returns the canonical encoding of the key, which its [`identity`](Self::identity) digests, - /// and [`from_bytes`](Self::from_bytes) reads back. + /// Returns the canonical persisted encoding, which [`from_bytes`](Self::from_bytes) reads. + /// + /// [`identity`](Self::identity) digests the compatibility prefix, items 1 through 8 below, + /// excluding item 9's per-capture time fields. /// /// Version 1 holds, in order, each value encoded as the [module](super) describes: /// @@ -770,18 +878,28 @@ impl SnapshotKey { /// the gateway's IPv4 address, 4 bytes, the optional IPv6 identity, with the guest's /// address, 16 bytes, its prefix length, a `u8`, and the gateway's address, 16 bytes, and /// the network policy's mode, a text, digest, 32 bytes, whether it is required, a boolean, - /// and encoding version, a `u32`; and the invariant settings, a list of names and values, - /// each a text, in the order of their names. + /// and encoding version, a `u32`. + /// 8. The invariant settings, a list of names and values, each a text, in name order. + /// 9. The capture-specific time contract: the capture TSC, UTC and monotonic anchors, + /// `u64`s; the host clock, a text; the host and boot identities, 16 bytes each; the lineage, + /// a `u32`; and the optional cut-to-anchor interval, a `u64`. The complete contract's rates + /// are already encoded in item 3. pub fn to_bytes(&self) -> Vec { let mut writer = Writer::default(); - writer.u16(Self::VERSION); - write_runtime(&mut writer, &self.runtime); - self.contracts.write(&mut writer); - write_machine(&mut writer, &self.machine, &self.filesystem_tag); - self.invariants.write(&mut writer); + self.write_compatibility(&mut writer); + write_time(&mut writer, &self.time); writer.finish() } + /// Writes exactly the compatibility prefix that the key's identity digests. + fn write_compatibility(&self, writer: &mut Writer) { + writer.u16(Self::VERSION); + write_runtime(writer, &self.runtime); + self.contracts.write(writer); + write_machine(writer, &self.machine, &self.filesystem_tag); + self.invariants.write(writer); + } + /// Reads a key from its canonical encoding, as [`to_bytes`](Self::to_bytes) writes it. /// /// The bytes must be the key's one encoding, of version [`VERSION`](Self::VERSION), with no @@ -817,12 +935,14 @@ impl SnapshotKey { let contracts = Contracts::read(reader)?; let (machine, filesystem_tag) = read_machine(reader)?; let invariants = Invariants::read(reader)?; + let time = read_time(reader, machine.rates)?; Ok(Self { runtime, contracts, machine, filesystem_tag, invariants, + time, }) }; let key = read(&mut reader).map_err(malformed)?; @@ -831,6 +951,46 @@ impl SnapshotKey { } } +/// Appends the capture-specific fields; the complete contract's rates are in the machine prefix. +fn write_time(writer: &mut Writer, time: &TimeContract) { + writer.u64(time.capture_tsc); + writer.u64(time.capture_utc_ns); + writer.u64(time.capture_monotonic_ns); + writer.text(time.host_clock.as_str()); + writer.fixed(&time.host_id); + writer.fixed(time.boot_id.as_bytes()); + writer.u32(time.lineage); + writer.option(time.cut_to_anchor_ns.as_ref(), |writer, nanos| { + writer.u64(*nanos) + }); +} + +/// Reads the full capture contract using the compatibility prefix's already-checked rates. +fn read_time(reader: &mut Reader<'_>, rates: Rates) -> Result { + let capture_tsc = reader.u64("the capture TSC")?; + let capture_utc_ns = reader.u64("the capture UTC anchor")?; + let capture_monotonic_ns = reader.u64("the capture monotonic anchor")?; + let clock = reader.text(MAX_TEXT_LEN, "the capture host clock")?; + let host_clock = HostClock::parse(clock).ok_or_else(|| { + CanonicalError::new(format!("the capture host clock {clock:?} is unknown")) + })?; + Ok(TimeContract { + tsc_frequency_hz: rates.tsc_hz, + tsc_tolerance_ppm: rates.tolerance_ppm, + apic_frequency_hz: rates.apic_hz, + capture_tsc, + capture_utc_ns, + capture_monotonic_ns, + host_clock, + host_id: reader.fixed("the capturing host identity")?, + boot_id: BootId::from_bytes(reader.fixed("the capture boot identity")?), + lineage: reader.u32("the capture lineage")?, + cut_to_anchor_ns: reader.option("the cut-to-anchor interval", |reader| { + reader.u64("the cut-to-anchor interval") + })?, + }) +} + /// Writes the machine `machine`, whose filesystem slot has the tag `tag`, as /// [`SnapshotKey::to_bytes`] describes it. fn write_machine(writer: &mut Writer, machine: &Machine, tag: &str) { diff --git a/nvx-rs/src/host/snapshot/key/tests.rs b/nvx-rs/src/host/snapshot/key/tests.rs index 0ee47de27..99c639dcf 100644 --- a/nvx-rs/src/host/snapshot/key/tests.rs +++ b/nvx-rs/src/host/snapshot/key/tests.rs @@ -13,6 +13,21 @@ fn manifest(spec: &ManifestSpec) -> SnapshotManifest { SnapshotManifest::parse(&spec.encode()).unwrap() } +/// The specified suffix length, independently of the key writer, for locating the identity prefix. +fn capture_time_len(time: &TimeContract) -> usize { + 3 * 8 + + 4 + + time.host_clock.as_str().len() + + 2 * 16 + + 4 + + 1 + + if time.cut_to_anchor_ns.is_some() { + 8 + } else { + 0 + } +} + /// Returns the key of the template that a capture of `class` made, whose manifest `spec` /// encodes. fn key(class: &CaptureClass, spec: &ManifestSpec) -> SnapshotKey { @@ -349,6 +364,50 @@ fn the_guest_build_is_not_part_of_a_snapshot_key() { ); } +#[test] +fn complete_capture_time_round_trips_without_changing_compatibility_identity() { + let base = network_key(); + for cut in [None, Some(0), Some(u64::MAX)] { + let mut spec = ManifestSpec::with_network(); + let time = spec.time(); + time.capture_tsc = u64::MAX; + time.capture_utc_ns = u64::MAX - 1; + time.capture_monotonic_ns = u64::MAX - 2; + time.host_clock = "windows-interrupt-time".to_owned(); + time.host_id = vec![0x31; 16]; + time.boot_id = vec![0x72; 16]; + time.lineage = u32::MAX; + time.cut_to_anchor_ns = cut; + let manifest = manifest(&spec); + let key = SnapshotKey::new(&network_class(), &manifest).unwrap(); + assert_eq!(key.time(), manifest.time()); + assert_ne!(key.to_bytes(), base.to_bytes()); + assert_eq!(key.identity(), base.identity()); + let decoded = SnapshotKey::from_bytes(&key.to_bytes()).unwrap(); + assert_eq!(decoded, key); + assert_eq!(decoded.time(), manifest.time()); + } +} + +#[test] +fn canonical_keys_require_capture_time_and_a_known_host_clock() { + let key = network_key(); + let mut prefix = Writer::default(); + key.write_compatibility(&mut prefix); + let prefix = prefix.finish(); + assert_eq!( + SnapshotKey::from_bytes(&prefix).unwrap_err().kind(), + crate::ErrorKind::Io + ); + let mut bytes = key.to_bytes(); + // Three anchors and the clock text's length precede its bytes. + bytes[prefix.len() + 3 * 8 + 4] = b'?'; + let error = SnapshotKey::from_bytes(&bytes).unwrap_err(); + assert_eq!(error.kind(), crate::ErrorKind::Io); + assert!(error.message().contains("host clock"), "{error}"); + assert!(error.message().contains("unknown"), "{error}"); +} + #[test] fn a_key_holds_what_its_capture_made() { let made = network_key(); @@ -670,7 +729,7 @@ fn keys_read_back_from_their_canonical_encoding() { assert_eq!(read.to_bytes(), bytes); let mut digest = Sha256::new(); digest.update(b"nvx snapshot key\0"); - digest.update(&bytes); + digest.update(&bytes[..bytes.len() - capture_time_len(made.time())]); assert_eq!( made.identity().as_bytes(), &<[u8; 32]>::from(digest.finalize()) @@ -849,12 +908,13 @@ fn keys_of_machines_that_no_manifest_describes_are_refused() { .unwrap() .with("b", "2") .unwrap(); - let mut bytes = key(&class, &ManifestSpec::with_network()).to_bytes(); - let len = bytes.len(); + let made = key(&class, &ManifestSpec::with_network()); + let mut bytes = made.to_bytes(); + let end = bytes.len() - capture_time_len(made.time()); let tail = [ 1, 0, 0, 0, b'b', 1, 0, 0, 0, b'2', 1, 0, 0, 0, b'a', 1, 0, 0, 0, b'1', ]; - bytes[len - tail.len()..].copy_from_slice(&tail); + bytes[end - tail.len()..end].copy_from_slice(&tail); assert_eq!( key_refusal(&bytes, ErrorCode::Io), "the snapshot key is malformed: the invariant settings are not in the strict order of \ diff --git a/nvx-rs/src/host/snapshot/manifest.rs b/nvx-rs/src/host/snapshot/manifest.rs index d6f45bafc..0249212ff 100644 --- a/nvx-rs/src/host/snapshot/manifest.rs +++ b/nvx-rs/src/host/snapshot/manifest.rs @@ -146,9 +146,9 @@ impl HostClock { } } -/// The time contract of a capture: the rates that it declares, which its template's key holds, -/// and the anchor at which it was captured, with the identities of the host and the boot that -/// captured it, which the generations of one key differ in. +/// The complete time contract of a capture, retained by its manifest and snapshot key. Its +/// rates contribute to the key's compatibility identity; its capture anchor, host, boot and +/// lineage do not, so captures with different anchors can share that identity. /// /// A restore uses the anchor to measure how long the capture has been down: on the same host /// boot, by the host's monotonic clock, and otherwise by UTC. diff --git a/nvx-rs/src/host/snapshot/tests.rs b/nvx-rs/src/host/snapshot/tests.rs index 7fb45bb31..23b8352f3 100644 --- a/nvx-rs/src/host/snapshot/tests.rs +++ b/nvx-rs/src/host/snapshot/tests.rs @@ -1215,6 +1215,33 @@ fn snapshot_description_contents_do_not_affect_opening() { } } +#[test] +fn snapshot_open_refuses_a_compatible_key_with_another_capture_time() { + use crate::host::store::files::write_private; + + let mutations: &[fn(&mut ManifestSpec)] = &[ + |spec| spec.time().capture_tsc += 1, + |spec| spec.time().capture_utc_ns += 1, + |spec| spec.time().capture_monotonic_ns += 1, + |spec| spec.time().host_clock = "windows-interrupt-time".to_owned(), + |spec| spec.time().host_id[0] ^= 1, + |spec| spec.time().boot_id[0] ^= 1, + |spec| spec.time().lineage += 1, + |spec| spec.time().cut_to_anchor_ns = None, + |spec| spec.time().cut_to_anchor_ns = Some(0), + ]; + for mutate in mutations { + let fixture = SnapshotFixture::new(); + let mut spec = fixture.spec.clone(); + mutate(&mut spec); + let manifest = SnapshotManifest::parse(&spec.encode()).unwrap(); + let key = SnapshotKey::new(&class(), &manifest).unwrap(); + assert_eq!(key.identity(), fixture.key.identity()); + write_private(&fixture.dir.join(KEY_FILE), &key.to_bytes()).unwrap(); + refused(&fixture.dir); + } +} + #[cfg(target_os = "linux")] #[test] fn snapshot_open_refuses_every_shared_file_and_both_shared_directories() { diff --git a/nvx-rs/src/host/template/store/capture.rs b/nvx-rs/src/host/template/store/capture.rs index 74a519049..660312d3e 100644 --- a/nvx-rs/src/host/template/store/capture.rs +++ b/nvx-rs/src/host/template/store/capture.rs @@ -26,8 +26,9 @@ use crate::host::instance::generate_capability; use crate::host::platform; use crate::host::sandbox::distro::{CheckedDistro, DistroBinding, DistroFormat}; use crate::host::sandbox::{LayerRole, SandboxLayer, SandboxPlan}; +use crate::host::snapshot::{check_exit, check_snapshot}; use crate::host::store::files::{create_new, remove_if_present, sync_dir, write_private}; -use crate::host::store::regular::{identity_of, open_regular, read_at_most}; +use crate::host::store::regular::{identity_of, open_regular}; use crate::host::store::{ JOURNAL_NAME, LOCK_NAME, LOG_NAME, LaunchId, LockedStore, OUTCOME_NAME, Recovery, SOCKET_NAME, SandboxStore, @@ -35,7 +36,7 @@ use crate::host::store::{ use crate::host::supervisor::{self, VmmCommand}; use crate::host::template::eligibility::HostTime; use crate::host::template::key::{CaptureClass, ClassIdentity, KeyIdentity, TemplateKey}; -use crate::host::template::manifest::{MANIFEST_NAME, TemplateManifest, TimeContract}; +use crate::host::template::manifest::{TemplateManifest, TimeContract}; use crate::host::template::plan::{SCRATCH_NAME, TemplatePlan, TemplateResources}; /// Name of the directory of a store's captures, in which each capture class has a slot. @@ -46,15 +47,6 @@ pub const TRANSACTION_NAME: &str = "transaction"; /// Name of the record of a capture, in its transaction and then in its generation's payload. pub const CAPTURE_RECORD_NAME: &str = "capture.json"; -/// Name of the saved state of a snapshot. -const STATE_NAME: &str = "state.bin"; -/// Name of the RAM of a snapshot. -const MEMORY_NAME: &str = "memory.bin"; -/// The artifacts of a platform-tier snapshot, in the order of their names: its manifest, its RAM, -/// and its saved state. -pub(in crate::host::template) const SNAPSHOT_ARTIFACTS: [&str; 3] = - [MANIFEST_NAME, MEMORY_NAME, STATE_NAME]; - /// Prefix of the name of the file that backs the RAM of a capture's VM, which OpenVMM creates /// beside the snapshot's destination, and removes as the VM ends. const MEMORY_FILE_PREFIX: &str = ".openvmm-microvm-memory-"; @@ -73,9 +65,6 @@ pub(in crate::host::template) const SOCKET_PATH_LIMIT: usize = 108; /// Longest record of a capture, in bytes. const MAX_CAPTURE_RECORD_LEN: u64 = 4 * 1024; -/// Longest outcome report of a capture's VM that the capture reads, in bytes. -const MAX_REPORT_LEN: u64 = 1024 * 1024; - /// Interval at which a rename that Windows refuses, while another process holds a file below the /// renamed directory open, is tried again, until the capture's deadline. const RENAME_RETRY_INTERVAL: Duration = Duration::from_millis(25); @@ -1114,128 +1103,6 @@ fn check_scratch(template: &TemplatePlan, created: (u64, u128)) -> Result<()> { Ok(()) } -/// The outcome report of a VM, as OpenVMM writes it, of which a capture reads the outcome. -#[derive(Deserialize)] -struct OutcomeReport { - /// The outcome of the VM. - outcome: Outcome, -} - -/// The outcome of a VM, as its outcome report records it. -#[derive(Deserialize)] -struct Outcome { - /// The operation of the VM: `capture` for a VM that OpenVMM captures. - operation: String, - /// The category of the outcome: `success` for a capture that OpenVMM published. - category: String, - /// The status code of the outcome: 0 for a capture that OpenVMM published. - status_code: i64, -} - -/// Checks the source's exit through the outcome report of the capture's VM at `path`: that -/// OpenVMM wrote one, for a capture that succeeded, as it does once it has published the snapshot -/// and torn the VM down, and before it exits. The launch removed the report of the slot's previous -/// VM, as [`LockedStore::begin_launch`] does, and OpenVMM publishes its report without replacing -/// a file, so the report is the VM's own. -fn check_exit(path: &Path) -> Result<()> { - let description = format!("the outcome report {}", path.display()); - let file = match open_regular(path) { - Ok(file) => file, - Err(error) if error.kind() == io::ErrorKind::NotFound => { - return Err(Error::exited(format!( - "the VM of the capture ended without {description}" - ))); - } - Err(error) => { - return Err(Error::io(format!("cannot read {description}")).with_source(error)); - } - }; - let bytes = read_at_most(file, MAX_REPORT_LEN, "an outcome report") - .map_err(|error| Error::io(format!("cannot read {description}")).with_source(error))?; - let report: OutcomeReport = serde_json::from_slice(&bytes) - .map_err(|error| Error::io(format!("{description} is malformed")).with_source(error))?; - let Outcome { - operation, - category, - status_code, - } = report.outcome; - if operation != "capture" || category != "success" || status_code != 0 { - return Err(Error::exited(format!( - "the VM of the capture ended without a capture: {description} records the outcome \ - {category} with status {status_code} of the operation {operation}" - ))); - } - Ok(()) -} - -/// Checks the snapshot at `snapshot` that OpenVMM published: a directory private to the current -/// user, as [`platform::check_snapshot_dir`] checks it, whose manifest -/// [`TemplateManifest::read`] reads, and whose artifacts are exactly its manifest, RAM, and saved -/// state, each a regular file private to the current user, of the length that the manifest -/// records. Reads no artifact but the manifest. Returns the manifest. -fn check_snapshot(snapshot: &Path) -> Result { - let what = "the snapshot of the capture"; - // A snapshot that is missing once its capture reported it is lost, not merely absent. - let problem = platform::check_snapshot_dir(snapshot).map_err(|error| { - Error::io(format!("cannot open {what} {}", snapshot.display())).with_source(error) - })?; - refuse_problem(snapshot, what, problem)?; - // So is a manifest that is missing from the snapshot. - let manifest = TemplateManifest::read(snapshot).map_err(|error| { - if error.kind() != ErrorKind::NotFound { - return error; - } - let (_, message, source) = error.into_parts(); - let error = Error::io(message); - match source { - Some(source) => error.with_source(source), - None => error, - } - })?; - let mut names = entries(snapshot)?; - names.sort(); - if names != SNAPSHOT_ARTIFACTS { - return Err(Error::io(format!( - "{what} {} holds {}, not exactly the manifest, RAM, and saved state of a platform-tier \ - snapshot", - snapshot.display(), - names.join(", ") - ))); - } - for (name, length) in [ - (MANIFEST_NAME, None), - (MEMORY_NAME, Some(manifest.memory_len())), - (STATE_NAME, Some(manifest.state_len())), - ] { - let path = snapshot.join(name); - let refused = |problem: String| { - Error::io(format!( - "the artifact {} of {what} {problem}", - path.display() - )) - }; - let file = - open_regular(&path).map_err(|error| refused(format!("cannot be used: {error}")))?; - if let Some(problem) = platform::private_file_problem(&file) - .map_err(|error| refused(format!("cannot be checked: {error}")))? - { - return Err(refused(problem)); - } - let found = file - .metadata() - .map_err(|error| refused(format!("cannot be examined: {error}")))? - .len(); - if let Some(length) = length - && found != length - { - return Err(refused(format!( - "is {found} bytes long, not the {length} bytes that its manifest records" - ))); - } - } - Ok(manifest) -} - /// Removes from the capture's transaction `transaction` what the template does not keep: the lock /// file of its sandbox store, which the capture closed once its VM had ended, the dormant scratch /// device's file, the file that backed the VM's RAM, which OpenVMM removes as the VM ends unless diff --git a/nvx-rs/src/host/template/store/capture/tests.rs b/nvx-rs/src/host/template/store/capture/tests.rs index b05b912d1..42b20b1d9 100644 --- a/nvx-rs/src/host/template/store/capture/tests.rs +++ b/nvx-rs/src/host/template/store/capture/tests.rs @@ -15,6 +15,7 @@ use super::*; use crate::host::sandbox::distro::{DISTRO_POLICY_REVISION, PolicyVerdict}; use crate::host::sandbox::{SandboxDistro, SandboxUpper}; use crate::host::snapshot::tests::{MIB, ManifestSpec, class, network_class}; +use crate::host::snapshot::{MANIFEST_NAME, SNAPSHOT_ARTIFACTS}; use crate::host::store::{CallerFiles, JOURNAL_NAME, LaunchId}; use crate::host::template::eligibility::MAX_DOWNTIME_NS; use crate::host::template::store::Lease; @@ -30,6 +31,11 @@ const GIB: u64 = 1024 * MIB; /// Nanoseconds in a second. const SECOND: u64 = 1_000_000_000; +/// Name of the saved state in the emulated snapshot. +const STATE_NAME: &str = "state.bin"; +/// Name of the RAM in the emulated snapshot. +const MEMORY_NAME: &str = "memory.bin"; + /// The identity of the placeholder. const IDENTITY: &str = "11111111-1111-1111-1111-111111111111"; diff --git a/nvx-rs/src/host/template/store/tests.rs b/nvx-rs/src/host/template/store/tests.rs index 59d2cb740..f38b612af 100644 --- a/nvx-rs/src/host/template/store/tests.rs +++ b/nvx-rs/src/host/template/store/tests.rs @@ -13,7 +13,7 @@ use crate::host::template::key::CaptureClass; use crate::host::template::manifest::TemplateManifest; #[cfg(any(target_os = "linux", windows))] -pub(in crate::host::template) use super::capture::SNAPSHOT_ARTIFACTS; +pub(in crate::host::template) use crate::host::snapshot::SNAPSHOT_ARTIFACTS; impl Store { /// Publishes a generation of a template, whose fixed time contract names a host and boot of diff --git a/nvx-rs/src/host/vm.rs b/nvx-rs/src/host/vm.rs index 758baac91..847c5795e 100644 --- a/nvx-rs/src/host/vm.rs +++ b/nvx-rs/src/host/vm.rs @@ -28,17 +28,15 @@ use crate::host::config::{ }; use crate::host::filesystem; use crate::host::network; -use crate::host::paths::{absolute, normalized}; +use crate::host::paths::absolute; use crate::host::platform; -#[cfg(not(windows))] -use crate::host::platform::{check_store_dir as check_dir, create_store_dir as create_dir}; -#[cfg(windows)] -use crate::host::platform::{check_vm_dir as check_dir, create_vm_dir as create_dir}; use crate::host::report::{self, Report}; use crate::host::runtime::{Runtime, RuntimeIdentity}; use crate::host::snapshot::{CaptureContext, Snapshot}; use crate::host::spawn; use crate::host::transport::{self, HostConnection}; +use crate::host::vm_directory::{CONSOLE, CONTROL, io_error}; +pub use crate::host::vm_directory::{VmDir, VmOutputs, create_private_dir}; use crate::host::{ platform::inherit::Inherited, process::{Opened, Process, ProcessRef}, @@ -46,16 +44,6 @@ use crate::host::{ use crate::protocol::CAPABILITY_LEN; use crate::{Error, Result}; -/// OpenVMM's log of a launch. -const LOG: &str = "openvmm.log"; -/// OpenVMM's outcome report of a launch. -const OUTCOME: &str = "outcome.json"; -/// The control socket on Linux. -const CONTROL: &str = "control.sock"; -/// The guest's boot-console socket on Linux. -const CONSOLE: &str = "console.sock"; -/// The restore-ready socket on Linux. -const RESTORE_READY: &str = "restore-ready.sock"; /// OpenVMM's snapshot files: a capture's destination and a restore's snapshot view. const OPENVMM: &str = "openvmm"; /// A finished capture's key. @@ -74,109 +62,6 @@ const DESCRIPTION: &str = "snapshot.json"; const CAPTURE_IMAGE: &str = "capture-image.raw"; /// The dormant scratch that a capture VM attaches. const CAPTURE_SCRATCH: &str = "capture-scratch.raw"; -/// The prefix OpenVMM requires of its Windows console pipes. -const OPENVMM_PIPE: &str = "openvmm-microvm"; -/// The prefix of a Windows restore-ready pipe. -const RESTORE_READY_PIPE: &str = "nvx-restore-ready"; - -/// The directory of a VM, which its caller creates and owns, private to the current user. -/// -/// The library reserves the fixed names of its launch and snapshot files and endpoints, and -/// every name that starts with `.openvmm`; the caller may add files under other names. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct VmDir { - /// The directory's absolute path. - path: PathBuf, -} - -impl VmDir { - /// Opens an existing directory private to the current user, as [`create_private_dir`] makes - /// it. A relative path names a directory from the current directory. Changes nothing. - /// - /// Fails with [`ErrorKind::Io`](crate::ErrorKind::Io) if the directory is not private or - /// cannot be read, as when it does not exist. - pub fn open(path: &Path) -> Result { - // A trailing separator or `/.` would make the OS follow a final symbolic link before - // applying the platform's no-follow check. Keep `..` components unresolved. - let path = absolute(&normalized(path)).map_err(|error| io_error("open", path, error))?; - let problem = check_dir(&path).map_err(|error| io_error("open", &path, error))?; - check_privacy(&path, problem)?; - Ok(Self { path }) - } - - /// Returns the directory as an absolute path. - pub fn path(&self) -> &Path { - &self.path - } - - /// Returns a launch's outputs, with a boot-console endpoint if requested. Creates nothing. - pub(crate) fn outputs(&self, console: bool) -> Result { - Ok(VmOutputs { - log: self.path.join(LOG), - outcome: self.path.join(OUTCOME), - endpoint: self.endpoint(CONTROL, OPENVMM_PIPE)?, - console: console - .then(|| self.endpoint(CONSOLE, OPENVMM_PIPE)) - .transpose()?, - }) - } - - /// Returns a restore-ready endpoint without listening on it. - pub(crate) fn restore_ready_endpoint(&self) -> Result { - self.endpoint(RESTORE_READY, RESTORE_READY_PIPE) - } - - /// Returns the Linux socket `name` or a fresh Windows pipe with the prefix `pipe`. - fn endpoint(&self, name: &str, pipe: &str) -> Result { - if cfg!(windows) { - let mut bytes = [0u8; 16]; - getrandom::fill(&mut bytes) - .map_err(|error| Error::io("cannot draw the name of a pipe").with_source(error))?; - let suffix: String = bytes.iter().map(|byte| format!("{byte:02x}")).collect(); - return Ok(format!("//./pipe/{pipe}-{suffix}")); - } - self.path - .join(name) - .into_os_string() - .into_string() - .map_err(|_| { - Error::unsupported(format!( - "the VM directory {} is not valid Unicode, which OpenVMM's endpoints must be", - self.path.display() - )) - }) - } -} - -/// Creates a VM directory private to the current user, or checks an existing one without -/// changing it. The parent must exist. -/// -/// On Linux, a new directory has mode 0700, whatever the umask and its parent's setgid bit. On -/// Windows, it belongs to the user, and its protected access control list allows the user alone -/// full access, which the files and directories created in it inherit. -/// -/// Fails with [`ErrorKind::Io`](crate::ErrorKind::Io) if an existing directory is not private or -/// the directory cannot be created or read. -pub fn create_private_dir(path: &Path) -> Result<()> { - let path = normalized(path); - let problem = create_dir(&path).map_err(|error| io_error("create", &path, error))?; - check_privacy(&path, problem) -} - -/// A launch's output files and endpoints. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct VmOutputs { - /// OpenVMM's log, `openvmm.log` in the VM directory. - pub log: PathBuf, - /// OpenVMM's outcome report, `outcome.json` in the VM directory. - pub outcome: PathBuf, - /// The control endpoint: `control.sock` in the VM directory on Linux, or on Windows a fresh - /// named pipe, `//./pipe/openvmm-microvm-` and 32 random lowercase hexadecimal digits. - pub endpoint: String, - /// The boot-console endpoint, if requested: `console.sock` in the VM directory on Linux, or - /// on Windows another fresh `//./pipe/openvmm-microvm-` pipe. - pub console: Option, -} /// The credential that the host presents at a VM's control endpoint. /// @@ -707,9 +592,9 @@ pub fn create( .map(NetworkConfig::cold_nic) .transpose()?; let capture_context = if let Some(capture) = capture { - let image_path = dir.path.join(CAPTURE_IMAGE); - let scratch_path = dir.path.join(CAPTURE_SCRATCH); - let destination = dir.path.join(OPENVMM); + let image_path = dir.path().join(CAPTURE_IMAGE); + let scratch_path = dir.path().join(CAPTURE_SCRATCH); + let destination = dir.path().join(OPENVMM); for path in [&image_path, &scratch_path, &destination] { unused_output(path)?; } @@ -999,25 +884,5 @@ fn unused_output(path: &Path) -> Result<()> { Err(Error::io(format!("cannot reserve VM output {}", path.display())).with_source(error)) } -/// Retains the host's I/O failure, or classifies an unsupported host as unavailable. -fn io_error(action: &str, path: &Path, error: io::Error) -> Error { - let message = format!("cannot {action} the VM directory {}", path.display()); - if error.kind() == io::ErrorKind::Unsupported { - Error::unavailable(message).with_source(error) - } else { - Error::io(message).with_source(error) - } -} - -/// Reports why an existing VM directory is not private without changing its permissions. -fn check_privacy(path: &Path, problem: Option) -> Result<()> { - problem.map_or(Ok(()), |problem| { - Err(Error::io(format!( - "the VM directory {} {problem}", - path.display() - ))) - }) -} - #[cfg(test)] mod tests; diff --git a/nvx-rs/src/host/vm/tests.rs b/nvx-rs/src/host/vm/tests.rs index f50abcc27..edf102390 100644 --- a/nvx-rs/src/host/vm/tests.rs +++ b/nvx-rs/src/host/vm/tests.rs @@ -19,6 +19,7 @@ use crate::host::network::{ #[cfg(any(target_os = "linux", windows))] use crate::host::process::Opened; use crate::host::runtime::{ArtifactIdentity, LaunchOptions, RuntimeIdentity}; +use crate::host::vm_directory::{LOG, OUTCOME, RESTORE_READY, tests::vm_dir}; use crate::hypervisor::Hypervisor; use crate::net::Ipv4Cidr; use crate::protocol::root::RootImage; @@ -27,345 +28,6 @@ use crate::uuid::Uuid; #[cfg(any(target_os = "linux", windows))] mod reattach; -fn is_fresh_pipe(endpoint: &str, prefix: &str) -> bool { - endpoint - .strip_prefix(&format!("//./pipe/{prefix}-")) - .is_some_and(|digits| { - digits.len() == 32 - && digits - .bytes() - .all(|digit| matches!(digit, b'0'..=b'9' | b'a'..=b'f')) - }) -} - -fn vm_dir() -> (tempfile::TempDir, VmDir) { - let directory = tempfile::tempdir().unwrap(); - let path = directory.path().join("vm"); - create_private_dir(&path).unwrap(); - let vm = VmDir::open(&path).unwrap(); - (directory, vm) -} - -#[cfg(target_os = "linux")] -#[test] -fn a_vm_directory_is_created_with_mode_700_for_the_current_user_whatever_the_umask() { - use std::os::unix::fs::{MetadataExt, PermissionsExt}; - use std::thread; - - let directory = tempfile::tempdir().unwrap(); - let mode = |path: &Path| fs::metadata(path).unwrap().permissions().mode() & 0o7777; - let parent = directory.path().join("setgid"); - fs::create_dir(&parent).unwrap(); - fs::set_permissions(&parent, fs::Permissions::from_mode(0o2700)).unwrap(); - let path = parent.join("vm"); - thread::scope(|scope| { - scope - .spawn(|| { - nix::sched::unshare(nix::sched::CloneFlags::CLONE_FS).unwrap(); - nix::sys::stat::umask(nix::sys::stat::Mode::from_bits_truncate(0o277)); - create_private_dir(&path) - }) - .join() - .unwrap() - }) - .unwrap(); - assert_eq!(mode(&path), 0o700); - assert_eq!( - fs::metadata(&path).unwrap().uid(), - nix::unistd::Uid::effective().as_raw() - ); - assert_eq!(VmDir::open(&path).unwrap().path(), path); - create_private_dir(&path).unwrap(); - assert_eq!(mode(&path), 0o700); -} - -#[cfg(target_os = "linux")] -#[test] -fn an_existing_directory_that_is_not_private_is_refused_and_left_unchanged() { - use std::os::unix::fs::{PermissionsExt, symlink}; - - use nix::sys::stat::Mode; - use nix::unistd::Uid; - - use crate::host::platform; - - let directory = tempfile::tempdir().unwrap(); - let mode = |path: &Path| fs::symlink_metadata(path).unwrap().permissions().mode() & 0o7777; - let set_mode = |path: &Path, mode: u32| { - fs::set_permissions(path, fs::Permissions::from_mode(mode)).unwrap(); - }; - let refused = |path: &Path, problem: &str| { - for error in [ - create_private_dir(path).unwrap_err(), - VmDir::open(path).unwrap_err(), - ] { - assert_eq!(error.kind(), ErrorKind::Io, "{error}"); - assert_eq!( - error.to_string(), - format!("the VM directory {} {problem}", path.display()) - ); - } - }; - let path = directory.path().join("vm"); - fs::create_dir(&path).unwrap(); - for shared in [0o750, 0o705, 0o770] { - set_mode(&path, shared); - refused( - &path, - &format!("is accessible to other users (mode {shared:03o})"), - ); - assert_eq!(mode(&path), shared); - } - for other in [0o2700, 0o1700, 0o500] { - set_mode(&path, other); - refused( - &path, - &format!( - "has mode {other:03o}, not the mode 700 that the directory of a control socket \ - must have" - ), - ); - assert_eq!(mode(&path), other); - } - assert_eq!(platform::check_owner_only_dir(&path).unwrap(), None); - assert_eq!( - platform::tests::store_dir_problem(Uid::from_raw(1001), Mode::S_IRWXU, Uid::from_raw(1000)), - Some("belongs to user 1001, not to the current user 1000".to_owned()) - ); - set_mode(&path, 0o700); - let link = directory.path().join("link"); - symlink(&path, &link).unwrap(); - refused(&link, "is not a plain directory"); - let file = directory.path().join("file"); - fs::write(&file, b"").unwrap(); - refused(&file, "is not a plain directory"); -} - -#[cfg(target_os = "linux")] -#[test] -fn trailing_separators_and_current_directory_suffixes_do_not_hide_a_symbolic_link() { - use std::os::unix::fs::{PermissionsExt, symlink}; - - let (directory, vm) = vm_dir(); - let link = directory.path().join("link"); - symlink(vm.path(), &link).unwrap(); - for suffix in ["/", "/.", "/./", "//.//"] { - let suffixed = |path: &Path| { - let mut name = path.as_os_str().to_os_string(); - name.push(suffix); - PathBuf::from(name) - }; - let path = suffixed(&link); - for error in [ - create_private_dir(&path).unwrap_err(), - VmDir::open(&path).unwrap_err(), - ] { - assert_eq!(error.kind(), ErrorKind::Io, "{error}"); - assert_eq!( - error.to_string(), - format!( - "the VM directory {} is not a plain directory", - link.display() - ) - ); - } - let private = suffixed(vm.path()); - create_private_dir(&private).unwrap(); - assert_eq!(VmDir::open(&private).unwrap().path(), vm.path()); - } - assert_eq!( - fs::metadata(vm.path()).unwrap().permissions().mode() & 0o7777, - 0o700 - ); - assert!(fs::symlink_metadata(link).unwrap().file_type().is_symlink()); - assert_eq!(fs::read_dir(vm.path()).unwrap().count(), 0); -} - -#[cfg(windows)] -#[test] -fn a_vm_directory_is_created_owner_only_and_protected_under_a_permissive_parent() { - use crate::host::platform; - - let directory = tempfile::tempdir().unwrap(); - platform::tests::set_access(directory.path(), "D:(A;OICI;FA;;;WD)").unwrap(); - let path = directory.path().join("vm"); - create_private_dir(&path).unwrap(); - let sid = platform::tests::user_sid(); - let private = format!("O:{sid}D:P(A;OICI;FA;;;{sid})"); - assert_eq!(platform::tests::access(&path).unwrap(), private); - assert_eq!(VmDir::open(&path).unwrap().path(), path); - create_private_dir(&path).unwrap(); - assert_eq!(platform::tests::access(&path).unwrap(), private); -} - -#[cfg(windows)] -#[test] -fn existing_vm_directories_that_grant_others_or_inherit_are_refused_and_left_unchanged() { - use crate::host::platform; - - let directory = tempfile::tempdir().unwrap(); - let sid = platform::tests::user_sid(); - let refused = |path: &Path, problem: &str| { - let access = platform::tests::access(path).unwrap(); - for error in [ - create_private_dir(path).unwrap_err(), - VmDir::open(path).unwrap_err(), - ] { - assert_eq!(error.kind(), ErrorKind::Io, "{error}"); - assert_eq!( - error.to_string(), - format!("the VM directory {} {problem}", path.display()) - ); - } - assert_eq!(platform::tests::access(path).unwrap(), access); - }; - let path = directory.path().join("vm"); - create_private_dir(&path).unwrap(); - let full = format!("(A;OICI;FA;;;{sid})"); - for other in ["SY", "BA", "OW"] { - platform::tests::set_access(&path, &format!("D:P{full}(A;OICI;FA;;;{other})")).unwrap(); - assert_eq!(platform::check_store_dir(&path).unwrap(), None, "{other}"); - refused(&path, "allows others than the current user"); - } - platform::tests::set_access(&path, "D:P(A;OICI;FA;;;WD)").unwrap(); - refused(&path, "allows others than the current user"); - for list in [ - format!("D:P(A;OICI;FR;;;{sid})"), - format!("D:P(A;;FA;;;{sid})"), - format!("D:P(A;OICINP;FA;;;{sid})"), - ] { - platform::tests::set_access(&path, &list).unwrap(); - refused( - &path, - "does not allow the current user full access that the files and directories \ - created in it inherit", - ); - } - platform::tests::set_access(&path, &format!("D:P(D;;WO;;;{sid}){full}")).unwrap(); - refused( - &path, - "has an access control entry of type 1, not one that allows access", - ); - platform::tests::set_access(&path, &format!("D:P{full}")).unwrap(); - let inheriting = path.join("inheriting"); - fs::create_dir(&inheriting).unwrap(); - assert_eq!(platform::check_store_dir(&inheriting).unwrap(), None); - let owned = platform::tests::access(&inheriting) - .unwrap() - .starts_with(&format!("O:{sid}D:")); - refused( - &inheriting, - if owned { - "inherits access control entries from its parent" - } else { - "belongs to another user" - }, - ); - let file = directory.path().join("file"); - fs::write(&file, b"").unwrap(); - refused(&file, "is not a plain directory"); -} - -#[test] -fn a_vm_directory_that_cannot_be_opened_or_created_fails_with_the_hosts_error() { - let directory = tempfile::tempdir().unwrap(); - let missing = directory.path().join("missing"); - let relative = Path::new("missing-vm-directory"); - for (error, action, path) in [ - (VmDir::open(&missing).unwrap_err(), "open", missing.clone()), - ( - create_private_dir(&missing.join("vm")).unwrap_err(), - "create", - missing.join("vm"), - ), - ( - VmDir::open(relative).unwrap_err(), - "open", - std::env::current_dir().unwrap().join(relative), - ), - ] { - assert_eq!(error.kind(), ErrorKind::Io, "{error}"); - assert_eq!( - error.to_string(), - format!("cannot {action} the VM directory {}", path.display()) - ); - let source = std::error::Error::source(&error).unwrap(); - let source = source.downcast_ref::().unwrap(); - assert_eq!(source.kind(), io::ErrorKind::NotFound, "{source}"); - } -} - -#[test] -fn outputs_name_the_vm_directorys_files_and_endpoints() { - let (_directory, vm) = vm_dir(); - let outputs = vm.outputs(false).unwrap(); - assert_eq!(outputs.log, vm.path().join("openvmm.log")); - assert_eq!(outputs.outcome, vm.path().join("outcome.json")); - assert_eq!(outputs.console, None); - let with_console = vm.outputs(true).unwrap(); - assert_eq!( - (&with_console.log, &with_console.outcome), - (&outputs.log, &outputs.outcome) - ); - let console = with_console.console.as_deref().unwrap(); - if cfg!(windows) { - for endpoint in [outputs.endpoint.as_str(), &with_console.endpoint, console] { - assert!(is_fresh_pipe(endpoint, "openvmm-microvm"), "{endpoint}"); - } - assert_ne!(outputs.endpoint, with_console.endpoint); - assert_ne!(with_console.endpoint, console); - } else { - let socket = |name: &str| vm.path().join(name).into_os_string().into_string().unwrap(); - assert_eq!(outputs.endpoint, socket("control.sock")); - assert_eq!(with_console.endpoint, outputs.endpoint); - assert_eq!(console, socket("console.sock")); - } -} - -#[cfg(target_os = "linux")] -#[test] -fn the_endpoints_of_a_vm_directory_that_is_not_unicode_are_refused() { - use std::ffi::OsStr; - use std::os::unix::ffi::OsStrExt; - - let directory = tempfile::tempdir().unwrap(); - let path = directory.path().join(OsStr::from_bytes(b"vm-\xff")); - create_private_dir(&path).unwrap(); - let vm = VmDir::open(&path).unwrap(); - for error in [ - vm.outputs(false).unwrap_err(), - vm.restore_ready_endpoint().unwrap_err(), - ] { - assert_eq!(error.kind(), ErrorKind::Unsupported, "{error}"); - assert_eq!( - error.to_string(), - format!( - "the VM directory {} is not valid Unicode, which OpenVMM's endpoints must be", - path.display() - ) - ); - } -} - -#[test] -fn a_restore_ready_endpoint_is_the_reserved_socket_or_a_fresh_pipe() { - let (_directory, vm) = vm_dir(); - let first = vm.restore_ready_endpoint().unwrap(); - let second = vm.restore_ready_endpoint().unwrap(); - if cfg!(windows) { - for endpoint in [&first, &second] { - assert!(is_fresh_pipe(endpoint, "nvx-restore-ready"), "{endpoint}"); - } - assert_ne!(first, second); - let error = fs::File::open(first.replace('/', "\\")).unwrap_err(); - assert_eq!(error.kind(), io::ErrorKind::NotFound, "{error}"); - } else { - let socket = vm.path().join("restore-ready.sock"); - assert_eq!(first, socket.into_os_string().into_string().unwrap()); - assert_eq!(second, first); - } -} - #[test] fn the_reserved_names_are_the_documented_ones_and_leave_the_openvmm_prefix_to_openvmm() { let names = [ @@ -409,21 +71,6 @@ fn a_capability_gives_its_bytes_and_its_debug_hides_them() { assert_eq!(format!("{capability:#?}"), "Capability { .. }"); } -#[test] -fn creating_and_opening_a_vm_directory_writes_no_capability_file_or_output() { - let (directory, vm) = vm_dir(); - create_private_dir(vm.path()).unwrap(); - VmDir::open(vm.path()).unwrap(); - vm.outputs(true).unwrap(); - vm.restore_ready_endpoint().unwrap(); - assert_eq!(fs::read_dir(vm.path()).unwrap().count(), 0); - let entries: Vec<_> = fs::read_dir(directory.path()) - .unwrap() - .map(|entry| entry.unwrap().file_name()) - .collect(); - assert_eq!(entries, ["vm"]); -} - fn cold_config() -> VmConfig { VmConfig { processors: 1, diff --git a/nvx-rs/src/host/vm_directory.rs b/nvx-rs/src/host/vm_directory.rs new file mode 100644 index 000000000..f63bc2aaa --- /dev/null +++ b/nvx-rs/src/host/vm_directory.rs @@ -0,0 +1,148 @@ +//! A caller-owned private VM directory and its fixed output files and endpoints. + +use std::io; +use std::path::{Path, PathBuf}; + +use crate::host::paths::{absolute, normalized}; +#[cfg(not(windows))] +use crate::host::platform::{check_store_dir as check_dir, create_store_dir as create_dir}; +#[cfg(windows)] +use crate::host::platform::{check_vm_dir as check_dir, create_vm_dir as create_dir}; +use crate::{Error, Result}; + +/// OpenVMM's log of a launch. +pub(super) const LOG: &str = "openvmm.log"; +/// OpenVMM's outcome report of a launch. +pub(super) const OUTCOME: &str = "outcome.json"; +/// The control socket on Linux. +pub(super) const CONTROL: &str = "control.sock"; +/// The guest's boot-console socket on Linux. +pub(super) const CONSOLE: &str = "console.sock"; +/// The restore-ready socket on Linux. +pub(super) const RESTORE_READY: &str = "restore-ready.sock"; +/// The prefix OpenVMM requires of its Windows console pipes. +const OPENVMM_PIPE: &str = "openvmm-microvm"; +/// The prefix of a Windows restore-ready pipe. +const RESTORE_READY_PIPE: &str = "nvx-restore-ready"; + +/// The directory of a VM, which its caller creates and owns, private to the current user. +/// +/// The library reserves the fixed names of its launch and snapshot files and endpoints, and +/// every name that starts with `.openvmm`; the caller may add files under other names. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct VmDir { + /// The directory's absolute path. + path: PathBuf, +} + +impl VmDir { + /// Opens an existing directory private to the current user, as [`create_private_dir`] makes + /// it. A relative path names a directory from the current directory. Changes nothing. + /// + /// Fails with [`ErrorKind::Io`](crate::ErrorKind::Io) if the directory is not private or + /// cannot be read, as when it does not exist. + pub fn open(path: &Path) -> Result { + // A trailing separator or `/.` would make the OS follow a final symbolic link before + // applying the platform's no-follow check. Keep `..` components unresolved. + let path = absolute(&normalized(path)).map_err(|error| io_error("open", path, error))?; + let problem = check_dir(&path).map_err(|error| io_error("open", &path, error))?; + check_privacy(&path, problem)?; + Ok(Self { path }) + } + + /// Returns the directory as an absolute path. + pub fn path(&self) -> &Path { + &self.path + } + + /// Returns a launch's outputs, with a boot-console endpoint if requested. Creates nothing. + pub(crate) fn outputs(&self, console: bool) -> Result { + Ok(VmOutputs { + log: self.path.join(LOG), + outcome: self.path.join(OUTCOME), + endpoint: self.endpoint(CONTROL, OPENVMM_PIPE)?, + console: console + .then(|| self.endpoint(CONSOLE, OPENVMM_PIPE)) + .transpose()?, + }) + } + + /// Returns a restore-ready endpoint without listening on it. + pub(crate) fn restore_ready_endpoint(&self) -> Result { + self.endpoint(RESTORE_READY, RESTORE_READY_PIPE) + } + + /// Returns the Linux socket `name` or a fresh Windows pipe with the prefix `pipe`. + fn endpoint(&self, name: &str, pipe: &str) -> Result { + if cfg!(windows) { + let mut bytes = [0u8; 16]; + getrandom::fill(&mut bytes) + .map_err(|error| Error::io("cannot draw the name of a pipe").with_source(error))?; + let suffix: String = bytes.iter().map(|byte| format!("{byte:02x}")).collect(); + return Ok(format!("//./pipe/{pipe}-{suffix}")); + } + self.path + .join(name) + .into_os_string() + .into_string() + .map_err(|_| { + Error::unsupported(format!( + "the VM directory {} is not valid Unicode, which OpenVMM's endpoints must be", + self.path.display() + )) + }) + } +} + +/// Creates a VM directory private to the current user, or checks an existing one without +/// changing it. The parent must exist. +/// +/// On Linux, a new directory has mode 0700, whatever the umask and its parent's setgid bit. On +/// Windows, it belongs to the user, and its protected access control list allows the user alone +/// full access, which the files and directories created in it inherit. +/// +/// Fails with [`ErrorKind::Io`](crate::ErrorKind::Io) if an existing directory is not private or +/// the directory cannot be created or read. +pub fn create_private_dir(path: &Path) -> Result<()> { + let path = normalized(path); + let problem = create_dir(&path).map_err(|error| io_error("create", &path, error))?; + check_privacy(&path, problem) +} + +/// A launch's output files and endpoints. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct VmOutputs { + /// OpenVMM's log, `openvmm.log` in the VM directory. + pub log: PathBuf, + /// OpenVMM's outcome report, `outcome.json` in the VM directory. + pub outcome: PathBuf, + /// The control endpoint: `control.sock` in the VM directory on Linux, or on Windows a fresh + /// named pipe, `//./pipe/openvmm-microvm-` and 32 random lowercase hexadecimal digits. + pub endpoint: String, + /// The boot-console endpoint, if requested: `console.sock` in the VM directory on Linux, or + /// on Windows another fresh `//./pipe/openvmm-microvm-` pipe. + pub console: Option, +} + +/// Retains the host's I/O failure, or classifies an unsupported host as unavailable. +pub(super) fn io_error(action: &str, path: &Path, error: io::Error) -> Error { + let message = format!("cannot {action} the VM directory {}", path.display()); + if error.kind() == io::ErrorKind::Unsupported { + Error::unavailable(message).with_source(error) + } else { + Error::io(message).with_source(error) + } +} + +/// Reports why an existing VM directory is not private without changing its permissions. +fn check_privacy(path: &Path, problem: Option) -> Result<()> { + problem.map_or(Ok(()), |problem| { + Err(Error::io(format!( + "the VM directory {} {problem}", + path.display() + ))) + }) +} + +#[cfg(test)] +pub(super) mod tests; diff --git a/nvx-rs/src/host/vm_directory/tests.rs b/nvx-rs/src/host/vm_directory/tests.rs new file mode 100644 index 000000000..1a284542b --- /dev/null +++ b/nvx-rs/src/host/vm_directory/tests.rs @@ -0,0 +1,358 @@ +use std::fs; + +use super::*; +use crate::ErrorKind; + +fn is_fresh_pipe(endpoint: &str, prefix: &str) -> bool { + endpoint + .strip_prefix(&format!("//./pipe/{prefix}-")) + .is_some_and(|digits| { + digits.len() == 32 + && digits + .bytes() + .all(|digit| matches!(digit, b'0'..=b'9' | b'a'..=b'f')) + }) +} + +pub(in crate::host) fn vm_dir() -> (tempfile::TempDir, VmDir) { + let directory = tempfile::tempdir().unwrap(); + let path = directory.path().join("vm"); + create_private_dir(&path).unwrap(); + let vm = VmDir::open(&path).unwrap(); + (directory, vm) +} + +#[cfg(target_os = "linux")] +#[test] +fn a_vm_directory_is_created_with_mode_700_for_the_current_user_whatever_the_umask() { + use std::os::unix::fs::{MetadataExt, PermissionsExt}; + use std::thread; + + let directory = tempfile::tempdir().unwrap(); + let mode = |path: &Path| fs::metadata(path).unwrap().permissions().mode() & 0o7777; + let parent = directory.path().join("setgid"); + fs::create_dir(&parent).unwrap(); + fs::set_permissions(&parent, fs::Permissions::from_mode(0o2700)).unwrap(); + let path = parent.join("vm"); + thread::scope(|scope| { + scope + .spawn(|| { + nix::sched::unshare(nix::sched::CloneFlags::CLONE_FS).unwrap(); + nix::sys::stat::umask(nix::sys::stat::Mode::from_bits_truncate(0o277)); + create_private_dir(&path) + }) + .join() + .unwrap() + }) + .unwrap(); + assert_eq!(mode(&path), 0o700); + assert_eq!( + fs::metadata(&path).unwrap().uid(), + nix::unistd::Uid::effective().as_raw() + ); + assert_eq!(VmDir::open(&path).unwrap().path(), path); + create_private_dir(&path).unwrap(); + assert_eq!(mode(&path), 0o700); +} + +#[cfg(target_os = "linux")] +#[test] +fn an_existing_directory_that_is_not_private_is_refused_and_left_unchanged() { + use std::os::unix::fs::{PermissionsExt, symlink}; + + use nix::sys::stat::Mode; + use nix::unistd::Uid; + + use crate::host::platform; + + let directory = tempfile::tempdir().unwrap(); + let mode = |path: &Path| fs::symlink_metadata(path).unwrap().permissions().mode() & 0o7777; + let set_mode = |path: &Path, mode: u32| { + fs::set_permissions(path, fs::Permissions::from_mode(mode)).unwrap(); + }; + let refused = |path: &Path, problem: &str| { + for error in [ + create_private_dir(path).unwrap_err(), + VmDir::open(path).unwrap_err(), + ] { + assert_eq!(error.kind(), ErrorKind::Io, "{error}"); + assert_eq!( + error.to_string(), + format!("the VM directory {} {problem}", path.display()) + ); + } + }; + let path = directory.path().join("vm"); + fs::create_dir(&path).unwrap(); + for shared in [0o750, 0o705, 0o770] { + set_mode(&path, shared); + refused( + &path, + &format!("is accessible to other users (mode {shared:03o})"), + ); + assert_eq!(mode(&path), shared); + } + for other in [0o2700, 0o1700, 0o500] { + set_mode(&path, other); + refused( + &path, + &format!( + "has mode {other:03o}, not the mode 700 that the directory of a control socket \ + must have" + ), + ); + assert_eq!(mode(&path), other); + } + assert_eq!(platform::check_owner_only_dir(&path).unwrap(), None); + assert_eq!( + platform::tests::store_dir_problem(Uid::from_raw(1001), Mode::S_IRWXU, Uid::from_raw(1000)), + Some("belongs to user 1001, not to the current user 1000".to_owned()) + ); + set_mode(&path, 0o700); + let link = directory.path().join("link"); + symlink(&path, &link).unwrap(); + refused(&link, "is not a plain directory"); + let file = directory.path().join("file"); + fs::write(&file, b"").unwrap(); + refused(&file, "is not a plain directory"); +} + +#[cfg(target_os = "linux")] +#[test] +fn trailing_separators_and_current_directory_suffixes_do_not_hide_a_symbolic_link() { + use std::os::unix::fs::{PermissionsExt, symlink}; + + let (directory, vm) = vm_dir(); + let link = directory.path().join("link"); + symlink(vm.path(), &link).unwrap(); + for suffix in ["/", "/.", "/./", "//.//"] { + let suffixed = |path: &Path| { + let mut name = path.as_os_str().to_os_string(); + name.push(suffix); + PathBuf::from(name) + }; + let path = suffixed(&link); + for error in [ + create_private_dir(&path).unwrap_err(), + VmDir::open(&path).unwrap_err(), + ] { + assert_eq!(error.kind(), ErrorKind::Io, "{error}"); + assert_eq!( + error.to_string(), + format!( + "the VM directory {} is not a plain directory", + link.display() + ) + ); + } + let private = suffixed(vm.path()); + create_private_dir(&private).unwrap(); + assert_eq!(VmDir::open(&private).unwrap().path(), vm.path()); + } + assert_eq!( + fs::metadata(vm.path()).unwrap().permissions().mode() & 0o7777, + 0o700 + ); + assert!(fs::symlink_metadata(link).unwrap().file_type().is_symlink()); + assert_eq!(fs::read_dir(vm.path()).unwrap().count(), 0); +} + +#[cfg(windows)] +#[test] +fn a_vm_directory_is_created_owner_only_and_protected_under_a_permissive_parent() { + use crate::host::platform; + + let directory = tempfile::tempdir().unwrap(); + platform::tests::set_access(directory.path(), "D:(A;OICI;FA;;;WD)").unwrap(); + let path = directory.path().join("vm"); + create_private_dir(&path).unwrap(); + let sid = platform::tests::user_sid(); + let private = format!("O:{sid}D:P(A;OICI;FA;;;{sid})"); + assert_eq!(platform::tests::access(&path).unwrap(), private); + assert_eq!(VmDir::open(&path).unwrap().path(), path); + create_private_dir(&path).unwrap(); + assert_eq!(platform::tests::access(&path).unwrap(), private); +} + +#[cfg(windows)] +#[test] +fn existing_vm_directories_that_grant_others_or_inherit_are_refused_and_left_unchanged() { + use crate::host::platform; + + let directory = tempfile::tempdir().unwrap(); + let sid = platform::tests::user_sid(); + let refused = |path: &Path, problem: &str| { + let access = platform::tests::access(path).unwrap(); + for error in [ + create_private_dir(path).unwrap_err(), + VmDir::open(path).unwrap_err(), + ] { + assert_eq!(error.kind(), ErrorKind::Io, "{error}"); + assert_eq!( + error.to_string(), + format!("the VM directory {} {problem}", path.display()) + ); + } + assert_eq!(platform::tests::access(path).unwrap(), access); + }; + let path = directory.path().join("vm"); + create_private_dir(&path).unwrap(); + let full = format!("(A;OICI;FA;;;{sid})"); + for other in ["SY", "BA", "OW"] { + platform::tests::set_access(&path, &format!("D:P{full}(A;OICI;FA;;;{other})")).unwrap(); + assert_eq!(platform::check_store_dir(&path).unwrap(), None, "{other}"); + refused(&path, "allows others than the current user"); + } + platform::tests::set_access(&path, "D:P(A;OICI;FA;;;WD)").unwrap(); + refused(&path, "allows others than the current user"); + for list in [ + format!("D:P(A;OICI;FR;;;{sid})"), + format!("D:P(A;;FA;;;{sid})"), + format!("D:P(A;OICINP;FA;;;{sid})"), + ] { + platform::tests::set_access(&path, &list).unwrap(); + refused( + &path, + "does not allow the current user full access that the files and directories \ + created in it inherit", + ); + } + platform::tests::set_access(&path, &format!("D:P(D;;WO;;;{sid}){full}")).unwrap(); + refused( + &path, + "has an access control entry of type 1, not one that allows access", + ); + platform::tests::set_access(&path, &format!("D:P{full}")).unwrap(); + let inheriting = path.join("inheriting"); + fs::create_dir(&inheriting).unwrap(); + assert_eq!(platform::check_store_dir(&inheriting).unwrap(), None); + let owned = platform::tests::access(&inheriting) + .unwrap() + .starts_with(&format!("O:{sid}D:")); + refused( + &inheriting, + if owned { + "inherits access control entries from its parent" + } else { + "belongs to another user" + }, + ); + let file = directory.path().join("file"); + fs::write(&file, b"").unwrap(); + refused(&file, "is not a plain directory"); +} + +#[test] +fn a_vm_directory_that_cannot_be_opened_or_created_fails_with_the_hosts_error() { + let directory = tempfile::tempdir().unwrap(); + let missing = directory.path().join("missing"); + let relative = Path::new("missing-vm-directory"); + for (error, action, path) in [ + (VmDir::open(&missing).unwrap_err(), "open", missing.clone()), + ( + create_private_dir(&missing.join("vm")).unwrap_err(), + "create", + missing.join("vm"), + ), + ( + VmDir::open(relative).unwrap_err(), + "open", + std::env::current_dir().unwrap().join(relative), + ), + ] { + assert_eq!(error.kind(), ErrorKind::Io, "{error}"); + assert_eq!( + error.to_string(), + format!("cannot {action} the VM directory {}", path.display()) + ); + let source = std::error::Error::source(&error).unwrap(); + let source = source.downcast_ref::().unwrap(); + assert_eq!(source.kind(), io::ErrorKind::NotFound, "{source}"); + } +} + +#[test] +fn outputs_name_the_vm_directorys_files_and_endpoints() { + let (_directory, vm) = vm_dir(); + let outputs = vm.outputs(false).unwrap(); + assert_eq!(outputs.log, vm.path().join("openvmm.log")); + assert_eq!(outputs.outcome, vm.path().join("outcome.json")); + assert_eq!(outputs.console, None); + let with_console = vm.outputs(true).unwrap(); + assert_eq!( + (&with_console.log, &with_console.outcome), + (&outputs.log, &outputs.outcome) + ); + let console = with_console.console.as_deref().unwrap(); + if cfg!(windows) { + for endpoint in [outputs.endpoint.as_str(), &with_console.endpoint, console] { + assert!(is_fresh_pipe(endpoint, "openvmm-microvm"), "{endpoint}"); + } + assert_ne!(outputs.endpoint, with_console.endpoint); + assert_ne!(with_console.endpoint, console); + } else { + let socket = |name: &str| vm.path().join(name).into_os_string().into_string().unwrap(); + assert_eq!(outputs.endpoint, socket("control.sock")); + assert_eq!(with_console.endpoint, outputs.endpoint); + assert_eq!(console, socket("console.sock")); + } +} + +#[cfg(target_os = "linux")] +#[test] +fn the_endpoints_of_a_vm_directory_that_is_not_unicode_are_refused() { + use std::ffi::OsStr; + use std::os::unix::ffi::OsStrExt; + + let directory = tempfile::tempdir().unwrap(); + let path = directory.path().join(OsStr::from_bytes(b"vm-\xff")); + create_private_dir(&path).unwrap(); + let vm = VmDir::open(&path).unwrap(); + for error in [ + vm.outputs(false).unwrap_err(), + vm.restore_ready_endpoint().unwrap_err(), + ] { + assert_eq!(error.kind(), ErrorKind::Unsupported, "{error}"); + assert_eq!( + error.to_string(), + format!( + "the VM directory {} is not valid Unicode, which OpenVMM's endpoints must be", + path.display() + ) + ); + } +} + +#[test] +fn a_restore_ready_endpoint_is_the_reserved_socket_or_a_fresh_pipe() { + let (_directory, vm) = vm_dir(); + let first = vm.restore_ready_endpoint().unwrap(); + let second = vm.restore_ready_endpoint().unwrap(); + if cfg!(windows) { + for endpoint in [&first, &second] { + assert!(is_fresh_pipe(endpoint, "nvx-restore-ready"), "{endpoint}"); + } + assert_ne!(first, second); + let error = fs::File::open(first.replace('/', "\\")).unwrap_err(); + assert_eq!(error.kind(), io::ErrorKind::NotFound, "{error}"); + } else { + let socket = vm.path().join("restore-ready.sock"); + assert_eq!(first, socket.into_os_string().into_string().unwrap()); + assert_eq!(second, first); + } +} + +#[test] +fn creating_and_opening_a_vm_directory_writes_no_capability_file_or_output() { + let (directory, vm) = vm_dir(); + create_private_dir(vm.path()).unwrap(); + VmDir::open(vm.path()).unwrap(); + vm.outputs(true).unwrap(); + vm.restore_ready_endpoint().unwrap(); + assert_eq!(fs::read_dir(vm.path()).unwrap().count(), 0); + let entries: Vec<_> = fs::read_dir(directory.path()) + .unwrap() + .map(|entry| entry.unwrap().file_name()) + .collect(); + assert_eq!(entries, ["vm"]); +} diff --git a/nvx-rs/tests/vectors/README.md b/nvx-rs/tests/vectors/README.md index 23aec531a..9aaefe167 100644 --- a/nvx-rs/tests/vectors/README.md +++ b/nvx-rs/tests/vectors/README.md @@ -73,8 +73,10 @@ the key that the vector's bytes decode to. of a class have not been released, so a template store that an earlier build created is not supported, and is deleted and created again, since nothing migrates it. The snapshot key also drops the obsolete guest-build field and - uses the `nvx snapshot key` hash domain in place: no snapshot outlives a - deployment, and the prior key encoding is not supported. + uses the `nvx snapshot key` hash domain in place, and now appends the full + capture-time contract while its compatibility identity still digests only + the preceding compatibility fields: no snapshot outlives a deployment, + and the prior key encoding is not supported. - The bytes come from the protocol's definition, never from the code that they check, and so do the values of each vector. diff --git a/nvx-rs/tests/vectors/snapshot-key-v1.txt b/nvx-rs/tests/vectors/snapshot-key-v1.txt index 99ad0a9af..46fef1809 100644 --- a/nvx-rs/tests/vectors/snapshot-key-v1.txt +++ b/nvx-rs/tests/vectors/snapshot-key-v1.txt @@ -4,9 +4,9 @@ # rules that the vectors follow. # # KEY|NAME|BYTES|IDENTITY -# The canonical encoding of a key, and its identity: the SHA-256 digest of "nvx snapshot key", a -# NUL byte, and the encoding. A decoder must decode the bytes, and an encoder must encode the key -# that they hold to the same bytes. +# The canonical persisted key, and its compatibility identity: the SHA-256 digest of +# "nvx snapshot key", a NUL byte, and the compatibility prefix before the capture-time fields. +# A decoder must decode the full key, and an encoder must reproduce its bytes. # CLASS|NAME|BYTES|IDENTITY # The canonical encoding of a class, and its identity: the SHA-256 digest of "nvx capture class", # a NUL byte, and the encoding. @@ -25,20 +25,24 @@ # nvx-microvm-v2-control-v2, control session 1, snapshot format 6, device-state schema 1, time # ABI 1, restore packet 4, and activation contract 1. # Capture classes alone also hold the guest build guest-build-1. +# TEMPLATE, NETWORK and HOST_PROFILE append capture TSC 12345678, UTC 1700000000000000000 ns, +# monotonic 123000000000 ns, clock linux-boottime, host and boot IDs of 16 bytes of 01 and 02, +# lineage 3 and cut-to-anchor Some(200). MINIMAL uses zero anchors, zero host/boot IDs, lineage 0 +# and no cut-to-anchor interval on linux-boottime. Their rates remain in the compatibility prefix. # The key of the template of a KVM microVM of 2 processors, all online at boot, and 512 MiB # of RAM, of a capacity of 2 GiB, with a distro of 1 GiB, a scratch of 64 MiB, the dormant # filesystem slot, a console, a control console, and no NIC, on the CPU profile # intel.icelake-sp.v1, whose digest is 32 bytes of 5a and whose effective CPUID is the bytes # 00 to 57: the capture of tests/fixtures/template/platform-kvm. -KEY|TEMPLATE|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d05000000696e74656c0a0000006963656c616b652d7370010000005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a58000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f50515253545556570100000001000000020000000100000002000000000000000100000000752b7d0000000000c2eb0b00000000fa000000020000000000000000000020000000000000008000000000000000080000000001000000000000000000000000000020000000000100000000000020000000000000006000000000530100006561726c79636f6e3d78653920636f6e736f6c653d68766331207265626f6f743d742070616e69633d2d31206e76785f73616e64626f783d31206e76785f636f6e6669673d307864303031303030302c3635353336206e725f637075733d32206e76785f736e617073686f745f746965723d706c6174666f726d2076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030313030303a362076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030323030303a372076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030333030303a342076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030363030303a31312076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030373030303a33206e76785f636f6e74726f6c5f7474793d6876633217f80f8858431ca75fcc7ca694ea68637e017ede02db15e18d0ed4581067858d000000400000000000020000001000000000000400000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000001000000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000003000000767030040000007663707509000000706172746974696f6e00000000000000000000000000000000000000000006000000766d74696d6505000000636c6f636b06000000766d74696d65000000000000000000000000000000000000000000030000007069630300000070696303000000706963000000000200000004000000706d696f2000000000000000020000000000000004000000706d696fa00000000000000002000000000000000000000000000000000000000006000000696f6170696306000000696f6170696306000000696f617069630000000001000000040000006d6d696f0000c0fe00000000001000000000000000000000000000000000000000050000006c61706963050000006c6170696309000000706172746974696f6e0000000001000000040000006d6d696f0000e0fe00000000001000000000000000000000000000000000000000030000007069740300000070697403000000706974000000000100000004000000706d696f400000000000000004000000000000000100000000000000000000000000000000030000007274630300000072746303000000727463000000000100000004000000706d696f7000000000000000020000000000000001080000000000000000000000000000000d0000006d6963726f766d2d706f72746205000000706f7274620d0000006d6963726f766d2d706f727462000000000100000004000000706d696fe900000000000000020000000000000000000000000000000000000000100000006d6963726f766d2d73687574646f776e0800000073687574646f776e100000006d6963726f766d2d73687574646f776e000000000100000004000000706d696f0406000000000000010000000000000000000000000000000000000000180000006d6963726f766d2d736e617073686f742d7265717565737410000000736e617073686f742d72657175657374180000006d6963726f766d2d736e617073686f742d72657175657374000000000100000004000000706d696f05060000000000000100000000000000000000000000000000000000000b00000066733a6d6963726f766d300900000076697274696f2d66731300000076697274696f66732d333438393636353032340b00000076697274696f2d6d6d696f01000000040000006d6d696f001000d000000000001000000000000001060000000200000000000030030000000200000002000000000100000001000017000000636f6e736f6c653a6d6963726f766d2d76697274696f300e00000076697274696f2d636f6e736f6c651900000076697274696f2d636f6e736f6c652d333438393636393132300b00000076697274696f2d6d6d696f01000000040000006d6d696f002000d000000000001000000000000001070000000200000001000030030000000200000002000000000100000001000012000000626c6b3a73616e64626f783a64697374726f0a00000076697274696f2d626c6b1500000076697274696f2d626c6b2d333438393637333231360b00000076697274696f2d6d6d696f01000000040000006d6d696f003000d0000000000010000000000000010400000002000000640600300300000001000000010000000001000013000000626c6b3a73616e64626f783a736372617463680a00000076697274696f2d626c6b1500000076697274696f2d626c6b2d333438393638353530340b00000076697274696f2d6d6d696f01000000040000006d6d696f006000d0000000000010000000000000010b00000002000000440600300300000001000000010000000001000018000000636f6e736f6c653a6d6963726f766d2d636f6e74726f6c301600000076697274696f2d636f6e74726f6c2d636f6e736f6c652100000076697274696f2d636f6e74726f6c2d636f6e736f6c652d333438393638393630300b00000076697274696f2d6d6d696f01000000040000006d6d696f007000d000000000001000000000000001030000000200000001000030030000000200000002000000000100000001000001000000070000006d6963726f766d0000000000|dc593980266a3bc96c177fbfb8fea08e958dc54609cdb07c988a33992d09aa32 +KEY|TEMPLATE|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d05000000696e74656c0a0000006963656c616b652d7370010000005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a58000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f50515253545556570100000001000000020000000100000002000000000000000100000000752b7d0000000000c2eb0b00000000fa000000020000000000000000000020000000000000008000000000000000080000000001000000000000000000000000000020000000000100000000000020000000000000006000000000530100006561726c79636f6e3d78653920636f6e736f6c653d68766331207265626f6f743d742070616e69633d2d31206e76785f73616e64626f783d31206e76785f636f6e6669673d307864303031303030302c3635353336206e725f637075733d32206e76785f736e617073686f745f746965723d706c6174666f726d2076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030313030303a362076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030323030303a372076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030333030303a342076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030363030303a31312076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030373030303a33206e76785f636f6e74726f6c5f7474793d6876633217f80f8858431ca75fcc7ca694ea68637e017ede02db15e18d0ed4581067858d000000400000000000020000001000000000000400000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000001000000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000003000000767030040000007663707509000000706172746974696f6e00000000000000000000000000000000000000000006000000766d74696d6505000000636c6f636b06000000766d74696d65000000000000000000000000000000000000000000030000007069630300000070696303000000706963000000000200000004000000706d696f2000000000000000020000000000000004000000706d696fa00000000000000002000000000000000000000000000000000000000006000000696f6170696306000000696f6170696306000000696f617069630000000001000000040000006d6d696f0000c0fe00000000001000000000000000000000000000000000000000050000006c61706963050000006c6170696309000000706172746974696f6e0000000001000000040000006d6d696f0000e0fe00000000001000000000000000000000000000000000000000030000007069740300000070697403000000706974000000000100000004000000706d696f400000000000000004000000000000000100000000000000000000000000000000030000007274630300000072746303000000727463000000000100000004000000706d696f7000000000000000020000000000000001080000000000000000000000000000000d0000006d6963726f766d2d706f72746205000000706f7274620d0000006d6963726f766d2d706f727462000000000100000004000000706d696fe900000000000000020000000000000000000000000000000000000000100000006d6963726f766d2d73687574646f776e0800000073687574646f776e100000006d6963726f766d2d73687574646f776e000000000100000004000000706d696f0406000000000000010000000000000000000000000000000000000000180000006d6963726f766d2d736e617073686f742d7265717565737410000000736e617073686f742d72657175657374180000006d6963726f766d2d736e617073686f742d72657175657374000000000100000004000000706d696f05060000000000000100000000000000000000000000000000000000000b00000066733a6d6963726f766d300900000076697274696f2d66731300000076697274696f66732d333438393636353032340b00000076697274696f2d6d6d696f01000000040000006d6d696f001000d000000000001000000000000001060000000200000000000030030000000200000002000000000100000001000017000000636f6e736f6c653a6d6963726f766d2d76697274696f300e00000076697274696f2d636f6e736f6c651900000076697274696f2d636f6e736f6c652d333438393636393132300b00000076697274696f2d6d6d696f01000000040000006d6d696f002000d000000000001000000000000001070000000200000001000030030000000200000002000000000100000001000012000000626c6b3a73616e64626f783a64697374726f0a00000076697274696f2d626c6b1500000076697274696f2d626c6b2d333438393637333231360b00000076697274696f2d6d6d696f01000000040000006d6d696f003000d0000000000010000000000000010400000002000000640600300300000001000000010000000001000013000000626c6b3a73616e64626f783a736372617463680a00000076697274696f2d626c6b1500000076697274696f2d626c6b2d333438393638353530340b00000076697274696f2d6d6d696f01000000040000006d6d696f006000d0000000000010000000000000010b00000002000000440600300300000001000000010000000001000018000000636f6e736f6c653a6d6963726f766d2d636f6e74726f6c301600000076697274696f2d636f6e74726f6c2d636f6e736f6c652100000076697274696f2d636f6e74726f6c2d636f6e736f6c652d333438393638393630300b00000076697274696f2d6d6d696f01000000040000006d6d696f007000d000000000001000000000000001030000000200000001000030030000000200000002000000000100000001000001000000070000006d6963726f766d00000000004e61bc000000000000002a36fe9c9717000e5fa31c0000000e0000006c696e75782d626f6f7474696d6501010101010101010101010101010101020202020202020202020202020202020300000001c800000000000000|dc593980266a3bc96c177fbfb8fea08e958dc54609cdb07c988a33992d09aa32 # The same with a fixed NIC at 10.0.0.2/24, with its IPv6 identity, under an allow-list # policy whose digest is 32 bytes of 07, and the invariant settings isolation.seccomp=strict # and limits.pids=512. -KEY|NETWORK|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d05000000696e74656c0a0000006963656c616b652d7370010000005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a58000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f50515253545556570100000001000000020000000100000002000000000000000100000000752b7d0000000000c2eb0b00000000fa000000020000000000000000000020000000000000008000000000000000080000000001000000000000000000000000000020000000000100000000000020000000000000006000000000f20100006561726c79636f6e3d78653920636f6e736f6c653d68766331207265626f6f743d742070616e69633d2d31206e76785f73616e64626f783d31206e76785f636f6e6669673d307864303031303030302c3635353336206e725f637075733d32206e76785f736e617073686f745f746965723d706c6174666f726d2076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030303030303a31302076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030313030303a362076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030323030303a372076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030333030303a342076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030363030303a31312076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030373030303a33206e76785f636f6e74726f6c5f7474793d6876633220766972746e65745f69703d31302e302e302e3220766972746e65745f6d61736b3d3235352e3235352e3235352e3020766972746e65745f67773d31302e302e302e3120766972746e65745f6970363d666430303a3a6130303a322f31323020766972746e65745f6777363d666430303a3a6130303a315e7d49c200c9d60e74547b549917b665501635035894c15c2d7780210f95caf0000000400000000000020000001000000000000400000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000001100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000003000000767030040000007663707509000000706172746974696f6e00000000000000000000000000000000000000000006000000766d74696d6505000000636c6f636b06000000766d74696d65000000000000000000000000000000000000000000030000007069630300000070696303000000706963000000000200000004000000706d696f2000000000000000020000000000000004000000706d696fa00000000000000002000000000000000000000000000000000000000006000000696f6170696306000000696f6170696306000000696f617069630000000001000000040000006d6d696f0000c0fe00000000001000000000000000000000000000000000000000050000006c61706963050000006c6170696309000000706172746974696f6e0000000001000000040000006d6d696f0000e0fe00000000001000000000000000000000000000000000000000030000007069740300000070697403000000706974000000000100000004000000706d696f400000000000000004000000000000000100000000000000000000000000000000030000007274630300000072746303000000727463000000000100000004000000706d696f7000000000000000020000000000000001080000000000000000000000000000000d0000006d6963726f766d2d706f72746205000000706f7274620d0000006d6963726f766d2d706f727462000000000100000004000000706d696fe900000000000000020000000000000000000000000000000000000000100000006d6963726f766d2d73687574646f776e0800000073687574646f776e100000006d6963726f766d2d73687574646f776e000000000100000004000000706d696f0406000000000000010000000000000000000000000000000000000000180000006d6963726f766d2d736e617073686f742d7265717565737410000000736e617073686f742d72657175657374180000006d6963726f766d2d736e617073686f742d72657175657374000000000100000004000000706d696f05060000000000000100000000000000000000000000000000000000000c0000006e65743a6d6963726f766d300a00000076697274696f2d6e65741500000076697274696f2d6e65742d333438393636303932380b00000076697274696f2d6d6d696f01000000040000006d6d696f000000d0000000000010000000000000010a000000020000002000000001000000020000000200000000010000000100000b00000066733a6d6963726f766d300900000076697274696f2d66731300000076697274696f66732d333438393636353032340b00000076697274696f2d6d6d696f01000000040000006d6d696f001000d000000000001000000000000001060000000200000000000030030000000200000002000000000100000001000017000000636f6e736f6c653a6d6963726f766d2d76697274696f300e00000076697274696f2d636f6e736f6c651900000076697274696f2d636f6e736f6c652d333438393636393132300b00000076697274696f2d6d6d696f01000000040000006d6d696f002000d000000000001000000000000001070000000200000001000030030000000200000002000000000100000001000012000000626c6b3a73616e64626f783a64697374726f0a00000076697274696f2d626c6b1500000076697274696f2d626c6b2d333438393637333231360b00000076697274696f2d6d6d696f01000000040000006d6d696f003000d0000000000010000000000000010400000002000000640600300300000001000000010000000001000013000000626c6b3a73616e64626f783a736372617463680a00000076697274696f2d626c6b1500000076697274696f2d626c6b2d333438393638353530340b00000076697274696f2d6d6d696f01000000040000006d6d696f006000d0000000000010000000000000010b00000002000000440600300300000001000000010000000001000018000000636f6e736f6c653a6d6963726f766d2d636f6e74726f6c301600000076697274696f2d636f6e74726f6c2d636f6e736f6c652100000076697274696f2d636f6e74726f6c2d636f6e736f6c652d333438393638393630300b00000076697274696f2d6d6d696f01000000040000006d6d696f007000d000000000001000000000000001030000000200000001000030030000000200000002000000000100000001000001000000070000006d6963726f766d0108000000706f727461626c655254000000025254000000010a000002180a00000101fd00000000000000000000000a00000278fd00000000000000000000000a0000010a000000616c6c6f772d6c69737407070707070707070707070707070707070707070707070707070707070707070104000000020000001100000069736f6c6174696f6e2e736563636f6d70060000007374726963740b0000006c696d6974732e7069647303000000353132|af73b6bf51095c0c2ab84fb114bfa63d3e0dc1fd2b6f56f9a729391e19c762f9 +KEY|NETWORK|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d05000000696e74656c0a0000006963656c616b652d7370010000005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a58000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f50515253545556570100000001000000020000000100000002000000000000000100000000752b7d0000000000c2eb0b00000000fa000000020000000000000000000020000000000000008000000000000000080000000001000000000000000000000000000020000000000100000000000020000000000000006000000000f20100006561726c79636f6e3d78653920636f6e736f6c653d68766331207265626f6f743d742070616e69633d2d31206e76785f73616e64626f783d31206e76785f636f6e6669673d307864303031303030302c3635353336206e725f637075733d32206e76785f736e617073686f745f746965723d706c6174666f726d2076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030303030303a31302076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030313030303a362076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030323030303a372076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030333030303a342076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030363030303a31312076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030373030303a33206e76785f636f6e74726f6c5f7474793d6876633220766972746e65745f69703d31302e302e302e3220766972746e65745f6d61736b3d3235352e3235352e3235352e3020766972746e65745f67773d31302e302e302e3120766972746e65745f6970363d666430303a3a6130303a322f31323020766972746e65745f6777363d666430303a3a6130303a315e7d49c200c9d60e74547b549917b665501635035894c15c2d7780210f95caf0000000400000000000020000001000000000000400000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000001100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000003000000767030040000007663707509000000706172746974696f6e00000000000000000000000000000000000000000006000000766d74696d6505000000636c6f636b06000000766d74696d65000000000000000000000000000000000000000000030000007069630300000070696303000000706963000000000200000004000000706d696f2000000000000000020000000000000004000000706d696fa00000000000000002000000000000000000000000000000000000000006000000696f6170696306000000696f6170696306000000696f617069630000000001000000040000006d6d696f0000c0fe00000000001000000000000000000000000000000000000000050000006c61706963050000006c6170696309000000706172746974696f6e0000000001000000040000006d6d696f0000e0fe00000000001000000000000000000000000000000000000000030000007069740300000070697403000000706974000000000100000004000000706d696f400000000000000004000000000000000100000000000000000000000000000000030000007274630300000072746303000000727463000000000100000004000000706d696f7000000000000000020000000000000001080000000000000000000000000000000d0000006d6963726f766d2d706f72746205000000706f7274620d0000006d6963726f766d2d706f727462000000000100000004000000706d696fe900000000000000020000000000000000000000000000000000000000100000006d6963726f766d2d73687574646f776e0800000073687574646f776e100000006d6963726f766d2d73687574646f776e000000000100000004000000706d696f0406000000000000010000000000000000000000000000000000000000180000006d6963726f766d2d736e617073686f742d7265717565737410000000736e617073686f742d72657175657374180000006d6963726f766d2d736e617073686f742d72657175657374000000000100000004000000706d696f05060000000000000100000000000000000000000000000000000000000c0000006e65743a6d6963726f766d300a00000076697274696f2d6e65741500000076697274696f2d6e65742d333438393636303932380b00000076697274696f2d6d6d696f01000000040000006d6d696f000000d0000000000010000000000000010a000000020000002000000001000000020000000200000000010000000100000b00000066733a6d6963726f766d300900000076697274696f2d66731300000076697274696f66732d333438393636353032340b00000076697274696f2d6d6d696f01000000040000006d6d696f001000d000000000001000000000000001060000000200000000000030030000000200000002000000000100000001000017000000636f6e736f6c653a6d6963726f766d2d76697274696f300e00000076697274696f2d636f6e736f6c651900000076697274696f2d636f6e736f6c652d333438393636393132300b00000076697274696f2d6d6d696f01000000040000006d6d696f002000d000000000001000000000000001070000000200000001000030030000000200000002000000000100000001000012000000626c6b3a73616e64626f783a64697374726f0a00000076697274696f2d626c6b1500000076697274696f2d626c6b2d333438393637333231360b00000076697274696f2d6d6d696f01000000040000006d6d696f003000d0000000000010000000000000010400000002000000640600300300000001000000010000000001000013000000626c6b3a73616e64626f783a736372617463680a00000076697274696f2d626c6b1500000076697274696f2d626c6b2d333438393638353530340b00000076697274696f2d6d6d696f01000000040000006d6d696f006000d0000000000010000000000000010b00000002000000440600300300000001000000010000000001000018000000636f6e736f6c653a6d6963726f766d2d636f6e74726f6c301600000076697274696f2d636f6e74726f6c2d636f6e736f6c652100000076697274696f2d636f6e74726f6c2d636f6e736f6c652d333438393638393630300b00000076697274696f2d6d6d696f01000000040000006d6d696f007000d000000000001000000000000001030000000200000001000030030000000200000002000000000100000001000001000000070000006d6963726f766d0108000000706f727461626c655254000000025254000000010a000002180a00000101fd00000000000000000000000a00000278fd00000000000000000000000a0000010a000000616c6c6f772d6c69737407070707070707070707070707070707070707070707070707070707070707070104000000020000001100000069736f6c6174696f6e2e736563636f6d70060000007374726963740b0000006c696d6974732e70696473030000003531324e61bc000000000000002a36fe9c9717000e5fa31c0000000e0000006c696e75782d626f6f7474696d6501010101010101010101010101010101020202020202020202020202020202020300000001c800000000000000|af73b6bf51095c0c2ab84fb114bfa63d3e0dc1fd2b6f56f9a729391e19c762f9 # TEMPLATE on the host profile intel.host.v1. -KEY|HOST_PROFILE|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d05000000696e74656c04000000686f7374010000005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a58000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f50515253545556570100000001000000020000000100000002000000000000000100000000752b7d0000000000c2eb0b00000000fa000000020000000000000000000020000000000000008000000000000000080000000001000000000000000000000000000020000000000100000000000020000000000000006000000000530100006561726c79636f6e3d78653920636f6e736f6c653d68766331207265626f6f743d742070616e69633d2d31206e76785f73616e64626f783d31206e76785f636f6e6669673d307864303031303030302c3635353336206e725f637075733d32206e76785f736e617073686f745f746965723d706c6174666f726d2076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030313030303a362076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030323030303a372076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030333030303a342076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030363030303a31312076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030373030303a33206e76785f636f6e74726f6c5f7474793d6876633217f80f8858431ca75fcc7ca694ea68637e017ede02db15e18d0ed4581067858d000000400000000000020000001000000000000400000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000001000000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000003000000767030040000007663707509000000706172746974696f6e00000000000000000000000000000000000000000006000000766d74696d6505000000636c6f636b06000000766d74696d65000000000000000000000000000000000000000000030000007069630300000070696303000000706963000000000200000004000000706d696f2000000000000000020000000000000004000000706d696fa00000000000000002000000000000000000000000000000000000000006000000696f6170696306000000696f6170696306000000696f617069630000000001000000040000006d6d696f0000c0fe00000000001000000000000000000000000000000000000000050000006c61706963050000006c6170696309000000706172746974696f6e0000000001000000040000006d6d696f0000e0fe00000000001000000000000000000000000000000000000000030000007069740300000070697403000000706974000000000100000004000000706d696f400000000000000004000000000000000100000000000000000000000000000000030000007274630300000072746303000000727463000000000100000004000000706d696f7000000000000000020000000000000001080000000000000000000000000000000d0000006d6963726f766d2d706f72746205000000706f7274620d0000006d6963726f766d2d706f727462000000000100000004000000706d696fe900000000000000020000000000000000000000000000000000000000100000006d6963726f766d2d73687574646f776e0800000073687574646f776e100000006d6963726f766d2d73687574646f776e000000000100000004000000706d696f0406000000000000010000000000000000000000000000000000000000180000006d6963726f766d2d736e617073686f742d7265717565737410000000736e617073686f742d72657175657374180000006d6963726f766d2d736e617073686f742d72657175657374000000000100000004000000706d696f05060000000000000100000000000000000000000000000000000000000b00000066733a6d6963726f766d300900000076697274696f2d66731300000076697274696f66732d333438393636353032340b00000076697274696f2d6d6d696f01000000040000006d6d696f001000d000000000001000000000000001060000000200000000000030030000000200000002000000000100000001000017000000636f6e736f6c653a6d6963726f766d2d76697274696f300e00000076697274696f2d636f6e736f6c651900000076697274696f2d636f6e736f6c652d333438393636393132300b00000076697274696f2d6d6d696f01000000040000006d6d696f002000d000000000001000000000000001070000000200000001000030030000000200000002000000000100000001000012000000626c6b3a73616e64626f783a64697374726f0a00000076697274696f2d626c6b1500000076697274696f2d626c6b2d333438393637333231360b00000076697274696f2d6d6d696f01000000040000006d6d696f003000d0000000000010000000000000010400000002000000640600300300000001000000010000000001000013000000626c6b3a73616e64626f783a736372617463680a00000076697274696f2d626c6b1500000076697274696f2d626c6b2d333438393638353530340b00000076697274696f2d6d6d696f01000000040000006d6d696f006000d0000000000010000000000000010b00000002000000440600300300000001000000010000000001000018000000636f6e736f6c653a6d6963726f766d2d636f6e74726f6c301600000076697274696f2d636f6e74726f6c2d636f6e736f6c652100000076697274696f2d636f6e74726f6c2d636f6e736f6c652d333438393638393630300b00000076697274696f2d6d6d696f01000000040000006d6d696f007000d000000000001000000000000001030000000200000001000030030000000200000002000000000100000001000001000000070000006d6963726f766d0000000000|35f14fb1b596b299a43751b7827b7e7b88bc5cd330ff0d8eae2ee40b3d7e0fc2 +KEY|HOST_PROFILE|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d05000000696e74656c04000000686f7374010000005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a58000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f50515253545556570100000001000000020000000100000002000000000000000100000000752b7d0000000000c2eb0b00000000fa000000020000000000000000000020000000000000008000000000000000080000000001000000000000000000000000000020000000000100000000000020000000000000006000000000530100006561726c79636f6e3d78653920636f6e736f6c653d68766331207265626f6f743d742070616e69633d2d31206e76785f73616e64626f783d31206e76785f636f6e6669673d307864303031303030302c3635353336206e725f637075733d32206e76785f736e617073686f745f746965723d706c6174666f726d2076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030313030303a362076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030323030303a372076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030333030303a342076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030363030303a31312076697274696f5f6d6d696f2e6465766963653d30783130303040307864303030373030303a33206e76785f636f6e74726f6c5f7474793d6876633217f80f8858431ca75fcc7ca694ea68637e017ede02db15e18d0ed4581067858d000000400000000000020000001000000000000400000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000001000000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000003000000767030040000007663707509000000706172746974696f6e00000000000000000000000000000000000000000006000000766d74696d6505000000636c6f636b06000000766d74696d65000000000000000000000000000000000000000000030000007069630300000070696303000000706963000000000200000004000000706d696f2000000000000000020000000000000004000000706d696fa00000000000000002000000000000000000000000000000000000000006000000696f6170696306000000696f6170696306000000696f617069630000000001000000040000006d6d696f0000c0fe00000000001000000000000000000000000000000000000000050000006c61706963050000006c6170696309000000706172746974696f6e0000000001000000040000006d6d696f0000e0fe00000000001000000000000000000000000000000000000000030000007069740300000070697403000000706974000000000100000004000000706d696f400000000000000004000000000000000100000000000000000000000000000000030000007274630300000072746303000000727463000000000100000004000000706d696f7000000000000000020000000000000001080000000000000000000000000000000d0000006d6963726f766d2d706f72746205000000706f7274620d0000006d6963726f766d2d706f727462000000000100000004000000706d696fe900000000000000020000000000000000000000000000000000000000100000006d6963726f766d2d73687574646f776e0800000073687574646f776e100000006d6963726f766d2d73687574646f776e000000000100000004000000706d696f0406000000000000010000000000000000000000000000000000000000180000006d6963726f766d2d736e617073686f742d7265717565737410000000736e617073686f742d72657175657374180000006d6963726f766d2d736e617073686f742d72657175657374000000000100000004000000706d696f05060000000000000100000000000000000000000000000000000000000b00000066733a6d6963726f766d300900000076697274696f2d66731300000076697274696f66732d333438393636353032340b00000076697274696f2d6d6d696f01000000040000006d6d696f001000d000000000001000000000000001060000000200000000000030030000000200000002000000000100000001000017000000636f6e736f6c653a6d6963726f766d2d76697274696f300e00000076697274696f2d636f6e736f6c651900000076697274696f2d636f6e736f6c652d333438393636393132300b00000076697274696f2d6d6d696f01000000040000006d6d696f002000d000000000001000000000000001070000000200000001000030030000000200000002000000000100000001000012000000626c6b3a73616e64626f783a64697374726f0a00000076697274696f2d626c6b1500000076697274696f2d626c6b2d333438393637333231360b00000076697274696f2d6d6d696f01000000040000006d6d696f003000d0000000000010000000000000010400000002000000640600300300000001000000010000000001000013000000626c6b3a73616e64626f783a736372617463680a00000076697274696f2d626c6b1500000076697274696f2d626c6b2d333438393638353530340b00000076697274696f2d6d6d696f01000000040000006d6d696f006000d0000000000010000000000000010b00000002000000440600300300000001000000010000000001000018000000636f6e736f6c653a6d6963726f766d2d636f6e74726f6c301600000076697274696f2d636f6e74726f6c2d636f6e736f6c652100000076697274696f2d636f6e74726f6c2d636f6e736f6c652d333438393638393630300b00000076697274696f2d6d6d696f01000000040000006d6d696f007000d000000000001000000000000001030000000200000001000030030000000200000002000000000100000001000001000000070000006d6963726f766d00000000004e61bc000000000000002a36fe9c9717000e5fa31c0000000e0000006c696e75782d626f6f7474696d6501010101010101010101010101010101020202020202020202020202020202020300000001c800000000000000|35f14fb1b596b299a43751b7827b7e7b88bc5cd330ff0d8eae2ee40b3d7e0fc2 # A machine of one processor and one device, and no NIC, which the invalid vectors change. -KEY|MINIMAL|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d0000000000|9adf5b7b4c5509968914b0cae4664263c768bab0e8e010616b562b0023331d64 +KEY|MINIMAL|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000|9adf5b7b4c5509968914b0cae4664263c768bab0e8e010616b562b0023331d64 # The class of the capture of TEMPLATE: the CPU profile auto, and 2 processors, all online at # boot. @@ -48,53 +52,53 @@ CLASS|TEMPLATE|0100470000007368613235363a616161616161616161616161616161616161616 CLASS|NETWORK|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d763201000000000000000d00000067756573742d6275696c642d3106000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d040000006175746f02000000020000000000002000000000000000800000000000000040000000000002000000100000000000040000000000020000000200000a000000050000002d2d6e65740b00000031302e302e302e322f3234110000002d2d6e6574776f726b2d70726f66696c6508000000706f727461626c65100000002d2d6e6574776f726b2d6567726573730400000064656e79110000002d2d6e6574776f726b2d696e67726573730400000064656e790f0000002d2d686f73742d6c6f6f706261636b0400000064656e79020000001100000069736f6c6174696f6e2e736563636f6d70060000007374726963740b0000006c696d6974732e7069647303000000353132|2f32edae7e9eb532625af600232c68aa78b92e02c8116cb49f857f13ff5fcc7c # MINIMAL in other versions of the encoding. -INVALID_KEY_VERSION|VERSION_0|0000470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d0000000000 -INVALID_KEY_VERSION|VERSION_2|0200470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d0000000000 -INVALID_KEY_VERSION|VERSION_65535|ffff470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d0000000000 +INVALID_KEY_VERSION|VERSION_0|0000470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 +INVALID_KEY_VERSION|VERSION_2|0200470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 +INVALID_KEY_VERSION|VERSION_65535|ffff470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 # No bytes. INVALID_KEY|EMPTY| # MINIMAL without its last byte. -INVALID_KEY|TRUNCATED|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00000000 +INVALID_KEY|TRUNCATED|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d65000000000000000000000000000000000000000000000000000000000000000000000000 # MINIMAL and a byte after it. -INVALID_KEY|TRAILING_BYTE|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d000000000000 +INVALID_KEY|TRAILING_BYTE|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d650000000000000000000000000000000000000000000000000000000000000000000000000000 # A command line whose digest does not match it. -INVALID_KEY|COMMAND_LINE_DIGEST|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663300000000000000000000000000000000000000000000000000000000000000000001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d0000000000 +INVALID_KEY|COMMAND_LINE_DIGEST|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663300000000000000000000000000000000000000000000000000000000000000000001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 # A backend that does not exist. -INVALID_KEY|UNKNOWN_BACKEND|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d76320100000000000000060000000000000001000000000000000100000000000000040000000000000001000000000000000300000078656e03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d0000000000 +INVALID_KEY|UNKNOWN_BACKEND|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d76320100000000000000060000000000000001000000000000000100000000000000040000000000000001000000000000000300000078656e03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 # A device range of an address space that does not exist. -INVALID_KEY|UNKNOWN_ADDRESS_SPACE|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e000000000100000004000000706f7274200000000000000002000000000000000000000000000000000000000001000000070000006d6963726f766d0000000000 +INVALID_KEY|UNKNOWN_ADDRESS_SPACE|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e000000000100000004000000706f7274200000000000000002000000000000000000000000000000000000000001000000070000006d6963726f766d00000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 # An interrupt delivery mode that is not UTF-8. -INVALID_KEY|INTERRUPT_MODE_NOT_UTF8|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000013000000656467652d7368617265642dff737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d0000000000 +INVALID_KEY|INTERRUPT_MODE_NOT_UTF8|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000013000000656467652d7368617265642dff737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 # A NIC whose presence tag is 2. -INVALID_KEY|PRESENCE_TAG_2|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d0200000000 +INVALID_KEY|PRESENCE_TAG_2|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d02000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 # A NIC whose policy's enforcement is 2. -INVALID_KEY|BOOLEAN_2|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d0108000000706f727461626c655254000000025254000000010a000002180a00000101fd00000000000000000000000a00000278fd00000000000000000000000a0000010a000000616c6c6f772d6c6973740707070707070707070707070707070707070707070707070707070707070707020400000000000000 +INVALID_KEY|BOOLEAN_2|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d0108000000706f727461626c655254000000025254000000010a000002180a00000101fd00000000000000000000000a00000278fd00000000000000000000000a0000010a000000616c6c6f772d6c69737407070707070707070707070707070707070707070707070707070707070707070204000000000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 # Invariant settings out of the order of their names. -INVALID_KEY|UNSORTED_SETTINGS|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00020000000b0000006c696d6974732e70696473030000003531321100000069736f6c6174696f6e2e736563636f6d7006000000737472696374 +INVALID_KEY|UNSORTED_SETTINGS|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00020000000b0000006c696d6974732e70696473030000003531321100000069736f6c6174696f6e2e736563636f6d70060000007374726963740000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 # An invariant setting named twice. -INVALID_KEY|DUPLICATE_SETTINGS|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00020000000b0000006c696d6974732e70696473030000003531320b0000006c696d6974732e7069647303000000353132 +INVALID_KEY|DUPLICATE_SETTINGS|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00020000000b0000006c696d6974732e70696473030000003531320b0000006c696d6974732e70696473030000003531320000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 # An invariant setting whose name holds an uppercase letter. -INVALID_KEY|UPPERCASE_SETTING|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00010000000b0000004c696d6974732e7069647303000000353132 +INVALID_KEY|UPPERCASE_SETTING|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00010000000b0000004c696d6974732e70696473030000003531320000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 # More APIC IDs than a microVM has processors. -INVALID_KEY|TOO_MANY_APIC_IDS|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b010000000100000001000000010000000900000000000000010000000200000003000000040000000500000006000000070000000800000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d0000000000 +INVALID_KEY|TOO_MANY_APIC_IDS|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b010000000100000001000000010000000900000000000000010000000200000003000000040000000500000006000000070000000800000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 # 3 processors, which a microVM cannot have. -INVALID_KEY|THREE_PROCESSORS|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b010000000100000003000000010000000300000000000000010000000200000000ca9a3b0000000000e1f505000000000000000003000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d0000000000 +INVALID_KEY|THREE_PROCESSORS|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b010000000100000003000000010000000300000000000000010000000200000000ca9a3b0000000000e1f505000000000000000003000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 # RAM ranges that do not hold the RAM. -INVALID_KEY|MEMORY_RANGES|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000400000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d0000000000 +INVALID_KEY|MEMORY_RANGES|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000400000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 # A machine without devices. -INVALID_KEY|NO_DEVICES|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000000000001000000070000006d6963726f766d0000000000 +INVALID_KEY|NO_DEVICES|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000000000001000000070000006d6963726f766d00000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 # A device ID twice. -INVALID_KEY|DUPLICATE_DEVICE|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000200000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000009000000706172746974696f6e05000000636c6f636b06000000766d74696d6500000000000000000000000000000000000000000001000000070000006d6963726f766d0000000000 +INVALID_KEY|DUPLICATE_DEVICE|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000200000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000009000000706172746974696f6e05000000636c6f636b06000000766d74696d6500000000000000000000000000000000000000000001000000070000006d6963726f766d00000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 # A filesystem slot that is not the dormant slot of version 1. -INVALID_KEY|SLOT_VERSION_0|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000000000000070000006d6963726f766d0000000000 +INVALID_KEY|SLOT_VERSION_0|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000000000000070000006d6963726f766d00000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 # A filesystem slot without a tag. -INVALID_KEY|EMPTY_TAG|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000000000000000000000 +INVALID_KEY|EMPTY_TAG|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 # A distro whose logical block size is 256. -INVALID_KEY|DISTRO_BLOCK_SIZE|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000010000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d0000000000 +INVALID_KEY|DISTRO_BLOCK_SIZE|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000010000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 # An OpenVMM identity that holds a space. -INVALID_KEY|ARTIFACT_WITH_SPACE|0100470000007368613235363a20616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d0000000000 +INVALID_KEY|ARTIFACT_WITH_SPACE|0100470000007368613235363a20616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 # A CPU profile of revision 0. -INVALID_KEY|CPU_PROFILE_REVISION_0|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610000000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d0000000000 +INVALID_KEY|CPU_PROFILE_REVISION_0|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610000000011111111111111111111111111111111111111111111111111111111111111112c000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000 # An effective CPUID that is not whole entries of 44 bytes. -INVALID_KEY|CPUID_LENGTH|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112b000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d0000000000 +INVALID_KEY|CPUID_LENGTH|0100470000007368613235363a61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161470000007368613235363a62626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262626262470000007368613235363a636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363630300000000000000190000006e76782d6d6963726f766d2d76322d636f6e74726f6c2d7632010000000000000006000000000000000100000000000000010000000000000004000000000000000100000000000000030000006b766d03000000616d640500000067656e6f610100000011111111111111111111111111111111111111111111111111111111111111112b000000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a01000000010000000100000001000000010000000000000000ca9a3b0000000000e1f505000000000000000001000000000000000000000800000000000000080000000000000008000000000100000000000000000000000000000800000000000000000c000000636f6e736f6c653d687663308c7f813b328f96bdb26c4e97b6f5c874cbd968e8664778addc56a7c81ea55113001000000000000000020000000200000010000000000000000200000002000012000000656467652d7368617265642d737461747573000003000000000000100000000000000100000009000000706172746974696f6e09000000706172746974696f6e09000000706172746974696f6e00000000000000000000000000000000000000000001000000070000006d6963726f766d00000000000000000000000000000000000000000000000000000000000e0000006c696e75782d626f6f7474696d6500000000000000000000000000000000000000000000000000000000000000000000000000