From: David Woodhouse <[email protected]>

kvm_vcpu_ioctl_set_cpuid() runs kvm_vcpu_after_set_cpuid() on every
plane twin, but only the calling vCPU is loaded. The vendor callback
writes VMCS/VMCB controls, and on VMX those writes go to whatever VMCS
is loaded on the current pCPU: the caller's, not the twin's. The
twin's controls shadow is updated anyway, so the stale values in its
real VMCS can never be corrected later; a twin created before
KVM_SET_CPUID2 keeps its empty-CPUID controls (RDSEED/RDRAND exiting
among them), and the guest's higher VTL takes a spurious #UD on its
first RDSEED.

Swap each twin in as the loaded vCPU for its update, and reload the
caller once at the end. Plane ioctls all share the common vCPU mutex,
so the twin cannot be loaded elsewhere concurrently. A preemption
inside the window is safe only because vcpu_load() now publishes the
loaded twin as common->current_vcpu (previous patch); before that, the
preempt notifiers would arch-put and arch-load the caller instead.

Fixes the twin updates in "kvm: x86: Update state for all plane VCPUs
after CPUID update".

Fixes: https://lore.kernel.org/all/[email protected]/
Signed-off-by: David Woodhouse <[email protected]>
---
 arch/x86/kvm/cpuid.c | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 54bcfedc5226..c9b7dfc5c967 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -501,7 +501,7 @@ static int kvm_set_cpuid(struct kvm_vcpu *vcpu, struct 
kvm_cpuid_entry2 *e2,
 {
        struct kvm_vcpu_common *common = vcpu->common;
        u32 vcpu_caps[NR_KVM_CPU_CAPS];
-       struct kvm_vcpu *v;
+       struct kvm_vcpu *v, *loaded = vcpu;
        unsigned i;
        int r;
 
@@ -568,9 +568,24 @@ static int kvm_set_cpuid(struct kvm_vcpu *vcpu, struct 
kvm_cpuid_entry2 *e2,
 #ifdef CONFIG_KVM_XEN
                v->arch.xen.cpuid = kvm_get_hypervisor_cpuid(vcpu, 
XEN_SIGNATURE);
 #endif
+               /*
+                * The ioctl loaded the calling vCPU; updating a twin's
+                * VMCS/VMCB controls requires that twin to be the loaded
+                * one, so swap it in for the update.
+                */
+               if (v != loaded) {
+                       vcpu_put(loaded);
+                       vcpu_load(v);
+                       loaded = v;
+               }
                kvm_vcpu_after_set_cpuid(v);
        }
 
+       if (loaded != vcpu) {
+               vcpu_put(loaded);
+               vcpu_load(vcpu);
+       }
+
 success:
        kvfree(e2);
        return 0;
-- 
2.50.1


Reply via email to