Commit 623347ab1c9885c235f90a5b9b75f8d4494b76c1

Authored by James Hogan
Committed by Greg Kroah-Hartman
1 parent 645177f349

KVM: MIPS: Don't leak FPU/DSP to guest

commit f798217dfd038af981a18bbe4bc57027a08bb182 upstream.

The FPU and DSP are enabled via the CP0 Status CU1 and MX bits by
kvm_mips_set_c0_status() on a guest exit, presumably in case there is
active state that needs saving if pre-emption occurs. However neither of
these bits are cleared again when returning to the guest.

This effectively gives the guest access to the FPU/DSP hardware after
the first guest exit even though it is not aware of its presence,
allowing FP instructions in guest user code to intermittently actually
execute instead of trapping into the guest OS for emulation. It will
then read & manipulate the hardware FP registers which technically
belong to the user process (e.g. QEMU), or are stale from another user
process. It can also crash the guest OS by causing an FP exception, for
which a guest exception handler won't have been registered.

First lets save and disable the FPU (and MSA) state with lose_fpu(1)
before entering the guest. This simplifies the problem, especially for
when guest FPU/MSA support is added in the future, and prevents FR=1 FPU
state being live when the FR bit gets cleared for the guest, which
according to the architecture causes the contents of the FPU and vector
registers to become UNPREDICTABLE.

We can then safely remove the enabling of the FPU in
kvm_mips_set_c0_status(), since there should never be any active FPU or
MSA state to save at pre-emption, which should plug the FPU leak.

DSP state is always live rather than being lazily restored, so for that
it is simpler to just clear the MX bit again when re-entering the guest.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Sanjay Lal <sanjayl@kymasys.com>
Cc: Gleb Natapov <gleb@kernel.org>
Cc: kvm@vger.kernel.org
Cc: linux-mips@linux-mips.org
Cc: <stable@vger.kernel.org> # v3.10+: 044f0f03eca0: MIPS: KVM: Deliver guest interrupts
Cc: <stable@vger.kernel.org> # v3.10+
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Showing 2 changed files with 4 additions and 4 deletions Side-by-side Diff

arch/mips/kvm/kvm_locore.S
... ... @@ -428,7 +428,7 @@
428 428 /* Setup status register for running guest in UM */
429 429 .set at
430 430 or v1, v1, (ST0_EXL | KSU_USER | ST0_IE)
431   - and v1, v1, ~ST0_CU0
  431 + and v1, v1, ~(ST0_CU0 | ST0_MX)
432 432 .set noat
433 433 mtc0 v1, CP0_STATUS
434 434 ehb
arch/mips/kvm/kvm_mips.c
... ... @@ -15,6 +15,7 @@
15 15 #include <linux/vmalloc.h>
16 16 #include <linux/fs.h>
17 17 #include <linux/bootmem.h>
  18 +#include <asm/fpu.h>
18 19 #include <asm/page.h>
19 20 #include <asm/cacheflush.h>
20 21 #include <asm/mmu_context.h>
... ... @@ -418,6 +419,8 @@
418 419 vcpu->mmio_needed = 0;
419 420 }
420 421  
  422 + lose_fpu(1);
  423 +
421 424 local_irq_disable();
422 425 /* Check if we have any exceptions/interrupts pending */
423 426 kvm_mips_deliver_interrupts(vcpu,
... ... @@ -1020,9 +1023,6 @@
1020 1023 void kvm_mips_set_c0_status(void)
1021 1024 {
1022 1025 uint32_t status = read_c0_status();
1023   -
1024   - if (cpu_has_fpu)
1025   - status |= (ST0_CU1);
1026 1026  
1027 1027 if (cpu_has_dsp)
1028 1028 status |= (ST0_MX);