Skip to content

Commit cf9f4c0

Browse files
committed
KVM: x86/mmu: Refresh CR0.WP prior to checking for emulated permission faults
Refresh the MMU's snapshot of the vCPU's CR0.WP prior to checking for permission faults when emulating a guest memory access and CR0.WP may be guest owned. If the guest toggles only CR0.WP and triggers emulation of a supervisor write, e.g. when KVM is emulating UMIP, KVM may consume a stale CR0.WP, i.e. use stale protection bits metadata. Note, KVM passes through CR0.WP if and only if EPT is enabled as CR0.WP is part of the MMU role for legacy shadow paging, and SVM (NPT) doesn't support per-bit interception controls for CR0. Don't bother checking for EPT vs. NPT as the "old == new" check will always be true under NPT, i.e. the only cost is the read of vcpu->arch.cr4 (SVM unconditionally grabs CR0 from the VMCB on VM-Exit). Reported-by: Mathias Krause <minipli@grsecurity.net> Link: https://lkml.kernel.org/r/677169b4-051f-fcae-756b-9a3e1bb9f8fe%40grsecurity.net Fixes: fb509f7 ("KVM: VMX: Make CR0.WP a guest owned bit") Tested-by: Mathias Krause <minipli@grsecurity.net> Link: https://lore.kernel.org/r/20230405002608.418442-1-seanjc@google.com Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent 944a8da commit cf9f4c0

2 files changed

Lines changed: 40 additions & 1 deletion

File tree

arch/x86/kvm/mmu.h

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly,
113113
bool kvm_can_do_async_pf(struct kvm_vcpu *vcpu);
114114
int kvm_handle_page_fault(struct kvm_vcpu *vcpu, u64 error_code,
115115
u64 fault_address, char *insn, int insn_len);
116+
void __kvm_mmu_refresh_passthrough_bits(struct kvm_vcpu *vcpu,
117+
struct kvm_mmu *mmu);
116118

117119
int kvm_mmu_load(struct kvm_vcpu *vcpu);
118120
void kvm_mmu_unload(struct kvm_vcpu *vcpu);
@@ -153,6 +155,24 @@ static inline void kvm_mmu_load_pgd(struct kvm_vcpu *vcpu)
153155
vcpu->arch.mmu->root_role.level);
154156
}
155157

158+
static inline void kvm_mmu_refresh_passthrough_bits(struct kvm_vcpu *vcpu,
159+
struct kvm_mmu *mmu)
160+
{
161+
/*
162+
* When EPT is enabled, KVM may passthrough CR0.WP to the guest, i.e.
163+
* @mmu's snapshot of CR0.WP and thus all related paging metadata may
164+
* be stale. Refresh CR0.WP and the metadata on-demand when checking
165+
* for permission faults. Exempt nested MMUs, i.e. MMUs for shadowing
166+
* nEPT and nNPT, as CR0.WP is ignored in both cases. Note, KVM does
167+
* need to refresh nested_mmu, a.k.a. the walker used to translate L2
168+
* GVAs to GPAs, as that "MMU" needs to honor L2's CR0.WP.
169+
*/
170+
if (!tdp_enabled || mmu == &vcpu->arch.guest_mmu)
171+
return;
172+
173+
__kvm_mmu_refresh_passthrough_bits(vcpu, mmu);
174+
}
175+
156176
/*
157177
* Check if a given access (described through the I/D, W/R and U/S bits of a
158178
* page fault error code pfec) causes a permission fault with the given PTE
@@ -184,8 +204,12 @@ static inline u8 permission_fault(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
184204
u64 implicit_access = access & PFERR_IMPLICIT_ACCESS;
185205
bool not_smap = ((rflags & X86_EFLAGS_AC) | implicit_access) == X86_EFLAGS_AC;
186206
int index = (pfec + (not_smap << PFERR_RSVD_BIT)) >> 1;
187-
bool fault = (mmu->permissions[index] >> pte_access) & 1;
188207
u32 errcode = PFERR_PRESENT_MASK;
208+
bool fault;
209+
210+
kvm_mmu_refresh_passthrough_bits(vcpu, mmu);
211+
212+
fault = (mmu->permissions[index] >> pte_access) & 1;
189213

190214
WARN_ON(pfec & (PFERR_PK_MASK | PFERR_RSVD_MASK));
191215
if (unlikely(mmu->pkru_mask)) {

arch/x86/kvm/mmu/mmu.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5112,6 +5112,21 @@ kvm_calc_cpu_role(struct kvm_vcpu *vcpu, const struct kvm_mmu_role_regs *regs)
51125112
return role;
51135113
}
51145114

5115+
void __kvm_mmu_refresh_passthrough_bits(struct kvm_vcpu *vcpu,
5116+
struct kvm_mmu *mmu)
5117+
{
5118+
const bool cr0_wp = kvm_is_cr0_bit_set(vcpu, X86_CR0_WP);
5119+
5120+
BUILD_BUG_ON((KVM_MMU_CR0_ROLE_BITS & KVM_POSSIBLE_CR0_GUEST_BITS) != X86_CR0_WP);
5121+
BUILD_BUG_ON((KVM_MMU_CR4_ROLE_BITS & KVM_POSSIBLE_CR4_GUEST_BITS));
5122+
5123+
if (is_cr0_wp(mmu) == cr0_wp)
5124+
return;
5125+
5126+
mmu->cpu_role.base.cr0_wp = cr0_wp;
5127+
reset_guest_paging_metadata(vcpu, mmu);
5128+
}
5129+
51155130
static inline int kvm_mmu_get_tdp_level(struct kvm_vcpu *vcpu)
51165131
{
51175132
/* tdp_root_level is architecture forced level, use it if nonzero */

0 commit comments

Comments
 (0)