3e20b48
From: Nadav Amit <namit@cs.technion.ac.il>
3e20b48
Date: Fri, 24 Oct 2014 17:07:16 +0200
3e20b48
Subject: [PATCH] KVM: x86: Emulator fixes for eip canonical checks on near
3e20b48
 branches
3e20b48
3e20b48
Before changing rip (during jmp, call, ret, etc.) the target should be asserted
3e20b48
to be canonical one, as real CPUs do.  During sysret, both target rsp and rip
3e20b48
should be canonical. If any of these values is noncanonical, a #GP exception
3e20b48
should occur.  The exception to this rule are syscall and sysenter instructions
3e20b48
in which the assigned rip is checked during the assignment to the relevant
3e20b48
MSRs.
3e20b48
3e20b48
This patch fixes the emulator to behave as real CPUs do for near branches.
3e20b48
Far branches are handled by the next patch.
3e20b48
3e20b48
This fixes CVE-2014-3647.
3e20b48
3e20b48
Cc: stable@vger.kernel.org
3e20b48
Signed-off-by: Nadav Amit <namit@cs.technion.ac.il>
3e20b48
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
3e20b48
---
3e20b48
 arch/x86/kvm/emulate.c | 78 ++++++++++++++++++++++++++++++++++----------------
3e20b48
 1 file changed, 54 insertions(+), 24 deletions(-)
3e20b48
3e20b48
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
3e20b48
index a85f438b6a47..e52e74feedb8 100644
3e20b48
--- a/arch/x86/kvm/emulate.c
3e20b48
+++ b/arch/x86/kvm/emulate.c
3e20b48
@@ -563,7 +563,8 @@ static int emulate_nm(struct x86_emulate_ctxt *ctxt)
3e20b48
 	return emulate_exception(ctxt, NM_VECTOR, 0, false);
3e20b48
 }
3e20b48
 
3e20b48
-static inline void assign_eip_near(struct x86_emulate_ctxt *ctxt, ulong dst)
3e20b48
+static inline int assign_eip_far(struct x86_emulate_ctxt *ctxt, ulong dst,
3e20b48
+			       int cs_l)
3e20b48
 {
3e20b48
 	switch (ctxt->op_bytes) {
3e20b48
 	case 2:
3e20b48
@@ -573,16 +574,25 @@ static inline void assign_eip_near(struct x86_emulate_ctxt *ctxt, ulong dst)
3e20b48
 		ctxt->_eip = (u32)dst;
3e20b48
 		break;
3e20b48
 	case 8:
3e20b48
+		if ((cs_l && is_noncanonical_address(dst)) ||
3e20b48
+		    (!cs_l && (dst & ~(u32)-1)))
3e20b48
+			return emulate_gp(ctxt, 0);
3e20b48
 		ctxt->_eip = dst;
3e20b48
 		break;
3e20b48
 	default:
3e20b48
 		WARN(1, "unsupported eip assignment size\n");
3e20b48
 	}
3e20b48
+	return X86EMUL_CONTINUE;
3e20b48
+}
3e20b48
+
3e20b48
+static inline int assign_eip_near(struct x86_emulate_ctxt *ctxt, ulong dst)
3e20b48
+{
3e20b48
+	return assign_eip_far(ctxt, dst, ctxt->mode == X86EMUL_MODE_PROT64);
3e20b48
 }
3e20b48
 
3e20b48
-static inline void jmp_rel(struct x86_emulate_ctxt *ctxt, int rel)
3e20b48
+static inline int jmp_rel(struct x86_emulate_ctxt *ctxt, int rel)
3e20b48
 {
3e20b48
-	assign_eip_near(ctxt, ctxt->_eip + rel);
3e20b48
+	return assign_eip_near(ctxt, ctxt->_eip + rel);
3e20b48
 }
3e20b48
 
3e20b48
 static u16 get_segment_selector(struct x86_emulate_ctxt *ctxt, unsigned seg)
3e20b48
@@ -1989,13 +1999,15 @@ static int em_grp45(struct x86_emulate_ctxt *ctxt)
3e20b48
 	case 2: /* call near abs */ {
3e20b48
 		long int old_eip;
3e20b48
 		old_eip = ctxt->_eip;
3e20b48
-		ctxt->_eip = ctxt->src.val;
3e20b48
+		rc = assign_eip_near(ctxt, ctxt->src.val);
3e20b48
+		if (rc != X86EMUL_CONTINUE)
3e20b48
+			break;
3e20b48
 		ctxt->src.val = old_eip;
3e20b48
 		rc = em_push(ctxt);
3e20b48
 		break;
3e20b48
 	}
3e20b48
 	case 4: /* jmp abs */
3e20b48
-		ctxt->_eip = ctxt->src.val;
3e20b48
+		rc = assign_eip_near(ctxt, ctxt->src.val);
3e20b48
 		break;
3e20b48
 	case 5: /* jmp far */
3e20b48
 		rc = em_jmp_far(ctxt);
3e20b48
@@ -2030,10 +2042,14 @@ static int em_cmpxchg8b(struct x86_emulate_ctxt *ctxt)
3e20b48
 
3e20b48
 static int em_ret(struct x86_emulate_ctxt *ctxt)
3e20b48
 {
3e20b48
-	ctxt->dst.type = OP_REG;
3e20b48
-	ctxt->dst.addr.reg = &ctxt->_eip;
3e20b48
-	ctxt->dst.bytes = ctxt->op_bytes;
3e20b48
-	return em_pop(ctxt);
3e20b48
+	int rc;
3e20b48
+	unsigned long eip;
3e20b48
+
3e20b48
+	rc = emulate_pop(ctxt, &eip, ctxt->op_bytes);
3e20b48
+	if (rc != X86EMUL_CONTINUE)
3e20b48
+		return rc;
3e20b48
+
3e20b48
+	return assign_eip_near(ctxt, eip);
3e20b48
 }
3e20b48
 
3e20b48
 static int em_ret_far(struct x86_emulate_ctxt *ctxt)
3e20b48
@@ -2314,7 +2330,7 @@ static int em_sysexit(struct x86_emulate_ctxt *ctxt)
3e20b48
 {
3e20b48
 	const struct x86_emulate_ops *ops = ctxt->ops;
3e20b48
 	struct desc_struct cs, ss;
3e20b48
-	u64 msr_data;
3e20b48
+	u64 msr_data, rcx, rdx;
3e20b48
 	int usermode;
3e20b48
 	u16 cs_sel = 0, ss_sel = 0;
3e20b48
 
3e20b48
@@ -2330,6 +2346,9 @@ static int em_sysexit(struct x86_emulate_ctxt *ctxt)
3e20b48
 	else
3e20b48
 		usermode = X86EMUL_MODE_PROT32;
3e20b48
 
3e20b48
+	rcx = reg_read(ctxt, VCPU_REGS_RCX);
3e20b48
+	rdx = reg_read(ctxt, VCPU_REGS_RDX);
3e20b48
+
3e20b48
 	cs.dpl = 3;
3e20b48
 	ss.dpl = 3;
3e20b48
 	ops->get_msr(ctxt, MSR_IA32_SYSENTER_CS, &msr_data);
3e20b48
@@ -2347,6 +2366,9 @@ static int em_sysexit(struct x86_emulate_ctxt *ctxt)
3e20b48
 		ss_sel = cs_sel + 8;
3e20b48
 		cs.d = 0;
3e20b48
 		cs.l = 1;
3e20b48
+		if (is_noncanonical_address(rcx) ||
3e20b48
+		    is_noncanonical_address(rdx))
3e20b48
+			return emulate_gp(ctxt, 0);
3e20b48
 		break;
3e20b48
 	}
3e20b48
 	cs_sel |= SELECTOR_RPL_MASK;
3e20b48
@@ -2355,8 +2377,8 @@ static int em_sysexit(struct x86_emulate_ctxt *ctxt)
3e20b48
 	ops->set_segment(ctxt, cs_sel, &cs, 0, VCPU_SREG_CS);
3e20b48
 	ops->set_segment(ctxt, ss_sel, &ss, 0, VCPU_SREG_SS);
3e20b48
 
3e20b48
-	ctxt->_eip = reg_read(ctxt, VCPU_REGS_RDX);
3e20b48
-	*reg_write(ctxt, VCPU_REGS_RSP) = reg_read(ctxt, VCPU_REGS_RCX);
3e20b48
+	ctxt->_eip = rdx;
3e20b48
+	*reg_write(ctxt, VCPU_REGS_RSP) = rcx;
3e20b48
 
3e20b48
 	return X86EMUL_CONTINUE;
3e20b48
 }
3e20b48
@@ -2897,10 +2919,13 @@ static int em_aad(struct x86_emulate_ctxt *ctxt)
3e20b48
 
3e20b48
 static int em_call(struct x86_emulate_ctxt *ctxt)
3e20b48
 {
3e20b48
+	int rc;
3e20b48
 	long rel = ctxt->src.val;
3e20b48
 
3e20b48
 	ctxt->src.val = (unsigned long)ctxt->_eip;
3e20b48
-	jmp_rel(ctxt, rel);
3e20b48
+	rc = jmp_rel(ctxt, rel);
3e20b48
+	if (rc != X86EMUL_CONTINUE)
3e20b48
+		return rc;
3e20b48
 	return em_push(ctxt);
3e20b48
 }
3e20b48
 
3e20b48
@@ -2932,11 +2957,12 @@ static int em_call_far(struct x86_emulate_ctxt *ctxt)
3e20b48
 static int em_ret_near_imm(struct x86_emulate_ctxt *ctxt)
3e20b48
 {
3e20b48
 	int rc;
3e20b48
+	unsigned long eip;
3e20b48
 
3e20b48
-	ctxt->dst.type = OP_REG;
3e20b48
-	ctxt->dst.addr.reg = &ctxt->_eip;
3e20b48
-	ctxt->dst.bytes = ctxt->op_bytes;
3e20b48
-	rc = emulate_pop(ctxt, &ctxt->dst.val, ctxt->op_bytes);
3e20b48
+	rc = emulate_pop(ctxt, &eip, ctxt->op_bytes);
3e20b48
+	if (rc != X86EMUL_CONTINUE)
3e20b48
+		return rc;
3e20b48
+	rc = assign_eip_near(ctxt, eip);
3e20b48
 	if (rc != X86EMUL_CONTINUE)
3e20b48
 		return rc;
3e20b48
 	rsp_increment(ctxt, ctxt->src.val);
3e20b48
@@ -3267,20 +3293,24 @@ static int em_lmsw(struct x86_emulate_ctxt *ctxt)
3e20b48
 
3e20b48
 static int em_loop(struct x86_emulate_ctxt *ctxt)
3e20b48
 {
3e20b48
+	int rc = X86EMUL_CONTINUE;
3e20b48
+
3e20b48
 	register_address_increment(ctxt, reg_rmw(ctxt, VCPU_REGS_RCX), -1);
3e20b48
 	if ((address_mask(ctxt, reg_read(ctxt, VCPU_REGS_RCX)) != 0) &&
3e20b48
 	    (ctxt->b == 0xe2 || test_cc(ctxt->b ^ 0x5, ctxt->eflags)))
3e20b48
-		jmp_rel(ctxt, ctxt->src.val);
3e20b48
+		rc = jmp_rel(ctxt, ctxt->src.val);
3e20b48
 
3e20b48
-	return X86EMUL_CONTINUE;
3e20b48
+	return rc;
3e20b48
 }
3e20b48
 
3e20b48
 static int em_jcxz(struct x86_emulate_ctxt *ctxt)
3e20b48
 {
3e20b48
+	int rc = X86EMUL_CONTINUE;
3e20b48
+
3e20b48
 	if (address_mask(ctxt, reg_read(ctxt, VCPU_REGS_RCX)) == 0)
3e20b48
-		jmp_rel(ctxt, ctxt->src.val);
3e20b48
+		rc = jmp_rel(ctxt, ctxt->src.val);
3e20b48
 
3e20b48
-	return X86EMUL_CONTINUE;
3e20b48
+	return rc;
3e20b48
 }
3e20b48
 
3e20b48
 static int em_in(struct x86_emulate_ctxt *ctxt)
3e20b48
@@ -4728,7 +4758,7 @@ special_insn:
3e20b48
 		break;
3e20b48
 	case 0x70 ... 0x7f: /* jcc (short) */
3e20b48
 		if (test_cc(ctxt->b, ctxt->eflags))
3e20b48
-			jmp_rel(ctxt, ctxt->src.val);
3e20b48
+			rc = jmp_rel(ctxt, ctxt->src.val);
3e20b48
 		break;
3e20b48
 	case 0x8d: /* lea r16/r32, m */
3e20b48
 		ctxt->dst.val = ctxt->src.addr.mem.ea;
3e20b48
@@ -4758,7 +4788,7 @@ special_insn:
3e20b48
 		break;
3e20b48
 	case 0xe9: /* jmp rel */
3e20b48
 	case 0xeb: /* jmp rel short */
3e20b48
-		jmp_rel(ctxt, ctxt->src.val);
3e20b48
+		rc = jmp_rel(ctxt, ctxt->src.val);
3e20b48
 		ctxt->dst.type = OP_NONE; /* Disable writeback. */
3e20b48
 		break;
3e20b48
 	case 0xf4:              /* hlt */
3e20b48
@@ -4881,7 +4911,7 @@ twobyte_insn:
3e20b48
 		break;
3e20b48
 	case 0x80 ... 0x8f: /* jnz rel, etc*/
3e20b48
 		if (test_cc(ctxt->b, ctxt->eflags))
3e20b48
-			jmp_rel(ctxt, ctxt->src.val);
3e20b48
+			rc = jmp_rel(ctxt, ctxt->src.val);
3e20b48
 		break;
3e20b48
 	case 0x90 ... 0x9f:     /* setcc r/m8 */
3e20b48
 		ctxt->dst.val = test_cc(ctxt->b, ctxt->eflags);
3e20b48
-- 
3e20b48
1.9.3
3e20b48