confidential-virt: treat an unreadable SEV MSR as confidential

msr() returned 0 on failure, indistinguishable from a real MSR value of
0. With /dev/cpu/0/msr unavailable (e.g. the msr module not loaded in the
initrd), detect_sev() read 0 and reported a genuine SEV/-ES/-SNP guest as
CONFIDENTIAL_VIRTUALIZATION_NONE.

That inverts the firmware-credential trust gate: import_credentials_*()
skip fw_cfg/SMBIOS credentials only when detect_confidential_virtualization()
is > 0 ("don't trust firmware in confidential VMs"). A false NONE makes a
confidential guest trust and import credentials injected by the untrusted
hypervisor.

msr() now returns a negative errno, and detect_sev() assumes plain SEV when
the MSR is unreadable but CPUID already advertised SEV under a hypervisor,
so the gate still trips.

The conservative branch only fires when CPUID already advertised SEV, i.e.
for a guest the hypervisor marked SEV-capable. QEMU gates that CPUID leaf on
the SEV launch object and does not expose it to ordinary guests even under
-cpu host, so it does not misfire for non-confidential guests. Were a
hypervisor to expose the bit anyway the outcome is fail-safe (we only
decline to trust firmware-supplied data); nothing in-tree branches on the
specific SEV tier.

Signed-off-by: Paul Meyer <katexochen0@gmail.com>
This commit is contained in:
Paul Meyer
2026-06-23 11:55:00 +02:00
parent 78a5271245
commit d76ce200a6
+31 -27
View File
@@ -43,37 +43,32 @@ static uint32_t cpuid_leaf(uint32_t eax, char ret_sig[static 13], bool swapped)
#define MSR_DEVICE "/dev/cpu/0/msr"
static uint64_t msr(uint64_t index) {
uint64_t ret;
ssize_t rv;
static int msr(uint64_t index, uint64_t *ret) {
_cleanup_close_ int fd = -EBADF;
uint64_t v;
ssize_t n;
assert(ret);
fd = open(MSR_DEVICE, O_RDONLY|O_CLOEXEC);
if (fd < 0) {
log_debug_errno(errno,
"Cannot open MSR device %s (index %" PRIu64 "), ignoring: %m",
MSR_DEVICE,
index);
return 0;
}
if (fd < 0)
return log_debug_errno(errno,
"Cannot open MSR device %s (index %" PRIu64 "): %m",
MSR_DEVICE, index);
rv = pread(fd, &ret, sizeof(ret), index);
if (rv < 0) {
log_debug_errno(errno,
"Cannot read MSR device %s (index %" PRIu64 "), ignoring: %m",
MSR_DEVICE,
index);
return 0;
} else if (rv != sizeof(ret)) {
log_debug("Short read %zd bytes from MSR device %s (index %" PRIu64 "), ignoring",
rv,
MSR_DEVICE,
index);
return 0;
}
n = pread(fd, &v, sizeof(v), index);
if (n < 0)
return log_debug_errno(errno,
"Cannot read MSR device %s (index %" PRIu64 "): %m",
MSR_DEVICE, index);
if (n != sizeof(v))
return log_debug_errno(SYNTHETIC_ERRNO(EIO),
"Short read %zd bytes from MSR device %s (index %" PRIu64 ")",
n, MSR_DEVICE, index);
log_debug("MSR %" PRIu64 " result %" PRIu64 "", index, ret);
return ret;
log_debug("MSR %" PRIu64 " result %" PRIu64, index, v);
*ret = v;
return 0;
}
static bool detect_hyperv_cvm(uint32_t isoltype) {
@@ -110,6 +105,7 @@ static bool detect_hyperv_cvm(uint32_t isoltype) {
static ConfidentialVirtualization detect_sev(void) {
uint32_t eax, ebx, ecx, edx;
uint64_t msrval;
int r;
eax = CPUID_GET_HIGHEST_FUNCTION;
ebx = ecx = edx = 0;
@@ -140,7 +136,15 @@ static ConfidentialVirtualization detect_sev(void) {
return CONFIDENTIAL_VIRTUALIZATION_NONE;
}
msrval = msr(MSR_AMD64_SEV);
r = msr(MSR_AMD64_SEV, &msrval);
if (r < 0) {
/* The CPU advertises SEV support and we're running under a hypervisor, but we couldn't read
* the SEV MSR to determine the exact mode (e.g. /dev/cpu/0/msr is unavailable because the
* msr module isn't loaded). Assume plain SEV. Misreporting a genuine confidential guest as
* non-confidential would wrongly make us trust hypervisor-provided data such as firmware credentials. */
log_debug_errno(r, "Failed to read SEV MSR, assuming SEV: %m");
return CONFIDENTIAL_VIRTUALIZATION_SEV;
}
/* Test reverse order, since the SEV-SNP bit implies
* the SEV-ES bit, which implies the SEV bit */