]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
confidential-virt: treat an unreadable SEV MSR as confidential
authorPaul Meyer <katexochen0@gmail.com>
Wed, 17 Jun 2026 14:13:35 +0000 (16:13 +0200)
committerPaul Meyer <katexochen0@gmail.com>
Tue, 23 Jun 2026 09:55:00 +0000 (11:55 +0200)
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>
src/basic/confidential-virt.c

index 29d598a5988d8c92b370378c443551b3ea702ce3..3f8cb2a3cd81fbc82c32c15ae0531beaa0fed756 100644 (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;
 
-        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;
-        }
+        assert(ret);
 
-        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;
-        }
-
-        log_debug("MSR %" PRIu64 " result %" PRIu64 "", index, ret);
-        return ret;
+        fd = open(MSR_DEVICE, O_RDONLY|O_CLOEXEC);
+        if (fd < 0)
+                return log_debug_errno(errno,
+                                       "Cannot open MSR device %s (index %" PRIu64 "): %m",
+                                       MSR_DEVICE, index);
+
+        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, 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 */