[PATCH 04/24] KVM: x86/mmu: intersect writability from __kvm_faultin_pfn with fault->map_writable
From: Paolo Bonzini
Date: Thu Jul 16 2026 - 14:36:25 EST
fault->map_writable is currently a pure output of __kvm_faultin_pfn(),
which is the only thing that restricts it. This will no longer hold once
memory protections derived from memory attributes are applied: those
compute their own access permissions that combine with those from
__kvm_faultin_pfn().
Applying them *before* faulting in the pfn lets a fault that violates
the attributes exit to userspace without the cost of gup and/or an
async #PF; but it means that permissions will then be restricted in two
independent steps, first by memory attributes and then by __kvm_faultin_pfn().
Switch fault->map_writable to that model by letting kvm_mmu_faultin_pfn()
only clear bits rather than assign them.
No functional change intended: nothing writes fault->map_writable between
the initializer and __kvm_mmu_faultin_pfn() yet, so the AND is equivalent
to the assignment it replaces.
Signed-off-by: Paolo Bonzini <pbonzini@xxxxxxxxxx>
---
arch/x86/kvm/mmu/mmu.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index 6c13da942bfc..cf6a409b76ae 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -4603,7 +4603,7 @@ static int kvm_mmu_faultin_pfn_gmem(struct kvm_vcpu *vcpu,
return r;
}
- fault->map_writable = !(fault->slot->flags & KVM_MEM_READONLY);
+ fault->map_writable &= !(fault->slot->flags & KVM_MEM_READONLY);
fault->max_level = kvm_max_level_for_order(max_order);
return RET_PF_CONTINUE;
@@ -4613,13 +4613,14 @@ static int __kvm_mmu_faultin_pfn(struct kvm_vcpu *vcpu,
struct kvm_page_fault *fault)
{
unsigned int foll = fault->write ? FOLL_WRITE : 0;
+ bool writable;
if (fault->is_private || kvm_memslot_is_gmem_only(fault->slot))
return kvm_mmu_faultin_pfn_gmem(vcpu, fault);
foll |= FOLL_NOWAIT;
fault->pfn = __kvm_faultin_pfn(fault->slot, fault->gfn, foll,
- &fault->map_writable, &fault->refcounted_page);
+ &writable, &fault->refcounted_page);
/*
* If resolving the page failed because I/O is needed to fault-in the
@@ -4628,7 +4629,7 @@ static int __kvm_mmu_faultin_pfn(struct kvm_vcpu *vcpu,
* other failures are terminal, i.e. retrying won't help.
*/
if (fault->pfn != KVM_PFN_ERR_NEEDS_IO)
- return RET_PF_CONTINUE;
+ goto out_pf_continue;
if (!fault->prefetch && kvm_can_do_async_pf(vcpu)) {
trace_kvm_try_async_get_page(fault->addr, fault->gfn);
@@ -4649,8 +4650,10 @@ static int __kvm_mmu_faultin_pfn(struct kvm_vcpu *vcpu,
foll |= FOLL_INTERRUPTIBLE;
foll &= ~FOLL_NOWAIT;
fault->pfn = __kvm_faultin_pfn(fault->slot, fault->gfn, foll,
- &fault->map_writable, &fault->refcounted_page);
+ &writable, &fault->refcounted_page);
+out_pf_continue:
+ fault->map_writable &= writable;
return RET_PF_CONTINUE;
}
@@ -4968,6 +4971,7 @@ static int kvm_mmu_do_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa,
.is_private = err & PFERR_PRIVATE_ACCESS,
.pfn = KVM_PFN_ERR_FAULT,
+ .map_writable = true,
};
int r;
--
2.52.0