From: Hao Zhang AMD specifies that SYSEXIT raises #UD in both 64-bit and compatibility modes. On AMD hosts, KVM intercepts the hardware #UD, but SYSEXIT is marked EmulateOnUD and em_sysexit() applies Intel semantics without checking the guest CPU vendor. For an AMD-compatible guest at CPL0, this can incorrectly complete the transition to CPL3 or inject #GP, depending on the SYSENTER_CS value and other operands. At nonzero CPL, the generic Priv check injects #GP instead of the required #UD. Inject #UD for AMD-compatible vCPUs when EFER.LMA is set, before checking CPL or SYSENTER_CS and before modifying architectural state. Move the CPL check into em_sysexit() and remove the opcode's Priv flag so that the generic privilege check does not take precedence over AMD's long-mode #UD. Retain the existing #GP behavior for real mode, virtual-8086 mode, and nonzero CPL outside the AMD long-mode case. See the SYSEXIT instruction description in AMD APM Volume 3. Fixes: 4668f0507870 ("KVM: x86 emulator: Add sysexit emulation") Signed-off-by: Hao Zhang --- arch/x86/kvm/emulate.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index c1b21282187f..2154a2f2fd92 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -2457,12 +2457,18 @@ static int em_sysexit(struct x86_emulate_ctxt *ctxt) const struct x86_emulate_ops *ops = ctxt->ops; struct desc_struct cs, ss; u64 msr_data, rcx, rdx; + u64 efer = 0; int usermode; u16 cs_sel = 0, ss_sel = 0; - /* inject #GP if in real mode or Virtual 8086 mode */ + /* AMD rejects SYSEXIT in both IA-32e submodes, even at CPL3. */ + ops->get_msr(ctxt, MSR_EFER, &efer); + if ((efer & EFER_LMA) && !ops->guest_cpuid_is_intel_compatible(ctxt)) + return emulate_ud(ctxt); + + /* Check CPL here so that AMD's long-mode #UD takes precedence. */ if (ctxt->mode == X86EMUL_MODE_REAL || - ctxt->mode == X86EMUL_MODE_VM86) + ctxt->mode == X86EMUL_MODE_VM86 || ops->cpl(ctxt)) return emulate_gp(ctxt, 0); setup_syscalls_segments(&cs, &ss); @@ -4390,7 +4396,7 @@ static const struct opcode twobyte_table[256] = { II(ImplicitOps | Priv, em_rdmsr, rdmsr), IIP(ImplicitOps, em_rdpmc, rdpmc, check_rdpmc), I(ImplicitOps | EmulateOnUD | IsBranch | ShadowStack, em_sysenter), - I(ImplicitOps | Priv | EmulateOnUD | IsBranch | ShadowStack, em_sysexit), + I(ImplicitOps | EmulateOnUD | IsBranch | ShadowStack, em_sysexit), N, N, N, N, N, N, N, N, N, N, /* 0x40 - 0x4F */ base-commit: 6c377d19d4a5116d9bec5203aa3c6c11523e7898 -- 2.15.0