Commit 82268083 authored by Nadav Amit's avatar Nadav Amit Committed by Paolo Bonzini

KVM: x86: Emulation of call may use incorrect stack size

On long-mode, when far call that changes cs.l takes place, the stack size is
determined by the new mode.  For instance, if we go from 32-bit mode to 64-bit
mode, the stack-size if 64.  KVM uses the old stack size.

Fix it.
Signed-off-by: default avatarNadav Amit <namit@cs.technion.ac.il>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent bac15531
...@@ -741,19 +741,26 @@ static int assign_eip_far(struct x86_emulate_ctxt *ctxt, ulong dst, ...@@ -741,19 +741,26 @@ static int assign_eip_far(struct x86_emulate_ctxt *ctxt, ulong dst,
const struct desc_struct *cs_desc) const struct desc_struct *cs_desc)
{ {
enum x86emul_mode mode = ctxt->mode; enum x86emul_mode mode = ctxt->mode;
int rc;
#ifdef CONFIG_X86_64 #ifdef CONFIG_X86_64
if (ctxt->mode >= X86EMUL_MODE_PROT32 && cs_desc->l) { if (ctxt->mode >= X86EMUL_MODE_PROT16) {
u64 efer = 0; if (cs_desc->l) {
u64 efer = 0;
ctxt->ops->get_msr(ctxt, MSR_EFER, &efer); ctxt->ops->get_msr(ctxt, MSR_EFER, &efer);
if (efer & EFER_LMA) if (efer & EFER_LMA)
mode = X86EMUL_MODE_PROT64; mode = X86EMUL_MODE_PROT64;
} else
mode = X86EMUL_MODE_PROT32; /* temporary value */
} }
#endif #endif
if (mode == X86EMUL_MODE_PROT16 || mode == X86EMUL_MODE_PROT32) if (mode == X86EMUL_MODE_PROT16 || mode == X86EMUL_MODE_PROT32)
mode = cs_desc->d ? X86EMUL_MODE_PROT32 : X86EMUL_MODE_PROT16; mode = cs_desc->d ? X86EMUL_MODE_PROT32 : X86EMUL_MODE_PROT16;
return assign_eip(ctxt, dst, mode); rc = assign_eip(ctxt, dst, mode);
if (rc == X86EMUL_CONTINUE)
ctxt->mode = mode;
return rc;
} }
static inline int jmp_rel(struct x86_emulate_ctxt *ctxt, int rel) static inline int jmp_rel(struct x86_emulate_ctxt *ctxt, int rel)
...@@ -3062,6 +3069,7 @@ static int em_call_far(struct x86_emulate_ctxt *ctxt) ...@@ -3062,6 +3069,7 @@ static int em_call_far(struct x86_emulate_ctxt *ctxt)
struct desc_struct old_desc, new_desc; struct desc_struct old_desc, new_desc;
const struct x86_emulate_ops *ops = ctxt->ops; const struct x86_emulate_ops *ops = ctxt->ops;
int cpl = ctxt->ops->cpl(ctxt); int cpl = ctxt->ops->cpl(ctxt);
enum x86emul_mode prev_mode = ctxt->mode;
old_eip = ctxt->_eip; old_eip = ctxt->_eip;
ops->get_segment(ctxt, &old_cs, &old_desc, NULL, VCPU_SREG_CS); ops->get_segment(ctxt, &old_cs, &old_desc, NULL, VCPU_SREG_CS);
...@@ -3085,11 +3093,14 @@ static int em_call_far(struct x86_emulate_ctxt *ctxt) ...@@ -3085,11 +3093,14 @@ static int em_call_far(struct x86_emulate_ctxt *ctxt)
rc = em_push(ctxt); rc = em_push(ctxt);
/* If we failed, we tainted the memory, but the very least we should /* If we failed, we tainted the memory, but the very least we should
restore cs */ restore cs */
if (rc != X86EMUL_CONTINUE) if (rc != X86EMUL_CONTINUE) {
pr_warn_once("faulting far call emulation tainted memory\n");
goto fail; goto fail;
}
return rc; return rc;
fail: fail:
ops->set_segment(ctxt, old_cs, &old_desc, 0, VCPU_SREG_CS); ops->set_segment(ctxt, old_cs, &old_desc, 0, VCPU_SREG_CS);
ctxt->mode = prev_mode;
return rc; return rc;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment