CVE-2014-3647 (C, published 2014-11-10T11:55Z)
Description: value: arch/x86/kvm/emulate.c in the KVM subsystem in the Linux kernel through 3.17.2 does not properly perform RIP changes, which allows guest OS users to cause a denial of service (guest OS crash) via a crafted application.
Severity: LOW (CVSS v3 base score: 5.5)
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/torvalds/linux
Files changed:
arch/x86/kvm/emulate.c
Commit message: KVM: x86: Emulator fixes for eip canonical checks on near branches

Before changing rip (during jmp, call, ret, etc.) the target should be asserted
to be canonical one, as real CPUs do.  During sysret, both target rsp and rip
should be canonical. If any of these values is noncanonical, a #GP exception
should occur.  The exception to this rule are syscall and sysenter instructions
in which the assigned rip is checked during the assignment to the relevant
MSRs.

This patch fixes the emulator to behave as real CPUs do for near branches.
Far branches are handled by the next patch.

This fixes CVE-2014-3647.

Cc: stable@vger.kernel.org
Signed-off-by: Nadav Amit <namit@cs.technion.ac.il>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>


Vulnerable code:
static inline void assign_eip_near(struct x86_emulate_ctxt *ctxt, ulong dst)
static inline void jmp_rel(struct x86_emulate_ctxt *ctxt, int rel)
	assign_eip_near(ctxt, ctxt->_eip + rel);
		ctxt->_eip = ctxt->src.val;
		ctxt->_eip = ctxt->src.val;
	ctxt->dst.type = OP_REG;
	ctxt->dst.addr.reg = &ctxt->_eip;
	ctxt->dst.bytes = ctxt->op_bytes;
	return em_pop(ctxt);
	u64 msr_data;
	ctxt->_eip = reg_read(ctxt, VCPU_REGS_RDX);
	*reg_write(ctxt, VCPU_REGS_RSP) = reg_read(ctxt, VCPU_REGS_RCX);
	jmp_rel(ctxt, rel);
	ctxt->dst.type = OP_REG;
	ctxt->dst.addr.reg = &ctxt->_eip;
	ctxt->dst.bytes = ctxt->op_bytes;
	rc = emulate_pop(ctxt, &ctxt->dst.val, ctxt->op_bytes);
		jmp_rel(ctxt, ctxt->src.val);
	return X86EMUL_CONTINUE;
		jmp_rel(ctxt, ctxt->src.val);
	return X86EMUL_CONTINUE;
			jmp_rel(ctxt, ctxt->src.val);
		jmp_rel(ctxt, ctxt->src.val);
			jmp_rel(ctxt, ctxt->src.val);

Fixed code:
static inline int assign_eip_far(struct x86_emulate_ctxt *ctxt, ulong dst,
			       int cs_l)
		if ((cs_l && is_noncanonical_address(dst)) ||
		    (!cs_l && (dst & ~(u32)-1)))
			return emulate_gp(ctxt, 0);
	return X86EMUL_CONTINUE;
}

static inline int assign_eip_near(struct x86_emulate_ctxt *ctxt, ulong dst)
{
	return assign_eip_far(ctxt, dst, ctxt->mode == X86EMUL_MODE_PROT64);
static inline int jmp_rel(struct x86_emulate_ctxt *ctxt, int rel)
	return assign_eip_near(ctxt, ctxt->_eip + rel);
		rc = assign_eip_near(ctxt, ctxt->src.val);
		if (rc != X86EMUL_CONTINUE)
			break;
		rc = assign_eip_near(ctxt, ctxt->src.val);
	int rc;
	unsigned long eip;

	rc = emulate_pop(ctxt, &eip, ctxt->op_bytes);
	if (rc != X86EMUL_CONTINUE)
		return rc;

	return assign_eip_near(ctxt, eip);
	u64 msr_data, rcx, rdx;
	rcx = reg_read(ctxt, VCPU_REGS_RCX);
	rdx = reg_read(ctxt, VCPU_REGS_RDX);

		if (is_noncanonical_address(rcx) ||
		    is_noncanonical_address(rdx))
			return emulate_gp(ctxt, 0);
	ctxt->_eip = rdx;
	*reg_write(ctxt, VCPU_REGS_RSP) = rcx;
	int rc;
	rc = jmp_rel(ctxt, rel);
	if (rc != X86EMUL_CONTINUE)
		return rc;
	unsigned long eip;
	rc = emulate_pop(ctxt, &eip, ctxt->op_bytes);
	if (rc != X86EMUL_CONTINUE)
		return rc;
	rc = assign_eip_near(ctxt, eip);
	int rc = X86EMUL_CONTINUE;

		rc = jmp_rel(ctxt, ctxt->src.val);
	return rc;
	int rc = X86EMUL_CONTINUE;

		rc = jmp_rel(ctxt, ctxt->src.val);
	return rc;
			rc = jmp_rel(ctxt, ctxt->src.val);
		rc = jmp_rel(ctxt, ctxt->src.val);
			rc = jmp_rel(ctxt, ctxt->src.val);