[PATCH v2 6/6] kvm: return -EFAULT for writes to !VM_WRITE IO mappings
Paolo Bonzini <[email protected]> Tue, 4 Aug 2026 14:05:28 +0200
| Newsgroups | gmane.comp.emulators.kvm.devel,gmane.linux.kernel,gmane.comp.video.dri.devel,gmane.linux.kernel.mm |
|---|---|
| Message-ID | <[email protected]> |
KVM's behavior when the guest writes to a non-writable VMA is inconsistent.
For regular, page-backed mappings it returns KVM_PFN_ERR_FAULT and thus
returns -EFAULT to userspace (which is ABI, and relied upon by tests);
for VM_IO/VM_PFNMAP mappings instead it returns KVM_PFN_ERR_RO_FAULT
and thus exits to userspace with KVM_EXIT_MMIO.
This behavior for VM_{IO,PFNMAP} was added by commit bd2fae8da794 ("KVM:
do not assume PTE is writable after follow_pfn"), and even if it has been
in place for five years it is unlikely that it is relied upon by userspace,
since it is inconsistent with KVM itself. Change hva_to_pfn() to return
KVM_PFN_ERR_FAULT for all non-writable VMAs, and restrict KVM_EXIT_MMIO
to the case of an explicitly read-only memslot.
Suggested-by: Sean Christopherson <[email protected]>
Signed-off-by: Paolo Bonzini <[email protected]>
---
virt/kvm/kvm_main.c | 11 +++--------
1 file changed, 3 insertions(+), 8 deletions(-)
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index b7c21a48a45c..da5b0bb62590 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -2999,15 +2999,10 @@ kvm_pfn_t hva_to_pfn(struct kvm_follow_pfn *kfp)
* or the page might be absent.
*/
- if (vma == NULL || unlikely(!(vma->vm_flags & VM_READ))) {
+ if (vma == NULL ||
+ unlikely(!(vma->vm_flags & VM_READ)) ||
+ ((kfp->flags & FOLL_WRITE) && unlikely(!(vma->vm_flags & VM_WRITE)))) {
pfn = KVM_PFN_ERR_FAULT;
- } else if ((kfp->flags & FOLL_WRITE) && unlikely(!(vma->vm_flags & VM_WRITE))) {
- /*
- * Exit to userspace for PROT_READ mappings in a writable
- * memslot, as this is part of the API.
- */
- pfn = vma->vm_flags & (VM_IO | VM_PFNMAP) ? KVM_PFN_ERR_RO_FAULT :
- KVM_PFN_ERR_FAULT;
} else if (vma->vm_flags & (VM_IO | VM_PFNMAP)) {
r = hva_to_pfn_remapped(vma, kfp, &pfn);
if (r == -EAGAIN)
--
2.55.0