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
