Chuck Ebbert 643d353
From: Avi Kivity <avi@redhat.com>
Chuck Ebbert 643d353
Date: Tue, 19 Oct 2010 14:46:55 +0000 (+0200)
Chuck Ebbert 643d353
Subject: KVM: Fix fs/gs reload oops with invalid ldt
Chuck Ebbert 643d353
X-Git-Tag: v2.6.36~4^2
Chuck Ebbert 643d353
X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=9581d442b9058d3699b4be568b6e5eae38a41493
Chuck Ebbert 643d353
Chuck Ebbert 643d353
KVM: Fix fs/gs reload oops with invalid ldt
Chuck Ebbert 643d353
Chuck Ebbert 643d353
kvm reloads the host's fs and gs blindly, however the underlying segment
Chuck Ebbert 643d353
descriptors may be invalid due to the user modifying the ldt after loading
Chuck Ebbert 643d353
them.
Chuck Ebbert 643d353
Chuck Ebbert 643d353
Fix by using the safe accessors (loadsegment() and load_gs_index()) instead
Chuck Ebbert 643d353
of home grown unsafe versions.
Chuck Ebbert 643d353
Chuck Ebbert 643d353
This is CVE-2010-3698.
Chuck Ebbert 643d353
Chuck Ebbert 643d353
KVM-Stable-Tag.
Chuck Ebbert 643d353
Signed-off-by: Avi Kivity <avi@redhat.com>
Chuck Ebbert 643d353
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Chuck Ebbert 643d353
---
Chuck Ebbert 643d353
Chuck Ebbert 643d353
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
Chuck Ebbert 643d353
index 502e53f..c52e2eb 100644
Chuck Ebbert 643d353
--- a/arch/x86/include/asm/kvm_host.h
Chuck Ebbert 643d353
+++ b/arch/x86/include/asm/kvm_host.h
Chuck Ebbert 643d353
@@ -652,20 +652,6 @@ static inline struct kvm_mmu_page *page_header(hpa_t shadow_page)
Chuck Ebbert 643d353
 	return (struct kvm_mmu_page *)page_private(page);
Chuck Ebbert 643d353
 }
Chuck Ebbert 643d353
 
Chuck Ebbert 643d353
-static inline u16 kvm_read_fs(void)
Chuck Ebbert 643d353
-{
Chuck Ebbert 643d353
-	u16 seg;
Chuck Ebbert 643d353
-	asm("mov %%fs, %0" : "=g"(seg));
Chuck Ebbert 643d353
-	return seg;
Chuck Ebbert 643d353
-}
Chuck Ebbert 643d353
-
Chuck Ebbert 643d353
-static inline u16 kvm_read_gs(void)
Chuck Ebbert 643d353
-{
Chuck Ebbert 643d353
-	u16 seg;
Chuck Ebbert 643d353
-	asm("mov %%gs, %0" : "=g"(seg));
Chuck Ebbert 643d353
-	return seg;
Chuck Ebbert 643d353
-}
Chuck Ebbert 643d353
-
Chuck Ebbert 643d353
 static inline u16 kvm_read_ldt(void)
Chuck Ebbert 643d353
 {
Chuck Ebbert 643d353
 	u16 ldt;
Chuck Ebbert 643d353
@@ -673,16 +659,6 @@ static inline u16 kvm_read_ldt(void)
Chuck Ebbert 643d353
 	return ldt;
Chuck Ebbert 643d353
 }
Chuck Ebbert 643d353
 
Chuck Ebbert 643d353
-static inline void kvm_load_fs(u16 sel)
Chuck Ebbert 643d353
-{
Chuck Ebbert 643d353
-	asm("mov %0, %%fs" : : "rm"(sel));
Chuck Ebbert 643d353
-}
Chuck Ebbert 643d353
-
Chuck Ebbert 643d353
-static inline void kvm_load_gs(u16 sel)
Chuck Ebbert 643d353
-{
Chuck Ebbert 643d353
-	asm("mov %0, %%gs" : : "rm"(sel));
Chuck Ebbert 643d353
-}
Chuck Ebbert 643d353
-
Chuck Ebbert 643d353
 static inline void kvm_load_ldt(u16 sel)
Chuck Ebbert 643d353
 {
Chuck Ebbert 643d353
 	asm("lldt %0" : : "rm"(sel));
Chuck Ebbert 643d353
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
Chuck Ebbert 643d353
index 81ed28c..8a3f9f6 100644
Chuck Ebbert 643d353
--- a/arch/x86/kvm/svm.c
Chuck Ebbert 643d353
+++ b/arch/x86/kvm/svm.c
Chuck Ebbert 643d353
@@ -3163,8 +3163,8 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu)
Chuck Ebbert 643d353
 	sync_lapic_to_cr8(vcpu);
Chuck Ebbert 643d353
 
Chuck Ebbert 643d353
 	save_host_msrs(vcpu);
Chuck Ebbert 643d353
-	fs_selector = kvm_read_fs();
Chuck Ebbert 643d353
-	gs_selector = kvm_read_gs();
Chuck Ebbert 643d353
+	savesegment(fs, fs_selector);
Chuck Ebbert 643d353
+	savesegment(gs, gs_selector);
Chuck Ebbert 643d353
 	ldt_selector = kvm_read_ldt();
Chuck Ebbert 643d353
 	svm->vmcb->save.cr2 = vcpu->arch.cr2;
Chuck Ebbert 643d353
 	/* required for live migration with NPT */
Chuck Ebbert 643d353
@@ -3251,10 +3251,15 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu)
Chuck Ebbert 643d353
 	vcpu->arch.regs[VCPU_REGS_RSP] = svm->vmcb->save.rsp;
Chuck Ebbert 643d353
 	vcpu->arch.regs[VCPU_REGS_RIP] = svm->vmcb->save.rip;
Chuck Ebbert 643d353
 
Chuck Ebbert 643d353
-	kvm_load_fs(fs_selector);
Chuck Ebbert 643d353
-	kvm_load_gs(gs_selector);
Chuck Ebbert 643d353
-	kvm_load_ldt(ldt_selector);
Chuck Ebbert 643d353
 	load_host_msrs(vcpu);
Chuck Ebbert 643d353
+	loadsegment(fs, fs_selector);
Chuck Ebbert 643d353
+#ifdef CONFIG_X86_64
Chuck Ebbert 643d353
+	load_gs_index(gs_selector);
Chuck Ebbert 643d353
+	wrmsrl(MSR_KERNEL_GS_BASE, current->thread.gs);
Chuck Ebbert 643d353
+#else
Chuck Ebbert 643d353
+	loadsegment(gs, gs_selector);
Chuck Ebbert 643d353
+#endif
Chuck Ebbert 643d353
+	kvm_load_ldt(ldt_selector);
Chuck Ebbert 643d353
 
Chuck Ebbert 643d353
 	reload_tss(vcpu);
Chuck Ebbert 643d353
 
Chuck Ebbert 643d353
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
Chuck Ebbert 643d353
index 49b25ee..7bddfab 100644
Chuck Ebbert 643d353
--- a/arch/x86/kvm/vmx.c
Chuck Ebbert 643d353
+++ b/arch/x86/kvm/vmx.c
Chuck Ebbert 643d353
@@ -803,7 +803,7 @@ static void vmx_save_host_state(struct kvm_vcpu *vcpu)
Chuck Ebbert 643d353
 	 */
Chuck Ebbert 643d353
 	vmx->host_state.ldt_sel = kvm_read_ldt();
Chuck Ebbert 643d353
 	vmx->host_state.gs_ldt_reload_needed = vmx->host_state.ldt_sel;
Chuck Ebbert 643d353
-	vmx->host_state.fs_sel = kvm_read_fs();
Chuck Ebbert 643d353
+	savesegment(fs, vmx->host_state.fs_sel);
Chuck Ebbert 643d353
 	if (!(vmx->host_state.fs_sel & 7)) {
Chuck Ebbert 643d353
 		vmcs_write16(HOST_FS_SELECTOR, vmx->host_state.fs_sel);
Chuck Ebbert 643d353
 		vmx->host_state.fs_reload_needed = 0;
Chuck Ebbert 643d353
@@ -811,7 +811,7 @@ static void vmx_save_host_state(struct kvm_vcpu *vcpu)
Chuck Ebbert 643d353
 		vmcs_write16(HOST_FS_SELECTOR, 0);
Chuck Ebbert 643d353
 		vmx->host_state.fs_reload_needed = 1;
Chuck Ebbert 643d353
 	}
Chuck Ebbert 643d353
-	vmx->host_state.gs_sel = kvm_read_gs();
Chuck Ebbert 643d353
+	savesegment(gs, vmx->host_state.gs_sel);
Chuck Ebbert 643d353
 	if (!(vmx->host_state.gs_sel & 7))
Chuck Ebbert 643d353
 		vmcs_write16(HOST_GS_SELECTOR, vmx->host_state.gs_sel);
Chuck Ebbert 643d353
 	else {
Chuck Ebbert 643d353
@@ -841,27 +841,21 @@ static void vmx_save_host_state(struct kvm_vcpu *vcpu)
Chuck Ebbert 643d353
 
Chuck Ebbert 643d353
 static void __vmx_load_host_state(struct vcpu_vmx *vmx)
Chuck Ebbert 643d353
 {
Chuck Ebbert 643d353
-	unsigned long flags;
Chuck Ebbert 643d353
-
Chuck Ebbert 643d353
 	if (!vmx->host_state.loaded)
Chuck Ebbert 643d353
 		return;
Chuck Ebbert 643d353
 
Chuck Ebbert 643d353
 	++vmx->vcpu.stat.host_state_reload;
Chuck Ebbert 643d353
 	vmx->host_state.loaded = 0;
Chuck Ebbert 643d353
 	if (vmx->host_state.fs_reload_needed)
Chuck Ebbert 643d353
-		kvm_load_fs(vmx->host_state.fs_sel);
Chuck Ebbert 643d353
+		loadsegment(fs, vmx->host_state.fs_sel);
Chuck Ebbert 643d353
 	if (vmx->host_state.gs_ldt_reload_needed) {
Chuck Ebbert 643d353
 		kvm_load_ldt(vmx->host_state.ldt_sel);
Chuck Ebbert 643d353
-		/*
Chuck Ebbert 643d353
-		 * If we have to reload gs, we must take care to
Chuck Ebbert 643d353
-		 * preserve our gs base.
Chuck Ebbert 643d353
-		 */
Chuck Ebbert 643d353
-		local_irq_save(flags);
Chuck Ebbert 643d353
-		kvm_load_gs(vmx->host_state.gs_sel);
Chuck Ebbert 643d353
 #ifdef CONFIG_X86_64
Chuck Ebbert 643d353
-		wrmsrl(MSR_GS_BASE, vmcs_readl(HOST_GS_BASE));
Chuck Ebbert 643d353
+		load_gs_index(vmx->host_state.gs_sel);
Chuck Ebbert 643d353
+		wrmsrl(MSR_KERNEL_GS_BASE, current->thread.gs);
Chuck Ebbert 643d353
+#else
Chuck Ebbert 643d353
+		loadsegment(gs, vmx->host_state.gs_sel);
Chuck Ebbert 643d353
 #endif
Chuck Ebbert 643d353
-		local_irq_restore(flags);
Chuck Ebbert 643d353
 	}
Chuck Ebbert 643d353
 	reload_tss();
Chuck Ebbert 643d353
 #ifdef CONFIG_X86_64
Chuck Ebbert 643d353
@@ -2589,8 +2583,8 @@ static int vmx_vcpu_setup(struct vcpu_vmx *vmx)
Chuck Ebbert 643d353
 	vmcs_write16(HOST_CS_SELECTOR, __KERNEL_CS);  /* 22.2.4 */
Chuck Ebbert 643d353
 	vmcs_write16(HOST_DS_SELECTOR, __KERNEL_DS);  /* 22.2.4 */
Chuck Ebbert 643d353
 	vmcs_write16(HOST_ES_SELECTOR, __KERNEL_DS);  /* 22.2.4 */
Chuck Ebbert 643d353
-	vmcs_write16(HOST_FS_SELECTOR, kvm_read_fs());    /* 22.2.4 */
Chuck Ebbert 643d353
-	vmcs_write16(HOST_GS_SELECTOR, kvm_read_gs());    /* 22.2.4 */
Chuck Ebbert 643d353
+	vmcs_write16(HOST_FS_SELECTOR, 0);            /* 22.2.4 */
Chuck Ebbert 643d353
+	vmcs_write16(HOST_GS_SELECTOR, 0);            /* 22.2.4 */
Chuck Ebbert 643d353
 	vmcs_write16(HOST_SS_SELECTOR, __KERNEL_DS);  /* 22.2.4 */
Chuck Ebbert 643d353
 #ifdef CONFIG_X86_64
Chuck Ebbert 643d353
 	rdmsrl(MSR_FS_BASE, a);