Skip to content

Commit 513d44f

Browse files
rucoderrene
authored andcommitted
KVM: x86/mmu: emulate (not -EFAULT) guest access to a disabled passthrough BAR
A passed-through PCI device's BAR is mapped into the guest via a VM_IO/ VM_PFNMAP VMA whose fault handler (e.g. vfio_pci_mmap_fault) declines to install a PTE while the device's memory space is disabled, such as right after the guest clears PCI_COMMAND.MEM. If another vCPU accesses that BAR during the window, the gup in the page-fault path fails with an error pfn even though the memslot is still valid, and KVM_RUN returns -EFAULT to userspace, crashing the VM. A guest can trigger this at will, so it is a guest-triggerable host-side VM kill. On real hardware an access to a BAR with memory decoding disabled completes as an Unsupported Request (reads return all-ones, writes are dropped). KVM can present the same behaviour by treating the access as MMIO and emulating it, which is exactly what the noslot path already does. Distinguish the VM_IO/VM_PFNMAP fault-handler failure from other error pfns with a new KVM_PFN_ERR_PFNMAP value (in-range, so existing error-pfn range checks are unaffected) and route it to kvm_handle_noslot_fault() in the x86 TDP fault path. Genuine, non-pfnmap faults (e.g. a vanished anonymous backing) still take the fatal -EFAULT path, so real errors are not masked. The MMIO mapping self-heals when the device memory is re-enabled and the memslot is updated, bumping the MMIO generation. Fixes: abafbc5 ("vfio-pci: Invalidate mmaps and block MMIO access on disabled memory") Signed-off-by: Mikhail Malyshev <mike.malyshev@gmail.com>
1 parent d5cf6db commit 513d44f

3 files changed

Lines changed: 29 additions & 2 deletions

File tree

arch/x86/kvm/mmu/mmu.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4521,8 +4521,22 @@ static int kvm_faultin_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault,
45214521
if (ret != RET_PF_CONTINUE)
45224522
return ret;
45234523

4524-
if (unlikely(is_error_pfn(fault->pfn)))
4524+
if (unlikely(is_error_pfn(fault->pfn))) {
4525+
/*
4526+
* A passed-through PCI BAR is backed by a VM_IO/VM_PFNMAP
4527+
* mapping whose fault handler refuses to install a PTE while the
4528+
* device's memory space is disabled (e.g. the guest cleared
4529+
* PCI_COMMAND.MEM). The gup then fails even though the memslot
4530+
* is still valid. Treat such an access as MMIO and emulate it
4531+
* (the guest observes Unsupported Request semantics, matching
4532+
* real hardware) instead of killing the VM with -EFAULT. Other,
4533+
* non-pfnmap errors still take the fatal path.
4534+
*/
4535+
if (fault->pfn == KVM_PFN_ERR_PFNMAP)
4536+
return kvm_handle_noslot_fault(vcpu, fault, access);
4537+
45254538
return kvm_handle_error_pfn(vcpu, fault);
4539+
}
45264540

45274541
if (WARN_ON_ONCE(!fault->slot || is_noslot_pfn(fault->pfn)))
45284542
return kvm_handle_noslot_fault(vcpu, fault, access);

include/linux/kvm_host.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@
9797
#define KVM_PFN_ERR_HWPOISON (KVM_PFN_ERR_MASK + 1)
9898
#define KVM_PFN_ERR_RO_FAULT (KVM_PFN_ERR_MASK + 2)
9999
#define KVM_PFN_ERR_SIGPENDING (KVM_PFN_ERR_MASK + 3)
100+
/*
101+
* Faulting in a VM_IO/VM_PFNMAP mapping failed because its fault handler
102+
* declined to install a PTE, e.g. a passed-through PCI BAR whose device memory
103+
* is currently disabled (guest cleared PCI_COMMAND.MEM). The memslot is valid;
104+
* the access should be treated as MMIO rather than a fatal -EFAULT.
105+
*/
106+
#define KVM_PFN_ERR_PFNMAP (KVM_PFN_ERR_MASK + 4)
100107

101108
/*
102109
* error pfns indicate that the gfn is in slot but faild to

virt/kvm/kvm_main.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2991,7 +2991,13 @@ kvm_pfn_t hva_to_pfn(unsigned long addr, bool atomic, bool interruptible,
29912991
if (r == -EAGAIN)
29922992
goto retry;
29932993
if (r < 0)
2994-
pfn = KVM_PFN_ERR_FAULT;
2994+
/*
2995+
* The mapping's fault handler declined to install a PTE
2996+
* (e.g. a passed-through PCI BAR with device memory
2997+
* disabled). Flag it distinctly so the fault handler can
2998+
* treat the access as MMIO instead of a fatal -EFAULT.
2999+
*/
3000+
pfn = KVM_PFN_ERR_PFNMAP;
29953001
} else {
29963002
if (async && vma_is_valid(vma, write_fault))
29973003
*async = true;

0 commit comments

Comments
 (0)