Skip to content

Commit 11e3491

Browse files
vittyvkbonzini
authored andcommitted
x86/kvm/nVMX: fix VMCLEAR when Enlightened VMCS is in use
When Enlightened VMCS is in use, it is valid to do VMCLEAR and, according to TLFS, this should "transition an enlightened VMCS from the active to the non-active state". It is, however, wrong to assume that it is only valid to do VMCLEAR for the eVMCS which is currently active on the vCPU performing VMCLEAR. Currently, the logic in handle_vmclear() is broken: in case, there is no active eVMCS on the vCPU doing VMCLEAR we treat the argument as a 'normal' VMCS and kvm_vcpu_write_guest() to the 'launch_state' field irreversibly corrupts the memory area. So, in case the VMCLEAR argument is not the current active eVMCS on the vCPU, how can we know if the area it is pointing to is a normal or an enlightened VMCS? Thanks to the bug in Hyper-V (see commit 72aeb60 ("KVM: nVMX: Verify eVMCS revision id match supported eVMCS version on eVMCS VMPTRLD")) we can not, the revision can't be used to distinguish between them. So let's assume it is always enlightened in case enlightened vmentry is enabled in the assist page. Also, check if vmx->nested.enlightened_vmcs_enabled to minimize the impact for 'unenlightened' workloads. Fixes: b8bbab9 ("KVM: nVMX: implement enlightened VMPTRLD and VMCLEAR") Signed-off-by: Vitaly Kuznetsov <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent a21a39c commit 11e3491

File tree

3 files changed

+37
-14
lines changed

3 files changed

+37
-14
lines changed

arch/x86/kvm/vmx/evmcs.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <linux/errno.h>
44
#include <linux/smp.h>
55

6+
#include "../hyperv.h"
67
#include "evmcs.h"
78
#include "vmcs.h"
89
#include "vmx.h"
@@ -313,6 +314,23 @@ void evmcs_sanitize_exec_ctrls(struct vmcs_config *vmcs_conf)
313314
}
314315
#endif
315316

317+
bool nested_enlightened_vmentry(struct kvm_vcpu *vcpu, u64 *evmcs_gpa)
318+
{
319+
struct hv_vp_assist_page assist_page;
320+
321+
*evmcs_gpa = -1ull;
322+
323+
if (unlikely(!kvm_hv_get_assist_page(vcpu, &assist_page)))
324+
return false;
325+
326+
if (unlikely(!assist_page.enlighten_vmentry))
327+
return false;
328+
329+
*evmcs_gpa = assist_page.current_nested_vmcs;
330+
331+
return true;
332+
}
333+
316334
uint16_t nested_get_evmcs_version(struct kvm_vcpu *vcpu)
317335
{
318336
struct vcpu_vmx *vmx = to_vmx(vcpu);

arch/x86/kvm/vmx/evmcs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ static inline void evmcs_sanitize_exec_ctrls(struct vmcs_config *vmcs_conf) {}
195195
static inline void evmcs_touch_msr_bitmap(void) {}
196196
#endif /* IS_ENABLED(CONFIG_HYPERV) */
197197

198+
bool nested_enlightened_vmentry(struct kvm_vcpu *vcpu, u64 *evmcs_gpa);
198199
uint16_t nested_get_evmcs_version(struct kvm_vcpu *vcpu);
199200
int nested_enable_evmcs(struct kvm_vcpu *vcpu,
200201
uint16_t *vmcs_version);

arch/x86/kvm/vmx/nested.c

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1783,27 +1783,22 @@ static int nested_vmx_handle_enlightened_vmptrld(struct kvm_vcpu *vcpu,
17831783
bool from_launch)
17841784
{
17851785
struct vcpu_vmx *vmx = to_vmx(vcpu);
1786-
struct hv_vp_assist_page assist_page;
17871786
bool evmcs_gpa_changed = false;
1787+
u64 evmcs_gpa;
17881788

17891789
if (likely(!vmx->nested.enlightened_vmcs_enabled))
17901790
return 1;
17911791

1792-
if (unlikely(!kvm_hv_get_assist_page(vcpu, &assist_page)))
1792+
if (!nested_enlightened_vmentry(vcpu, &evmcs_gpa))
17931793
return 1;
17941794

1795-
if (unlikely(!assist_page.enlighten_vmentry))
1796-
return 1;
1797-
1798-
if (unlikely(assist_page.current_nested_vmcs !=
1799-
vmx->nested.hv_evmcs_vmptr)) {
1800-
1795+
if (unlikely(evmcs_gpa != vmx->nested.hv_evmcs_vmptr)) {
18011796
if (!vmx->nested.hv_evmcs)
18021797
vmx->nested.current_vmptr = -1ull;
18031798

18041799
nested_release_evmcs(vcpu);
18051800

1806-
if (kvm_vcpu_map(vcpu, gpa_to_gfn(assist_page.current_nested_vmcs),
1801+
if (kvm_vcpu_map(vcpu, gpa_to_gfn(evmcs_gpa),
18071802
&vmx->nested.hv_evmcs_map))
18081803
return 0;
18091804

@@ -1838,7 +1833,7 @@ static int nested_vmx_handle_enlightened_vmptrld(struct kvm_vcpu *vcpu,
18381833
}
18391834

18401835
vmx->nested.dirty_vmcs12 = true;
1841-
vmx->nested.hv_evmcs_vmptr = assist_page.current_nested_vmcs;
1836+
vmx->nested.hv_evmcs_vmptr = evmcs_gpa;
18421837

18431838
evmcs_gpa_changed = true;
18441839
/*
@@ -4436,6 +4431,7 @@ static int handle_vmclear(struct kvm_vcpu *vcpu)
44364431
struct vcpu_vmx *vmx = to_vmx(vcpu);
44374432
u32 zero = 0;
44384433
gpa_t vmptr;
4434+
u64 evmcs_gpa;
44394435

44404436
if (!nested_vmx_check_permission(vcpu))
44414437
return 1;
@@ -4451,10 +4447,18 @@ static int handle_vmclear(struct kvm_vcpu *vcpu)
44514447
return nested_vmx_failValid(vcpu,
44524448
VMXERR_VMCLEAR_VMXON_POINTER);
44534449

4454-
if (vmx->nested.hv_evmcs_map.hva) {
4455-
if (vmptr == vmx->nested.hv_evmcs_vmptr)
4456-
nested_release_evmcs(vcpu);
4457-
} else {
4450+
/*
4451+
* When Enlightened VMEntry is enabled on the calling CPU we treat
4452+
* memory area pointer by vmptr as Enlightened VMCS (as there's no good
4453+
* way to distinguish it from VMCS12) and we must not corrupt it by
4454+
* writing to the non-existent 'launch_state' field. The area doesn't
4455+
* have to be the currently active EVMCS on the calling CPU and there's
4456+
* nothing KVM has to do to transition it from 'active' to 'non-active'
4457+
* state. It is possible that the area will stay mapped as
4458+
* vmx->nested.hv_evmcs but this shouldn't be a problem.
4459+
*/
4460+
if (likely(!vmx->nested.enlightened_vmcs_enabled ||
4461+
!nested_enlightened_vmentry(vcpu, &evmcs_gpa))) {
44584462
if (vmptr == vmx->nested.current_vmptr)
44594463
nested_release_vmcs12(vcpu);
44604464

0 commit comments

Comments
 (0)