git: c1434fd2deaf - stable/13 - bhyve: Validate host PAs used to map passthrough BARs.

From: John Baldwin <jhb_at_FreeBSD.org>
Date: Thu, 25 Aug 2022 17:31:43 UTC
The branch stable/13 has been updated by jhb:

URL: https://cgit.FreeBSD.org/src/commit/?id=c1434fd2deaf66a0004cabd4d893584652b24f80

commit c1434fd2deaf66a0004cabd4d893584652b24f80
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2022-08-19 21:59:44 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2022-08-25 16:38:56 +0000

    bhyve: Validate host PAs used to map passthrough BARs.
    
    Reject attempts to map host physical address ranges that are not
    subsets of a passthrough device's BAR into a guest.
    
    Reviewed by:    markj, emaste
    MFC after:      1 week
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D36238
    
    (cherry picked from commit c94f30ea85b745a5137471876013f79689e0af03)
---
 sys/amd64/vmm/io/ppt.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/sys/amd64/vmm/io/ppt.c b/sys/amd64/vmm/io/ppt.c
index a936326e8df3..3782b7b510f6 100644
--- a/sys/amd64/vmm/io/ppt.c
+++ b/sys/amd64/vmm/io/ppt.c
@@ -440,6 +440,23 @@ ppt_unassign_all(struct vm *vm)
 	return (0);
 }
 
+static bool
+ppt_valid_bar_mapping(struct pptdev *ppt, vm_paddr_t hpa, size_t len)
+{
+	struct pci_map *pm;
+	pci_addr_t base, size;
+
+	for (pm = pci_first_bar(ppt->dev); pm != NULL; pm = pci_next_bar(pm)) {
+		if (!PCI_BAR_MEM(pm->pm_value))
+			continue;
+		base = pm->pm_value & PCIM_BAR_MEM_BASE;
+		size = (pci_addr_t)1 << pm->pm_size;
+		if (hpa >= base && hpa + len <= base + size)
+			return (true);
+	}
+	return (false);
+}
+
 int
 ppt_map_mmio(struct vm *vm, int bus, int slot, int func,
 	     vm_paddr_t gpa, size_t len, vm_paddr_t hpa)
@@ -448,10 +465,17 @@ ppt_map_mmio(struct vm *vm, int bus, int slot, int func,
 	struct pptseg *seg;
 	struct pptdev *ppt;
 
+	if (len % PAGE_SIZE != 0 || len == 0 || gpa % PAGE_SIZE != 0 ||
+	    hpa % PAGE_SIZE != 0 || gpa + len < gpa || hpa + len < hpa)
+		return (EINVAL);
+
 	error = ppt_find(vm, bus, slot, func, &ppt);
 	if (error)
 		return (error);
 
+	if (!ppt_valid_bar_mapping(ppt, hpa, len))
+		return (EINVAL);
+
 	for (i = 0; i < MAX_MMIOSEGS; i++) {
 		seg = &ppt->mmio[i];
 		if (seg->len == 0) {