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