39e2564
x86emul: honor guest CR0.TS and CR0.EM
39e2564
39e2564
We must not emulate any instructions accessing respective registers
39e2564
when either of these flags is set in the guest view of the register, or
39e2564
else we may do so on data not belonging to the guest's current task.
39e2564
39e2564
Being architecturally required behavior, the logic gets placed in the
39e2564
instruction emulator instead of hvmemul_get_fpu(). It should be noted,
39e2564
though, that hvmemul_get_fpu() being the only current handler for the
39e2564
get_fpu() callback, we don't have an active problem with CR4: Both
39e2564
CR4.OSFXSR and CR4.OSXSAVE get handled as necessary by that function.
39e2564
39e2564
This is XSA-190.
39e2564
39e2564
Signed-off-by: Jan Beulich <jbeulich@suse.com>
39e2564
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
39e2564
---
39e2564
v4: Only raise #NM on FWAIT when CR0.TS and CR0.MP are set.
39e2564
v3: Correct which exception to raise upon set CR0.EM.
39e2564
v2: Require the read_cr hook to be set, which then requires a change to
39e2564
    the test code too.
39e2564
---
39e2564
The change to xen/arch/x86/hvm/emulate.c isn't strictly needed for
39e2564
fixing the security issue, but the patch would be rather incomplete
39e2564
without.
39e2564
39e2564
--- a/tools/tests/x86_emulator/test_x86_emulator.c
39e2564
+++ b/tools/tests/x86_emulator/test_x86_emulator.c
39e2564
@@ -158,6 +158,22 @@ static inline uint64_t xgetbv(uint32_t x
39e2564
     (ebx & (1U << 5)) != 0; \
39e2564
 })
39e2564
 
39e2564
+static int read_cr(
39e2564
+    unsigned int reg,
39e2564
+    unsigned long *val,
39e2564
+    struct x86_emulate_ctxt *ctxt)
39e2564
+{
39e2564
+    /* Fake just enough state for the emulator's _get_fpu() to be happy. */
39e2564
+    switch ( reg )
39e2564
+    {
39e2564
+    case 0:
39e2564
+        *val = 0x00000001; /* PE */
39e2564
+        return X86EMUL_OKAY;
39e2564
+    }
39e2564
+
39e2564
+    return X86EMUL_UNHANDLEABLE;
39e2564
+}
39e2564
+
39e2564
 int get_fpu(
39e2564
     void (*exception_callback)(void *, struct cpu_user_regs *),
39e2564
     void *exception_callback_arg,
39e2564
@@ -189,6 +205,7 @@ static struct x86_emulate_ops emulops =
39e2564
     .write      = write,
39e2564
     .cmpxchg    = cmpxchg,
39e2564
     .cpuid      = cpuid,
39e2564
+    .read_cr    = read_cr,
39e2564
     .get_fpu    = get_fpu,
39e2564
 };
39e2564
 
39e2564
--- a/xen/arch/x86/hvm/emulate.c
39e2564
+++ b/xen/arch/x86/hvm/emulate.c
39e2564
@@ -1628,14 +1628,14 @@ static int hvmemul_get_fpu(
39e2564
     switch ( type )
39e2564
     {
39e2564
     case X86EMUL_FPU_fpu:
39e2564
+    case X86EMUL_FPU_wait:
39e2564
         break;
39e2564
     case X86EMUL_FPU_mmx:
39e2564
         if ( !cpu_has_mmx )
39e2564
             return X86EMUL_UNHANDLEABLE;
39e2564
         break;
39e2564
     case X86EMUL_FPU_xmm:
39e2564
-        if ( (curr->arch.hvm_vcpu.guest_cr[0] & X86_CR0_EM) ||
39e2564
-             !(curr->arch.hvm_vcpu.guest_cr[4] & X86_CR4_OSFXSR) )
39e2564
+        if ( !(curr->arch.hvm_vcpu.guest_cr[4] & X86_CR4_OSFXSR) )
39e2564
             return X86EMUL_UNHANDLEABLE;
39e2564
         break;
39e2564
     case X86EMUL_FPU_ymm:
39e2564
--- a/xen/arch/x86/x86_emulate/x86_emulate.c
39e2564
+++ b/xen/arch/x86/x86_emulate/x86_emulate.c
39e2564
@@ -420,6 +420,9 @@ typedef union {
39e2564
 
39e2564
 /* Control register flags. */
39e2564
 #define CR0_PE    (1<<0)
39e2564
+#define CR0_MP    (1<<1)
39e2564
+#define CR0_EM    (1<<2)
39e2564
+#define CR0_TS    (1<<3)
39e2564
 #define CR4_TSD   (1<<2)
39e2564
 
39e2564
 /* EFLAGS bit definitions. */
39e2564
@@ -447,6 +450,7 @@ typedef union {
39e2564
 #define EXC_OF  4
39e2564
 #define EXC_BR  5
39e2564
 #define EXC_UD  6
39e2564
+#define EXC_NM  7
39e2564
 #define EXC_TS 10
39e2564
 #define EXC_NP 11
39e2564
 #define EXC_SS 12
39e2564
@@ -746,10 +750,45 @@ static void fpu_handle_exception(void *_
39e2564
     regs->eip += fic->insn_bytes;
39e2564
 }
39e2564
 
39e2564
+static int _get_fpu(
39e2564
+    enum x86_emulate_fpu_type type,
39e2564
+    struct fpu_insn_ctxt *fic,
39e2564
+    struct x86_emulate_ctxt *ctxt,
39e2564
+    const struct x86_emulate_ops *ops)
39e2564
+{
39e2564
+    int rc;
39e2564
+
39e2564
+    fic->exn_raised = 0;
39e2564
+
39e2564
+    fail_if(!ops->get_fpu);
39e2564
+    rc = ops->get_fpu(fpu_handle_exception, fic, type, ctxt);
39e2564
+
39e2564
+    if ( rc == X86EMUL_OKAY )
39e2564
+    {
39e2564
+        unsigned long cr0;
39e2564
+
39e2564
+        fail_if(!ops->read_cr);
39e2564
+        rc = ops->read_cr(0, &cr0, ctxt);
39e2564
+        if ( rc != X86EMUL_OKAY )
39e2564
+            return rc;
39e2564
+        if ( cr0 & CR0_EM )
39e2564
+        {
39e2564
+            generate_exception_if(type == X86EMUL_FPU_fpu, EXC_NM, -1);
39e2564
+            generate_exception_if(type == X86EMUL_FPU_mmx, EXC_UD, -1);
39e2564
+            generate_exception_if(type == X86EMUL_FPU_xmm, EXC_UD, -1);
39e2564
+        }
39e2564
+        generate_exception_if((cr0 & CR0_TS) &&
39e2564
+                              (type != X86EMUL_FPU_wait || (cr0 & CR0_MP)),
39e2564
+                              EXC_NM, -1);
39e2564
+    }
39e2564
+
39e2564
+ done:
39e2564
+    return rc;
39e2564
+}
39e2564
+
39e2564
 #define get_fpu(_type, _fic)                                    \
39e2564
-do{ (_fic)->exn_raised = 0;                                     \
39e2564
-    fail_if(ops->get_fpu == NULL);                              \
39e2564
-    rc = ops->get_fpu(fpu_handle_exception, _fic, _type, ctxt); \
39e2564
+do {                                                            \
39e2564
+    rc = _get_fpu(_type, _fic, ctxt, ops);                      \
39e2564
     if ( rc ) goto done;                                        \
39e2564
 } while (0)
39e2564
 #define _put_fpu()                                              \
39e2564
@@ -2879,8 +2918,14 @@ x86_emulate(
39e2564
     }
39e2564
 
39e2564
     case 0x9b:  /* wait/fwait */
39e2564
-        emulate_fpu_insn("fwait");
39e2564
+    {
39e2564
+        struct fpu_insn_ctxt fic = { .insn_bytes = 1 };
39e2564
+
39e2564
+        get_fpu(X86EMUL_FPU_wait, &fic;;
39e2564
+        asm volatile ( "fwait" ::: "memory" );
39e2564
+        put_fpu(&fic;;
39e2564
         break;
39e2564
+    }
39e2564
 
39e2564
     case 0x9c: /* pushf */
39e2564
         src.val = _regs.eflags;
39e2564
--- a/xen/arch/x86/x86_emulate/x86_emulate.h
39e2564
+++ b/xen/arch/x86/x86_emulate/x86_emulate.h
39e2564
@@ -115,6 +115,7 @@ struct __packed segment_register {
39e2564
 /* FPU sub-types which may be requested via ->get_fpu(). */
39e2564
 enum x86_emulate_fpu_type {
39e2564
     X86EMUL_FPU_fpu, /* Standard FPU coprocessor instruction set */
39e2564
+    X86EMUL_FPU_wait, /* WAIT/FWAIT instruction */
39e2564
     X86EMUL_FPU_mmx, /* MMX instruction set (%mm0-%mm7) */
39e2564
     X86EMUL_FPU_xmm, /* SSE instruction set (%xmm0-%xmm7/15) */
39e2564
     X86EMUL_FPU_ymm  /* AVX/XOP instruction set (%ymm0-%ymm7/15) */