Vulnerability: arch/x86/kvm/vmx.c in the Linux kernel through 4.6.3 mishandles the APICv on/off state, which allows guest OS users to obtain direct APIC MSR access on the host OS, and consequently cause a denial of service (host OS crash) or possibly execute arbitrary code on the host OS, via x2APIC mode.
Vulnerability ID: CVE-2016-4440
CWE: CWE-264
Severity score: 10
Project: https://github.com/torvalds/linux
Published: 2016-06-27
File: arch/x86/kvm/vmx.c
Commit message: kvm:vmx: more complete state update on APICv on/off

The function to update APICv on/off state (in particular, to deactivate
it when enabling Hyper-V SynIC) is incomplete: it doesn't adjust
APICv-related fields among secondary processor-based VM-execution
controls.  As a result, Windows 2012 guests get stuck when SynIC-based
auto-EOI interrupt intersected with e.g. an IPI in the guest.

In addition, the MSR intercept bitmap isn't updated every time "virtualize
x2APIC mode" is toggled.  This path can only be triggered by a malicious
guest, because Windows didn't use x2APIC but rather their own synthetic
APIC access MSRs; however a guest running in a SynIC-enabled VM could
switch to x2APIC and thus obtain direct access to host APIC MSRs
(CVE-2016-4440).

The patch fixes those omissions.

Signed-off-by: Roman Kagan <rkagan@virtuozzo.com>
Reported-by: Steve Rutherford <srutherford@google.com>
Reported-by: Yang Zhang <yang.zhang.wz@gmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Commit author: Roman Kagan

Vulnerable version (before fix):
 static void vmx_set_msr_bitmap(struct kvm_vcpu *vcpu)  	if (is_guest_mode(vcpu)) 		msr_bitmap = vmx_msr_bitmap_nested; 	else if (vcpu->arch.apic_base & X2APIC_ENABLE) { 		if (is_long_mode(vcpu)) 			msr_bitmap = vmx_msr_bitmap_longmode_x2apic; 		else static void vmx_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu) 	struct vcpu_vmx *vmx = to_vmx(vcpu);  	vmcs_write32(PIN_BASED_VM_EXEC_CONTROL, vmx_pin_based_exec_ctrl(vmx)); }  static u32 vmx_exec_control(struct vcpu_vmx *vmx) static __init int hardware_setup(void)  	set_bit(0, vmx_vpid_bitmap); /* 0 is reserved for host */  	if (enable_apicv) { 		for (msr = 0x800; msr <= 0x8ff; msr++) 			vmx_disable_intercept_msr_read_x2apic(msr);  		/* According SDM, in x2apic mode, the whole id reg is used. 		 * But in KVM, it only use the highest eight bits. Need to 		 * intercept it */ 		vmx_enable_intercept_msr_read_x2apic(0x802); 		/* TMCCT */ 		vmx_enable_intercept_msr_read_x2apic(0x839); 		/* TPR */ 		vmx_disable_intercept_msr_write_x2apic(0x808); 		/* EOI */ 		vmx_disable_intercept_msr_write_x2apic(0x80b); 		/* SELF-IPI */ 		vmx_disable_intercept_msr_write_x2apic(0x83f); 	}  	if (enable_ept) { 		kvm_mmu_set_mask_ptes(0ull,

Fixed version (after fix):
 static void vmx_set_msr_bitmap(struct kvm_vcpu *vcpu)  	if (is_guest_mode(vcpu)) 		msr_bitmap = vmx_msr_bitmap_nested; 	else if (cpu_has_secondary_exec_ctrls() && 		 (vmcs_read32(SECONDARY_VM_EXEC_CONTROL) & 		  SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE)) { 		if (is_long_mode(vcpu)) 			msr_bitmap = vmx_msr_bitmap_longmode_x2apic; 		else static void vmx_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu) 	struct vcpu_vmx *vmx = to_vmx(vcpu);  	vmcs_write32(PIN_BASED_VM_EXEC_CONTROL, vmx_pin_based_exec_ctrl(vmx)); 	if (cpu_has_secondary_exec_ctrls()) { 		if (kvm_vcpu_apicv_active(vcpu)) 			vmcs_set_bits(SECONDARY_VM_EXEC_CONTROL, 				      SECONDARY_EXEC_APIC_REGISTER_VIRT | 				      SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY); 		else 			vmcs_clear_bits(SECONDARY_VM_EXEC_CONTROL, 					SECONDARY_EXEC_APIC_REGISTER_VIRT | 					SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY); 	}  	if (cpu_has_vmx_msr_bitmap()) 		vmx_set_msr_bitmap(vcpu); }  static u32 vmx_exec_control(struct vcpu_vmx *vmx) static __init int hardware_setup(void)  	set_bit(0, vmx_vpid_bitmap); /* 0 is reserved for host */  	for (msr = 0x800; msr <= 0x8ff; msr++) 		vmx_disable_intercept_msr_read_x2apic(msr);  	/* According SDM, in x2apic mode, the whole id reg is used.  But in 	 * KVM, it only use the highest eight bits. Need to intercept it */ 	vmx_enable_intercept_msr_read_x2apic(0x802); 	/* TMCCT */ 	vmx_enable_intercept_msr_read_x2apic(0x839); 	/* TPR */ 	vmx_disable_intercept_msr_write_x2apic(0x808); 	/* EOI */ 	vmx_disable_intercept_msr_write_x2apic(0x80b); 	/* SELF-IPI */ 	vmx_disable_intercept_msr_write_x2apic(0x83f);  	if (enable_ept) { 		kvm_mmu_set_mask_ptes(0ull,

Patch:
patch: @@ -2418,7 +2418,9 @@ static void vmx_set_msr_bitmap(struct kvm_vcpu *vcpu)
 
 	if (is_guest_mode(vcpu))
 		msr_bitmap = vmx_msr_bitmap_nested;
-	else if (vcpu->arch.apic_base & X2APIC_ENABLE) {
+	else if (cpu_has_secondary_exec_ctrls() &&
+		 (vmcs_read32(SECONDARY_VM_EXEC_CONTROL) &
+		  SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE)) {
 		if (is_long_mode(vcpu))
 			msr_bitmap = vmx_msr_bitmap_longmode_x2apic;
 		else
@@ -4783,6 +4785,19 @@ static void vmx_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu)
 	struct vcpu_vmx *vmx = to_vmx(vcpu);
 
 	vmcs_write32(PIN_BASED_VM_EXEC_CONTROL, vmx_pin_based_exec_ctrl(vmx));
+	if (cpu_has_secondary_exec_ctrls()) {
+		if (kvm_vcpu_apicv_active(vcpu))
+			vmcs_set_bits(SECONDARY_VM_EXEC_CONTROL,
+				      SECONDARY_EXEC_APIC_REGISTER_VIRT |
+				      SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY);
+		else
+			vmcs_clear_bits(SECONDARY_VM_EXEC_CONTROL,
+					SECONDARY_EXEC_APIC_REGISTER_VIRT |
+					SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY);
+	}
+
+	if (cpu_has_vmx_msr_bitmap())
+		vmx_set_msr_bitmap(vcpu);
 }
 
 static u32 vmx_exec_control(struct vcpu_vmx *vmx)
@@ -6329,23 +6344,20 @@ static __init int hardware_setup(void)
 
 	set_bit(0, vmx_vpid_bitmap); /* 0 is reserved for host */
 
-	if (enable_apicv) {
-		for (msr = 0x800; msr <= 0x8ff; msr++)
-			vmx_disable_intercept_msr_read_x2apic(msr);
-
-		/* According SDM, in x2apic mode, the whole id reg is used.
-		 * But in KVM, it only use the highest eight bits. Need to
-		 * intercept it */
-		vmx_enable_intercept_msr_read_x2apic(0x802);
-		/* TMCCT */
-		vmx_enable_intercept_msr_read_x2apic(0x839);
-		/* TPR */
-		vmx_disable_intercept_msr_write_x2apic(0x808);
-		/* EOI */
-		vmx_disable_intercept_msr_write_x2apic(0x80b);
-		/* SELF-IPI */
-		vmx_disable_intercept_msr_write_x2apic(0x83f);
-	}
+	for (msr = 0x800; msr <= 0x8ff; msr++)
+		vmx_disable_intercept_msr_read_x2apic(msr);
+
+	/* According SDM, in x2apic mode, the whole id reg is used.  But in
+	 * KVM, it only use the highest eight bits. Need to intercept it */
+	vmx_enable_intercept_msr_read_x2apic(0x802);
+	/* TMCCT */
+	vmx_enable_intercept_msr_read_x2apic(0x839);
+	/* TPR */
+	vmx_disable_intercept_msr_write_x2apic(0x808);
+	/* EOI */
+	vmx_disable_intercept_msr_write_x2apic(0x80b);
+	/* SELF-IPI */
+	vmx_disable_intercept_msr_write_x2apic(0x83f);
 
 	if (enable_ept) {
 		kvm_mmu_set_mask_ptes(0ull,
path: arch/x86/kvm/vmx.c