aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLiran Alon <liran.alon@oracle.com>2018-06-29 15:59:04 -0400
committerPaolo Bonzini <pbonzini@redhat.com>2018-07-18 05:31:28 -0400
commit2307af1c4b2e0ad886f30e31739845322cbd328b (patch)
treea2dd40049c5d7613e1c71afceb9c6ee51713b2c3
parent9432a3175770e06cb83eada2d91fac90c977cb99 (diff)
KVM: VMX: Mark VMXArea with revision_id of physical CPU even when eVMCS enabled
When eVMCS is enabled, all VMCS allocated to be used by KVM are marked with revision_id of KVM_EVMCS_VERSION instead of revision_id reported by MSR_IA32_VMX_BASIC. However, even though not explictly documented by TLFS, VMXArea passed as VMXON argument should still be marked with revision_id reported by physical CPU. This issue was found by the following setup: * L0 = KVM which expose eVMCS to it's L1 guest. * L1 = KVM which consume eVMCS reported by L0. This setup caused the following to occur: 1) L1 execute hardware_enable(). 2) hardware_enable() calls kvm_cpu_vmxon() to execute VMXON. 3) L0 intercept L1 VMXON and execute handle_vmon() which notes vmxarea->revision_id != VMCS12_REVISION and therefore fails with nested_vmx_failInvalid() which sets RFLAGS.CF. 4) L1 kvm_cpu_vmxon() don't check RFLAGS.CF for failure and therefore hardware_enable() continues as usual. 5) L1 hardware_enable() then calls ept_sync_global() which executes INVEPT. 6) L0 intercept INVEPT and execute handle_invept() which notes !vmx->nested.vmxon and thus raise a #UD to L1. 7) Raised #UD caused L1 to panic. Reviewed-by: Krish Sadhukhan <krish.sadhukhan@oracle.com> Cc: stable@vger.kernel.org Fixes: 773e8a0425c923bc02668a2d6534a5ef5a43cc69 Signed-off-by: Liran Alon <liran.alon@oracle.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-rw-r--r--arch/x86/kvm/vmx.c27
1 files changed, 21 insertions, 6 deletions
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index ba981459d706..c3c85908b8de 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -4108,11 +4108,7 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf)
4108 vmcs_conf->order = get_order(vmcs_conf->size); 4108 vmcs_conf->order = get_order(vmcs_conf->size);
4109 vmcs_conf->basic_cap = vmx_msr_high & ~0x1fff; 4109 vmcs_conf->basic_cap = vmx_msr_high & ~0x1fff;
4110 4110
4111 /* KVM supports Enlightened VMCS v1 only */ 4111 vmcs_conf->revision_id = vmx_msr_low;
4112 if (static_branch_unlikely(&enable_evmcs))
4113 vmcs_conf->revision_id = KVM_EVMCS_VERSION;
4114 else
4115 vmcs_conf->revision_id = vmx_msr_low;
4116 4112
4117 vmcs_conf->pin_based_exec_ctrl = _pin_based_exec_control; 4113 vmcs_conf->pin_based_exec_ctrl = _pin_based_exec_control;
4118 vmcs_conf->cpu_based_exec_ctrl = _cpu_based_exec_control; 4114 vmcs_conf->cpu_based_exec_ctrl = _cpu_based_exec_control;
@@ -4182,7 +4178,13 @@ static struct vmcs *alloc_vmcs_cpu(int cpu)
4182 return NULL; 4178 return NULL;
4183 vmcs = page_address(pages); 4179 vmcs = page_address(pages);
4184 memset(vmcs, 0, vmcs_config.size); 4180 memset(vmcs, 0, vmcs_config.size);
4185 vmcs->revision_id = vmcs_config.revision_id; /* vmcs revision id */ 4181
4182 /* KVM supports Enlightened VMCS v1 only */
4183 if (static_branch_unlikely(&enable_evmcs))
4184 vmcs->revision_id = KVM_EVMCS_VERSION;
4185 else
4186 vmcs->revision_id = vmcs_config.revision_id;
4187
4186 return vmcs; 4188 return vmcs;
4187} 4189}
4188 4190
@@ -4341,6 +4343,19 @@ static __init int alloc_kvm_area(void)
4341 return -ENOMEM; 4343 return -ENOMEM;
4342 } 4344 }
4343 4345
4346 /*
4347 * When eVMCS is enabled, alloc_vmcs_cpu() sets
4348 * vmcs->revision_id to KVM_EVMCS_VERSION instead of
4349 * revision_id reported by MSR_IA32_VMX_BASIC.
4350 *
4351 * However, even though not explictly documented by
4352 * TLFS, VMXArea passed as VMXON argument should
4353 * still be marked with revision_id reported by
4354 * physical CPU.
4355 */
4356 if (static_branch_unlikely(&enable_evmcs))
4357 vmcs->revision_id = vmcs_config.revision_id;
4358
4344 per_cpu(vmxarea, cpu) = vmcs; 4359 per_cpu(vmxarea, cpu) = vmcs;
4345 } 4360 }
4346 return 0; 4361 return 0;