Skip to content

Commit b5b0977

Browse files
sean-jcbonzini
authored andcommitted
KVM: x86/mmu: Properly account NX huge page workaround for nonpaging MMUs
Account and track NX huge pages for nonpaging MMUs so that a future enhancement to precisely check if a shadow page can't be replaced by a NX huge page doesn't get false positives. Without correct tracking, KVM can get stuck in a loop if an instruction is fetching and writing data on the same huge page, e.g. KVM installs a small executable page on the fetch fault, replaces it with an NX huge page on the write fault, and faults again on the fetch. Alternatively, and perhaps ideally, KVM would simply not enforce the workaround for nonpaging MMUs. The guest has no page tables to abuse and KVM is guaranteed to switch to a different MMU on CR0.PG being toggled so there's no security or performance concerns. However, getting make_spte() to play nice now and in the future is unnecessarily complex. In the current code base, make_spte() can enforce the mitigation if TDP is enabled or the MMU is indirect, but make_spte() may not always have a vCPU/MMU to work with, e.g. if KVM were to support in-line huge page promotion when disabling dirty logging. Without a vCPU/MMU, KVM could either pass in the correct information and/or derive it from the shadow page, but the former is ugly and the latter subtly non-trivial due to the possibility of direct shadow pages in indirect MMUs. Given that using shadow paging with an unpaged guest is far from top priority _and_ has been subjected to the workaround since its inception, keep it simple and just fix the accounting glitch. Signed-off-by: Sean Christopherson <[email protected]> Reviewed-by: David Matlack <[email protected]> Reviewed-by: Mingwei Zhang <[email protected]> Message-Id: <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent 55c510e commit b5b0977

File tree

2 files changed

+13
-1
lines changed

2 files changed

+13
-1
lines changed

arch/x86/kvm/mmu/mmu.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3144,7 +3144,7 @@ static int __direct_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault)
31443144
continue;
31453145

31463146
link_shadow_page(vcpu, it.sptep, sp);
3147-
if (fault->is_tdp && fault->huge_page_disallowed)
3147+
if (fault->huge_page_disallowed)
31483148
account_nx_huge_page(vcpu->kvm, sp,
31493149
fault->req_level >= it.level);
31503150
}

arch/x86/kvm/mmu/spte.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,18 @@ bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp,
161161
if (!prefetch)
162162
spte |= spte_shadow_accessed_mask(spte);
163163

164+
/*
165+
* For simplicity, enforce the NX huge page mitigation even if not
166+
* strictly necessary. KVM could ignore the mitigation if paging is
167+
* disabled in the guest, as the guest doesn't have an page tables to
168+
* abuse. But to safely ignore the mitigation, KVM would have to
169+
* ensure a new MMU is loaded (or all shadow pages zapped) when CR0.PG
170+
* is toggled on, and that's a net negative for performance when TDP is
171+
* enabled. When TDP is disabled, KVM will always switch to a new MMU
172+
* when CR0.PG is toggled, but leveraging that to ignore the mitigation
173+
* would tie make_spte() further to vCPU/MMU state, and add complexity
174+
* just to optimize a mode that is anything but performance critical.
175+
*/
164176
if (level > PG_LEVEL_4K && (pte_access & ACC_EXEC_MASK) &&
165177
is_nx_huge_page_enabled(vcpu->kvm)) {
166178
pte_access &= ~ACC_EXEC_MASK;

0 commit comments

Comments
 (0)