feat(report): record the SEV platform facts that distinguish otherwise-identical hosts - #311
Draft
markg-github wants to merge 6 commits into
Draft
feat(report): record the SEV platform facts that distinguish otherwise-identical hosts#311markg-github wants to merge 6 commits into
markg-github wants to merge 6 commits into
Conversation
Two hosts can be identical by OS, kernel, QEMU and OVMF version and still
behave differently under SNP, so a failing certification result currently
cannot be diagnosed from its own report.
Record the facts that actually distinguish them:
- SEV firmware version. This is the firmware in effect, which is not
necessarily the one the BIOS supplied: the ccp driver loads
/lib/firmware/amd/*.sbin at boot when present, so it tracks the host OS
image rather than the platform.
- Reported TCB, condensed to one line.
- Host CPU model and CPUID family/model/stepping. Generation-dependent
behaviour keys on family and model, both here and in the tooling this
harness drives, so a failure that turns on processor generation is
otherwise invisible in the result.
- snphost and snpguest versions as found on the host.
Detection is best-effort in the existing style: anything unavailable, or
unreadable because the harness is not running as root, yields None and is
simply omitted from the report.
The host and guest each carry their own snpguest, installed independently. When a guest-side attestation step fails there is currently nothing in the result identifying which build produced the failure, so the first question asked of such a report cannot be answered from it. Add get_guest_snpguest_version() next to the existing guest OS probe, and introduce update_environment_with_guest_info() to gather both on the first successful launch. The existing update_environment_with_guest_os() keeps its narrower meaning rather than quietly growing a second responsibility. Best-effort as before: no guest, no vsock agent, or no snpguest on the guest yields None and the line is omitted.
The snpguest build module resolves "latest" from the GitHub API at build time unless SNPGUEST_TAG is set, so two builds of identical source can install different tooling and nothing in the resulting image, or in the certification result it produces, says which one it got. Write the resolved tag to /usr/local/share/sev-certify/snpguest-tag during the build and report it alongside the detected versions. Absent on hosts not built by this project, in which case the line is simply omitted. This records the tag rather than pinning it; pinning is a policy decision left to SNPGUEST_TAG.
Two further facts that distinguish otherwise-identical hosts. Platform identifier, from snphost show identifier, so results can be tied to a specific machine rather than to a machine description. A one-line summary of any attestation report the run produced: its version, its CPUID family/model/stepping bytes, and whether CHIP_ID came back zeroed. These decide how a report is parsed rather than what it attests — consumers select a TCB layout from the processor generation implied by those CPUID bytes, and reject the report outright when they do not recognise it, before reading any of its contents. A result that fails there currently says only that it failed. CHIP_ID is included because MASK_CHIP_ID zeroes it and snphost show offers no way to read that setting back, so a zeroed CHIP_ID is the only externally visible sign that masking is in effect. The mask is set by snphost config set <bl> <tee> <snp> <ucode> <mask> and cleared by snphost config reset, which the config/commit test exercises directly. The report is located by mtime against the start of the run so one left over from an earlier run is not described as though this run produced it.
snpguest declines to write a report whose processor generation it cannot
resolve, so precisely when a run most needs the report it produces no
artifact at all: the one thing that would explain the failure is destroyed
by the failure.
Capture it a second way. The kernel's configfs-TSM interface is vendor
neutral and returns the raw report without interpreting it, so it yields
bytes in cases where the snpguest path yields nothing:
mkdir /sys/kernel/config/tsm/report/sev_verify
head -c 64 /dev/urandom > .../inblob
cat .../outblob
Both steps are typed "info", so a guest whose kernel lacks configfs-TSM
support costs nothing and the certification result is unaffected either way.
The pulled artifact is named tsm-report.bin and joins report.bin as a source
the environment summary will describe, whichever is newer.
Verified against a Rocky 10.2 guest (kernel 6.12): 1184 bytes, parsing as a
version 3 report. The two reports differ in their bytes because each carries
its own nonce and signature.
The reported SEV firmware version is the one currently in effect, but says
nothing about where it came from. Two versions are in play at different
times, and they govern different things: the BIOS-supplied firmware runs at
processor init and decides platform-level behaviour, while the ccp driver
may replace it at boot from /lib/firmware/amd/*.sbin, after which every
firmware command answers from the replacement.
Whether that replacement happens is a property of the host OS image. A
result that reports only the resulting version cannot be read correctly,
because the same version string can mean "this platform's own firmware" or
"whatever this distro happened to ship".
Record two things:
- The SEV firmware blobs available to the driver, identified by name and
digest. Their absence is stated explicitly rather than left as a missing
field, because absence is the informative case: it means no override
occurred and the platform is running BIOS-supplied firmware.
- The driver's own SEV lines from the kernel log, verbatim. These carry the
firmware build number that snphost show version omits and report whether
an update was applied at boot. They are quoted rather than parsed: the
wording varies across kernels, and a parser would silently produce
nothing on the versions it did not anticipate.
Digests are recorded because two builds of a blob share a filename and may
share an API version while differing in behaviour.
There was a problem hiding this comment.
Pull request overview
This PR expands sev_verify’s reporting so each certification result can include additional SEV/SNP platform facts (firmware state, CPU identity, platform identifier, tooling versions) that help distinguish otherwise-identical hosts and quickly correlate shared root causes across runs.
Changes:
- Extend markdown output to include additional environment fields (CPU, SEV firmware details, platform identifier, attestation report summary, snphost/snpguest versions, and guest snpguest version).
- Add host-side environment detection for SEV/SNP facts and a lightweight attestation report summarizer, plus logic to locate a report artifact produced during the run.
- Record the snpguest release tag at image-build time and (best-effort) query the guest’s snpguest version via vsock; add configfs-TSM report capture steps to
attestation-test.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
sev_verify/output.py |
Renders newly collected environment facts into the markdown environment block. |
sev_verify/os_info.py |
Adds best-effort guest snpguest --version collection and a combined guest info updater. |
sev_verify/environment.py |
Implements SEV/SNP environment probing, report summarization, and report artifact discovery. |
sev_verify/cli.py |
Integrates guest info enrichment and populates report_summary from newly produced artifacts. |
sev_verify/cert_tests/c3_0/c3_0_0_0/attestation_test.py |
Adds configfs-TSM report capture/pull as diagnostic info steps. |
modules/build/common/snpguest/mkosi.build |
Persists the installed snpguest release tag into the built image for later reporting. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Records, in every certification result, the facts that distinguish two hosts
which look identical by OS, kernel, QEMU and OVMF version but behave differently
under SNP.
New lines in the environment block:
Why
A worked example, which is what prompted this. Eight distributions failed
attestation-testwithprocessor is not of know SEV-SNP generation. Theresults read as eight independent distribution failures. They were one platform
bug: the BIOS supplies SEV firmware 1.55 build 38, which leaves
CPUID_FAM_ID/MOD_ID/STEPzeroed in version-3 reports, and no host imageinstalls
linux-firmware, so nothing overrides it. Build 39 populates thosefields. Establishing that took several days and three discarded hypotheses,
because none of the relevant facts appeared in any result.
With these changes the same sweep reports
blobs=none,build=38,no override, and
cpuid=0x00/0x00/0x00on every image — the shared cause isvisible at a glance, and the two genuine distribution findings (Debian 13 ships
no SEV OVMF; Debian forky's kernel and QEMU cannot register a
guest_memfdslot) separate cleanly from it.
This overlaps with #268 and #265 in intent, and is a step toward both rather
than a substitute for either.
Notes for review
snphost, notrunning as root, a host this project did not build — yields
Noneand theline is omitted. Nothing here can fail a run.
sev_firmware_sourcereports absence explicitly rather than omitting thefield, because absence is the informative case: it means no override occurred
and the platform is running its own firmware.
snphost show versionomits, but its wording varies across kernels, and aparser would silently yield nothing on versions it did not anticipate.
which adds two
infosteps toattestation-test. It exists becausesnpguest reportwill not write a report it cannot classify, so preciselywhen a run most needs the report there is no artifact at all. The kernel's
vendor-neutral configfs-TSM interface returns the raw bytes without
interpreting them. A guest whose kernel lacks support pays nothing. Filed
upstream as snpguest report discards a report it cannot classify by processor generation virtee/snpguest#158.
Testing
Verified on an EPYC 9654 (Genoa) across nine host images and both firmware
states — BIOS-supplied build 38 with no blobs present, and build 39 loaded from
linux-firmware— plus the absent-tool paths on a machine with no SEV at all.