b136801
From: Nadav Amit <namit@cs.technion.ac.il>
b136801
Date: Fri, 24 Oct 2014 17:07:12 +0200
b136801
Subject: [PATCH] KVM: x86: Check non-canonical addresses upon WRMSR
b136801
b136801
Upon WRMSR, the CPU should inject #GP if a non-canonical value (address) is
b136801
written to certain MSRs. The behavior is "almost" identical for AMD and Intel
b136801
(ignoring MSRs that are not implemented in either architecture since they would
b136801
anyhow #GP). However, IA32_SYSENTER_ESP and IA32_SYSENTER_EIP cause #GP if
b136801
non-canonical address is written on Intel but not on AMD (which ignores the top
b136801
32-bits).
b136801
b136801
Accordingly, this patch injects a #GP on the MSRs which behave identically on
b136801
Intel and AMD.  To eliminate the differences between the architecutres, the
b136801
value which is written to IA32_SYSENTER_ESP and IA32_SYSENTER_EIP is turned to
b136801
canonical value before writing instead of injecting a #GP.
b136801
b136801
Some references from Intel and AMD manuals:
b136801
b136801
According to Intel SDM description of WRMSR instruction #GP is expected on
b136801
WRMSR "If the source register contains a non-canonical address and ECX
b136801
specifies one of the following MSRs: IA32_DS_AREA, IA32_FS_BASE, IA32_GS_BASE,
b136801
IA32_KERNEL_GS_BASE, IA32_LSTAR, IA32_SYSENTER_EIP, IA32_SYSENTER_ESP."
b136801
b136801
According to AMD manual instruction manual:
b136801
LSTAR/CSTAR (SYSCALL): "The WRMSR instruction loads the target RIP into the
b136801
LSTAR and CSTAR registers.  If an RIP written by WRMSR is not in canonical
b136801
form, a general-protection exception (#GP) occurs."
b136801
IA32_GS_BASE and IA32_FS_BASE (WRFSBASE/WRGSBASE): "The address written to the
b136801
base field must be in canonical form or a #GP fault will occur."
b136801
IA32_KERNEL_GS_BASE (SWAPGS): "The address stored in the KernelGSbase MSR must
b136801
be in canonical form."
b136801
b136801
This patch fixes CVE-2014-3610.
b136801
b136801
Cc: stable@vger.kernel.org
b136801
Signed-off-by: Nadav Amit <namit@cs.technion.ac.il>
b136801
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
b136801
---
b136801
 arch/x86/include/asm/kvm_host.h | 14 ++++++++++++++
b136801
 arch/x86/kvm/svm.c              |  2 +-
b136801
 arch/x86/kvm/vmx.c              |  2 +-
b136801
 arch/x86/kvm/x86.c              | 27 ++++++++++++++++++++++++++-
b136801
 4 files changed, 42 insertions(+), 3 deletions(-)
b136801
b136801
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
b136801
index 7c492ed9087b..78d014c83ae3 100644
b136801
--- a/arch/x86/include/asm/kvm_host.h
b136801
+++ b/arch/x86/include/asm/kvm_host.h
b136801
@@ -990,6 +990,20 @@ static inline void kvm_inject_gp(struct kvm_vcpu *vcpu, u32 error_code)
b136801
 	kvm_queue_exception_e(vcpu, GP_VECTOR, error_code);
b136801
 }
b136801
 
b136801
+static inline u64 get_canonical(u64 la)
b136801
+{
b136801
+	return ((int64_t)la << 16) >> 16;
b136801
+}
b136801
+
b136801
+static inline bool is_noncanonical_address(u64 la)
b136801
+{
b136801
+#ifdef CONFIG_X86_64
b136801
+	return get_canonical(la) != la;
b136801
+#else
b136801
+	return false;
b136801
+#endif
b136801
+}
b136801
+
b136801
 #define TSS_IOPB_BASE_OFFSET 0x66
b136801
 #define TSS_BASE_SIZE 0x68
b136801
 #define TSS_IOPB_SIZE (65536 / 8)
b136801
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
b136801
index ddf742768ecf..e2de97daa03c 100644
b136801
--- a/arch/x86/kvm/svm.c
b136801
+++ b/arch/x86/kvm/svm.c
b136801
@@ -3234,7 +3234,7 @@ static int wrmsr_interception(struct vcpu_svm *svm)
b136801
 	msr.host_initiated = false;
b136801
 
b136801
 	svm->next_rip = kvm_rip_read(&svm->vcpu) + 2;
b136801
-	if (svm_set_msr(&svm->vcpu, &msr)) {
b136801
+	if (kvm_set_msr(&svm->vcpu, &msr)) {
b136801
 		trace_kvm_msr_write_ex(ecx, data);
b136801
 		kvm_inject_gp(&svm->vcpu, 0);
b136801
 	} else {
b136801
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
b136801
index 6a118fa378b5..3a3e419780df 100644
b136801
--- a/arch/x86/kvm/vmx.c
b136801
+++ b/arch/x86/kvm/vmx.c
b136801
@@ -5263,7 +5263,7 @@ static int handle_wrmsr(struct kvm_vcpu *vcpu)
b136801
 	msr.data = data;
b136801
 	msr.index = ecx;
b136801
 	msr.host_initiated = false;
b136801
-	if (vmx_set_msr(vcpu, &msr) != 0) {
b136801
+	if (kvm_set_msr(vcpu, &msr) != 0) {
b136801
 		trace_kvm_msr_write_ex(ecx, data);
b136801
 		kvm_inject_gp(vcpu, 0);
b136801
 		return 1;
b136801
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
b136801
index 8f1e22d3b286..1f9a233d8624 100644
b136801
--- a/arch/x86/kvm/x86.c
b136801
+++ b/arch/x86/kvm/x86.c
b136801
@@ -984,7 +984,6 @@ void kvm_enable_efer_bits(u64 mask)
b136801
 }
b136801
 EXPORT_SYMBOL_GPL(kvm_enable_efer_bits);
b136801
 
b136801
-
b136801
 /*
b136801
  * Writes msr value into into the appropriate "register".
b136801
  * Returns 0 on success, non-0 otherwise.
b136801
@@ -992,8 +991,34 @@ EXPORT_SYMBOL_GPL(kvm_enable_efer_bits);
b136801
  */
b136801
 int kvm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
b136801
 {
b136801
+	switch (msr->index) {
b136801
+	case MSR_FS_BASE:
b136801
+	case MSR_GS_BASE:
b136801
+	case MSR_KERNEL_GS_BASE:
b136801
+	case MSR_CSTAR:
b136801
+	case MSR_LSTAR:
b136801
+		if (is_noncanonical_address(msr->data))
b136801
+			return 1;
b136801
+		break;
b136801
+	case MSR_IA32_SYSENTER_EIP:
b136801
+	case MSR_IA32_SYSENTER_ESP:
b136801
+		/*
b136801
+		 * IA32_SYSENTER_ESP and IA32_SYSENTER_EIP cause #GP if
b136801
+		 * non-canonical address is written on Intel but not on
b136801
+		 * AMD (which ignores the top 32-bits, because it does
b136801
+		 * not implement 64-bit SYSENTER).
b136801
+		 *
b136801
+		 * 64-bit code should hence be able to write a non-canonical
b136801
+		 * value on AMD.  Making the address canonical ensures that
b136801
+		 * vmentry does not fail on Intel after writing a non-canonical
b136801
+		 * value, and that something deterministic happens if the guest
b136801
+		 * invokes 64-bit SYSENTER.
b136801
+		 */
b136801
+		msr->data = get_canonical(msr->data);
b136801
+	}
b136801
 	return kvm_x86_ops->set_msr(vcpu, msr);
b136801
 }
b136801
+EXPORT_SYMBOL_GPL(kvm_set_msr);
b136801
 
b136801
 /*
b136801
  * Adapt set_msr() to msr_io()'s calling convention
b136801
-- 
b136801
1.9.3
b136801