After 28e39181 ("drm/gem-shmem: Track folio accessed/dirty status in mmap") was merged, a guest write to an unpopulated PTE from a mapping backed by a DRM GEM BO triggers a VM exit with EFAULT, with hva_to_pfn_remapped setting p_pfn to KVM_PFN_ERR_RO_FAULT. This happens because that commit implements pfn_mkwrite for drm_gem_shmem_vm_ops. With that function present, vma_wants_writenotify returns true in vma_set_page_prot, clearing VM_SHARED and leading to the entry to be installed as read-only. This is done on purpose so the fault handler gets notified when the entry is going to be written. In KVM, hva_to_pfn_remapped calls to fixup_user_fault to trigger the fault handler but, as seen above, this one might install a read-only PTE even with FAULT_FLAG_WRITE present in fault_flags. The check at the end of hva_to_pfn_remapped notices that the entry is not writable despite this being a write fault and sets p_pfn to KVM_PFN_ERR_RO_FAULT. To address this issue, have hva_to_pfn_remapped issue a second fixup_user_fault call when needed for write-upgrading the PTE. Signed-off-by: Sergio Lopez --- virt/kvm/kvm_main.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 45e784462ec6..82c5a6b94267 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -2941,6 +2941,7 @@ static int hva_to_pfn_remapped(struct vm_area_struct *vma, { struct follow_pfnmap_args args = { .vma = vma, .address = kfp->hva }; bool write_fault = kfp->flags & FOLL_WRITE; + bool unlocked = false; int r; /* @@ -2957,7 +2958,6 @@ static int hva_to_pfn_remapped(struct vm_area_struct *vma, * get_user_pages fails for VM_IO and VM_PFNMAP vmas and does * not call the fault handler, so do it here. */ - bool unlocked = false; r = fixup_user_fault(current->mm, kfp->hva, (write_fault ? FAULT_FLAG_WRITE : 0), &unlocked); @@ -2972,8 +2972,27 @@ static int hva_to_pfn_remapped(struct vm_area_struct *vma, } if (write_fault && !args.writable) { - *p_pfn = KVM_PFN_ERR_RO_FAULT; - goto out; + /* + * VM_PFNMAP fault handlers may install read-only PTEs via + * vmf_insert_pfn(), deferring the write upgrade to a second + * fault. Trigger that upgrade now. + */ + follow_pfnmap_end(&args); + r = fixup_user_fault(current->mm, kfp->hva, FAULT_FLAG_WRITE, + &unlocked); + if (unlocked) + return -EAGAIN; + if (r) + return r; + + r = follow_pfnmap_start(&args); + if (r) + return r; + + if (!args.writable) { + *p_pfn = KVM_PFN_ERR_RO_FAULT; + goto out; + } } *p_pfn = kvm_resolve_pfn(kfp, NULL, &args, args.writable); -- 2.55.0