|
|
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) */
|