Skip to content

Commit fc5b7f3

Browse files
dmatlackbonzini
authored andcommitted
kvm: x86: do not leak guest xcr0 into host interrupt handlers
An interrupt handler that uses the fpu can kill a KVM VM, if it runs under the following conditions: - the guest's xcr0 register is loaded on the cpu - the guest's fpu context is not loaded - the host is using eagerfpu Note that the guest's xcr0 register and fpu context are not loaded as part of the atomic world switch into "guest mode". They are loaded by KVM while the cpu is still in "host mode". Usage of the fpu in interrupt context is gated by irq_fpu_usable(). The interrupt handler will look something like this: if (irq_fpu_usable()) { kernel_fpu_begin(); [... code that uses the fpu ...] kernel_fpu_end(); } As long as the guest's fpu is not loaded and the host is using eager fpu, irq_fpu_usable() returns true (interrupted_kernel_fpu_idle() returns true). The interrupt handler proceeds to use the fpu with the guest's xcr0 live. kernel_fpu_begin() saves the current fpu context. If this uses XSAVE[OPT], it may leave the xsave area in an undesirable state. According to the SDM, during XSAVE bit i of XSTATE_BV is not modified if bit i is 0 in xcr0. So it's possible that XSTATE_BV[i] == 1 and xcr0[i] == 0 following an XSAVE. kernel_fpu_end() restores the fpu context. Now if any bit i in XSTATE_BV == 1 while xcr0[i] == 0, XRSTOR generates a #GP. The fault is trapped and SIGSEGV is delivered to the current process. Only pre-4.2 kernels appear to be vulnerable to this sequence of events. Commit 653f52c ("kvm,x86: load guest FPU context more eagerly") from 4.2 forces the guest's fpu to always be loaded on eagerfpu hosts. This patch fixes the bug by keeping the host's xcr0 loaded outside of the interrupts-disabled region where KVM switches into guest mode. Cc: stable@vger.kernel.org Suggested-by: Andy Lutomirski <luto@amacapital.net> Signed-off-by: David Matlack <dmatlack@google.com> [Move load after goto cancel_injection. - Paolo] Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent 7a98205 commit fc5b7f3

File tree

1 file changed

+4
-6
lines changed

1 file changed

+4
-6
lines changed

arch/x86/kvm/x86.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,6 @@ static int __kvm_set_xcr(struct kvm_vcpu *vcpu, u32 index, u64 xcr)
700700
if ((xcr0 & XFEATURE_MASK_AVX512) != XFEATURE_MASK_AVX512)
701701
return 1;
702702
}
703-
kvm_put_guest_xcr0(vcpu);
704703
vcpu->arch.xcr0 = xcr0;
705704

706705
if ((xcr0 ^ old_xcr0) & XFEATURE_MASK_EXTEND)
@@ -6590,8 +6589,6 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
65906589
kvm_x86_ops->prepare_guest_switch(vcpu);
65916590
if (vcpu->fpu_active)
65926591
kvm_load_guest_fpu(vcpu);
6593-
kvm_load_guest_xcr0(vcpu);
6594-
65956592
vcpu->mode = IN_GUEST_MODE;
65966593

65976594
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
@@ -6618,6 +6615,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
66186615
goto cancel_injection;
66196616
}
66206617

6618+
kvm_load_guest_xcr0(vcpu);
6619+
66216620
if (req_immediate_exit)
66226621
smp_send_reschedule(vcpu->cpu);
66236622

@@ -6667,6 +6666,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
66676666
vcpu->mode = OUTSIDE_GUEST_MODE;
66686667
smp_wmb();
66696668

6669+
kvm_put_guest_xcr0(vcpu);
6670+
66706671
/* Interrupt is enabled by handle_external_intr() */
66716672
kvm_x86_ops->handle_external_intr(vcpu);
66726673

@@ -7314,7 +7315,6 @@ void kvm_load_guest_fpu(struct kvm_vcpu *vcpu)
73147315
* and assume host would use all available bits.
73157316
* Guest xcr0 would be loaded later.
73167317
*/
7317-
kvm_put_guest_xcr0(vcpu);
73187318
vcpu->guest_fpu_loaded = 1;
73197319
__kernel_fpu_begin();
73207320
__copy_kernel_to_fpregs(&vcpu->arch.guest_fpu.state);
@@ -7323,8 +7323,6 @@ void kvm_load_guest_fpu(struct kvm_vcpu *vcpu)
73237323

73247324
void kvm_put_guest_fpu(struct kvm_vcpu *vcpu)
73257325
{
7326-
kvm_put_guest_xcr0(vcpu);
7327-
73287326
if (!vcpu->guest_fpu_loaded) {
73297327
vcpu->fpu_counter = 0;
73307328
return;

0 commit comments

Comments
 (0)