From 880bf81b173e53c53427a7b22ae577ecc0d48038 Mon Sep 17 00:00:00 2001
Message-Id: <880bf81b173e53c53427a7b22ae577ecc0d48038@dist-git>
From: Paolo Bonzini <pbonzini@redhat.com>
Date: Tue, 12 Dec 2017 16:23:41 +0100
Subject: [PATCH] qemu: capabilities: force update if the microcode version
 does not match

A microcode update can cause the CPUID bits to change; an example
from the past was the update that disabled TSX on several Haswell
and Broadwell machines.

Therefore, place microcode version in the virQEMUCaps struct and
XML, and rebuild the cache if the versions do not match.

CVE-2017-5715

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>

Conflicts:
	src/qemu/qemu_capabilities.c
	src/qemu/qemu_capabilities.h
            - QEMU capabilities cache was refactored since 7.3

	src/qemu/qemu_capspriv.h
            - context; more functions were added since 7.3

	tests/qemucapabilitiesdata/caps_2.7.0.x86_64.xml
            - context; different package version in 7.3

	tests/qemucapabilitiesdata/caps_2.10.0-gicv2.aarch64.xml
	tests/qemucapabilitiesdata/caps_2.10.0-gicv3.aarch64.xml
	tests/qemucapabilitiesdata/caps_2.10.0.ppc64.xml
	tests/qemucapabilitiesdata/caps_2.10.0.s390x.xml
	tests/qemucapabilitiesdata/caps_2.10.0.x86_64.xml
	tests/qemucapabilitiesdata/caps_2.7.0.s390x.xml
	tests/qemucapabilitiesdata/caps_2.8.0.s390x.xml
	tests/qemucapabilitiesdata/caps_2.8.0.x86_64.xml
	tests/qemucapabilitiesdata/caps_2.9.0.x86_64.xml
	tests/qemucapabilitiesdata/caps_2.9.0.ppc64.xml
	tests/qemucapabilitiesdata/caps_2.9.0.s390x.xml
            - missing in 7.3

	tests/qemucapabilitiestest.c
            - no explicit TCG caps probing for KVM binaries in 7.3 yet

	tests/qemucapsprobe.c
            - the caps parameter for virQEMUCapsNewForBinaryInternal
              added in 7.4 is missing in 7.3 (which matches 7.5 in this
              respect)

Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
---
 src/qemu/qemu_capabilities.c                       | 57 ++++++++++++++++++++--
 src/qemu/qemu_capspriv.h                           |  5 ++
 tests/qemucapabilitiesdata/caps_1.2.2.x86_64.xml   |  1 +
 tests/qemucapabilitiesdata/caps_1.3.1.x86_64.xml   |  1 +
 tests/qemucapabilitiesdata/caps_1.4.2.x86_64.xml   |  1 +
 tests/qemucapabilitiesdata/caps_1.5.3.x86_64.xml   |  1 +
 tests/qemucapabilitiesdata/caps_1.6.0.x86_64.xml   |  1 +
 tests/qemucapabilitiesdata/caps_1.7.0.x86_64.xml   |  1 +
 tests/qemucapabilitiesdata/caps_2.1.1.x86_64.xml   |  1 +
 tests/qemucapabilitiesdata/caps_2.4.0.x86_64.xml   |  1 +
 tests/qemucapabilitiesdata/caps_2.5.0.x86_64.xml   |  1 +
 .../caps_2.6.0-gicv2.aarch64.xml                   |  1 +
 .../caps_2.6.0-gicv3.aarch64.xml                   |  1 +
 tests/qemucapabilitiesdata/caps_2.6.0.ppc64le.xml  |  1 +
 tests/qemucapabilitiesdata/caps_2.6.0.x86_64.xml   |  1 +
 tests/qemucapabilitiesdata/caps_2.7.0.x86_64.xml   |  1 +
 tests/qemucapabilitiestest.c                       |  8 +++
 tests/qemucapsprobe.c                              |  2 +-
 18 files changed, 80 insertions(+), 6 deletions(-)

Index: libvirt-1.2.5/src/qemu/qemu_capabilities.c
===================================================================
--- libvirt-1.2.5.orig/src/qemu/qemu_capabilities.c
+++ libvirt-1.2.5/src/qemu/qemu_capabilities.c
@@ -278,6 +278,7 @@ struct _virQEMUCaps {
 
     unsigned int version;
     unsigned int kvmVersion;
+    unsigned int microcodeVersion;
 
     virArch arch;
 
@@ -297,6 +298,7 @@ struct _virQEMUCapsCache {
     char *cacheDir;
     uid_t runUid;
     gid_t runGid;
+    unsigned int microcodeVersion;
 };
 
 
@@ -1839,6 +1841,7 @@ virQEMUCapsPtr virQEMUCapsNewCopy(virQEM
     ret->usedQMP = qemuCaps->usedQMP;
     ret->version = qemuCaps->version;
     ret->kvmVersion = qemuCaps->kvmVersion;
+    ret->microcodeVersion = qemuCaps->microcodeVersion;
     ret->arch = qemuCaps->arch;
 
     if (VIR_ALLOC_N(ret->cpuDefinitions, qemuCaps->ncpuDefinitions) < 0)
@@ -2537,6 +2540,13 @@ virQEMUCapsLoadCache(virQEMUCapsPtr qemu
         goto cleanup;
     }
 
+    if (virXPathUInt("string(./microcodeVersion)", ctxt,
+                     &qemuCaps->microcodeVersion) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("missing microcode version in QEMU capabilities cache"));
+        goto cleanup;
+    }
+
     if (!(str = virXPathString("string(./arch)", ctxt))) {
         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                        _("missing arch in QEMU capabilities cache"));
@@ -2648,6 +2658,9 @@ virQEMUCapsSaveCache(virQEMUCapsPtr qemu
     virBufferAsprintf(&buf, "<kvmVersion>%d</kvmVersion>\n",
                       qemuCaps->kvmVersion);
 
+    virBufferAsprintf(&buf, "<microcodeVersion>%u</microcodeVersion>\n",
+                      qemuCaps->microcodeVersion);
+
     virBufferAsprintf(&buf, "<arch>%s</arch>\n",
                       virArchToString(qemuCaps->arch));
 
@@ -2759,7 +2772,8 @@ virQEMUCapsReset(virQEMUCapsPtr qemuCaps
 
 static int
 virQEMUCapsInitCached(virQEMUCapsPtr qemuCaps,
-                      const char *cacheDir)
+                      const char *cacheDir,
+                      unsigned int microcodeVersion)
 {
     char *capsdir = NULL;
     char *capsfile = NULL;
@@ -2811,6 +2825,20 @@ virQEMUCapsInitCached(virQEMUCapsPtr qem
         goto cleanup;
     }
 
+    if ((virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM) ||
+         virQEMUCapsGet(qemuCaps, QEMU_CAPS_ENABLE_KVM)) &&
+        microcodeVersion != qemuCaps->microcodeVersion) {
+        VIR_DEBUG("Outdated capabilities for '%s': microcode version changed "
+                  "(%u vs %u)",
+                  qemuCaps->binary,
+                  microcodeVersion,
+                  qemuCaps->microcodeVersion);
+        ignore_value(unlink(capsfile));
+        virQEMUCapsReset(qemuCaps);
+        ret = 0;
+        goto cleanup;
+    }
+
     /* Discard if cache is older that QEMU binary */
     if (qemuctime != qemuCaps->ctime ||
         selfctime < virGetSelfLastChanged()) {
@@ -3293,7 +3321,8 @@ virQEMUCapsPtr virQEMUCapsNewForBinary(c
                                        const char *libDir,
                                        const char *cacheDir,
                                        uid_t runUid,
-                                       gid_t runGid)
+                                       gid_t runGid,
+                                       unsigned int microcodeVersion)
 {
     virQEMUCapsPtr qemuCaps = virQEMUCapsNew();
     struct stat sb;
@@ -3321,7 +3350,7 @@ virQEMUCapsPtr virQEMUCapsNewForBinary(c
         goto error;
     }
 
-    if ((rv = virQEMUCapsInitCached(qemuCaps, cacheDir)) < 0)
+    if ((rv = virQEMUCapsInitCached(qemuCaps, cacheDir, microcodeVersion)) < 0)
         goto error;
 
     if (rv == 0) {
@@ -3336,6 +3365,10 @@ virQEMUCapsPtr virQEMUCapsNewForBinary(c
             goto error;
         }
 
+        if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM) ||
+            virQEMUCapsGet(qemuCaps, QEMU_CAPS_ENABLE_KVM))
+            qemuCaps->microcodeVersion = microcodeVersion;
+
         if (virQEMUCapsRememberCached(qemuCaps, cacheDir) < 0)
             goto error;
     }
@@ -3371,6 +3404,9 @@ virQEMUCapsCacheNew(const char *libDir,
 {
     virQEMUCapsCachePtr cache;
 
+    if (virQEMUCapsInitialize() < 0)
+        return NULL;
+
     if (VIR_ALLOC(cache) < 0)
         return NULL;
 
@@ -3390,6 +3426,7 @@ virQEMUCapsCacheNew(const char *libDir,
 
     cache->runUid = runUid;
     cache->runGid = runGid;
+    cache->microcodeVersion = cpuMicrocodeVersion;
 
     return cache;
 
@@ -3417,7 +3454,8 @@ virQEMUCapsCacheLookup(virQEMUCapsCacheP
                   binary);
         ret = virQEMUCapsNewForBinary(binary, cache->libDir,
                                       cache->cacheDir,
-                                      cache->runUid, cache->runGid);
+                                      cache->runUid, cache->runGid,
+                                      cache->microcodeVersion);
         if (ret) {
             VIR_DEBUG("Caching capabilities %p for %s",
                       ret, binary);
@@ -3493,3 +3531,11 @@ virQEMUCapsSupportsChardev(virDomainDefP
             (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE &&
              chr->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_VIRTIO));
 }
+
+
+void
+virQEMUCapsSetMicrocodeVersion(virQEMUCapsPtr qemuCaps,
+                               unsigned int microcodeVersion)
+{
+    qemuCaps->microcodeVersion = microcodeVersion;
+}
Index: libvirt-1.2.5/tests/qemucapabilitiestest.c
===================================================================
--- libvirt-1.2.5.orig/tests/qemucapabilitiestest.c
+++ libvirt-1.2.5/tests/qemucapabilitiestest.c
@@ -193,6 +193,14 @@ testQemuCaps(const void *opaque)
                                   qemuMonitorTestGetMonitor(mon)) < 0)
         goto cleanup;
 
+    if (virQEMUCapsGet(capsComputed, QEMU_CAPS_KVM) ||
+        virQEMUCapsGet(capsComputed, QEMU_CAPS_ENABLE_KVM)) {
+        /* Fill microcodeVersion with a "random" value which is the file
+         * length to provide a reproducible number for testing.
+         */
+        virQEMUCapsSetMicrocodeVersion(capsComputed, virFileLength(repliesFile, -1));
+    }
+
     /* So that our test does not depend on the contents of /proc, we
      * hoisted the setting of ENABLE_FIPS to virQEMUCapsInitQMP.  But
      * we do want to test the effect of that flag.  */
Index: libvirt-1.2.5/src/qemu/qemu_capabilities.h
===================================================================
--- libvirt-1.2.5.orig/src/qemu/qemu_capabilities.h
+++ libvirt-1.2.5/src/qemu/qemu_capabilities.h
@@ -222,7 +222,8 @@ virQEMUCapsPtr virQEMUCapsNewForBinary(c
                                        const char *libDir,
                                        const char *cacheDir,
                                        uid_t runUid,
-                                       gid_t runGid);
+                                       gid_t runGid,
+                                       unsigned int microcodeVersion);
 
 int virQEMUCapsInitQMPMonitor(virQEMUCapsPtr qemuCaps,
                               qemuMonitorPtr mon);
