Skip to content

fix(tee): bare-metal SEV-SNP via configfs-TSM; accept report version >=2#412

Merged
imran-siddique merged 2 commits into
mainfrom
fix/snp-configfs-tsm-collector
Jul 21, 2026
Merged

fix(tee): bare-metal SEV-SNP via configfs-TSM; accept report version >=2#412
imran-siddique merged 2 commits into
mainfrom
fix/snp-configfs-tsm-collector

Conversation

@imran-siddique

Copy link
Copy Markdown
Contributor

Two bugs in the bare-metal (non-paravisor) SEV-SNP path, both caught on real hardware — a GCP N2D SEV-SNP VM (AMD Milan, genuine non-paravisor SNP: dmesg shows AMD SEV SEV-ES SEV-SNP, guest controls REPORT_DATA).

Bug 1 — collector used a broken /dev/sev-guest ioctl

SEVSNPProvider.get_attestation_report issued ioctl 0xC0A01181 with an inline request struct. The real kernel ABI is SNP_GET_REPORT = _IOWR('S', 0x0, struct snp_guest_request_ioctl) with pointers to separate request/response structs, so the call failed on real hardware with OSError [Errno 25] Inappropriate ioctl for device (ENOTTY).

Fix: obtain the report via the kernel configfs-TSM interface (/sys/kernel/config/tsm/report, Linux 6.7+) — the same interface agent-manifest uses. Validated live: the guest-supplied nonce lands in REPORT_DATA and the report verifies against the AMD VCEK (verified=True, fields measurement, report_data, vcek_cert_chain, report_signature).

Bug 2 — verifier rejected report version 5

cmcp_verify.sev_snp accepted only report versions (2, 3); real Milan hardware emits version 5, so genuine reports were rejected as invalid_snp_report_version. The fields read (report_data 0x50, measurement 0x90, reported_tcb 0x180, chip_id 0x1a0, signature 0x2a0) are layout-stable v2..v5, and the VCEK signature is the real gate. Fix: accept version >= 2.

Tests

Updated to the configfs-TSM model (mock _tsm_get_report instead of fcntl; version-5 accepted, version-1 rejected). ruff + mypy src/cmcp_runtime/ clean; 103 attestation/verify tests pass.

Note: independent of the distinct-azure-cvm-sev-snp-platform change (#411) — different files.

🤖 Generated with Claude Code

Hardware validation on a real non-paravisor SEV-SNP guest (GCP N2D, AMD Milan)
surfaced two bugs in the bare-metal path:

1. SEVSNPProvider used a /dev/sev-guest ioctl whose number (0xC0A01181) and
   inline request ABI were wrong; it failed with ENOTTY on real hardware.
   Rewritten to use the kernel configfs-TSM interface (/sys/kernel/config/tsm/
   report), the same interface agent-manifest uses. Validated: the guest nonce
   lands in REPORT_DATA and the report verifies against the AMD VCEK.
2. cmcp_verify.sev_snp rejected any report version outside (2, 3); real Milan
   hardware emits version 5. Accept version >=2 (field offsets are stable
   across v2..v5; the VCEK signature is the real gate).

Tests updated to the configfs-TSM model (mock _tsm_get_report; version-5
accepted, version-1 rejected). ruff + mypy (cmcp_runtime) clean; 103 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The configfs-TSM report directory exists whenever tsm.ko is loaded (including
on ordinary CI runners) with no provider registered, so detecting on the
directory wrongly auto-selected SEVSNPProvider and aborted gateway startup
with a configfs PermissionError. Gate on /dev/sev-guest (created by the
sev-guest driver on a real SNP guest); acquisition still uses configfs-TSM.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@imran-siddique
imran-siddique merged commit e4604d9 into main Jul 21, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant