git: 4485bfaed214 - main - bhyve: Validate VirtIO queue guest addresses

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Wed, 30 Sep 2026 15:54:09 UTC
The branch main has been updated by markj:

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

commit 4485bfaed2145e88911bd3522d6fee5c0f8ceb39
Author:     Hayzam Sherif <hayzam@gmail.com>
AuthorDate: 2026-09-30 14:17:24 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-09-30 15:53:51 +0000

    bhyve: Validate VirtIO queue guest addresses
    
    paddr_guest2host() returns NULL when a range does not fit within guest
    RAM.  An unmappable queue address previously left the queue marked
    allocated with ring pointers computed from the failed mapping.  Check
    the legacy virtqueue ring mapping before updating the queue state, and
    ignore an unmappable PFN write after printing a diagnostic.  This leaves
    an unallocated queue unallocated and preserves an existing queue's PFN,
    ring pointers, flags, and indices.
    
    Also check the indirect descriptor table mapping before dereferencing
    it, returning an error if the table is unmappable, and ignore guest
    queue notifications for unallocated queues.
    
    Reviewed by:    markj
    MFC after:      2 weeks
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D59904
---
 usr.sbin/bhyve/virtio.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/usr.sbin/bhyve/virtio.c b/usr.sbin/bhyve/virtio.c
index a5ccbb9983a0..c0122238b457 100644
--- a/usr.sbin/bhyve/virtio.c
+++ b/usr.sbin/bhyve/virtio.c
@@ -182,10 +182,15 @@ vi_vq_init(struct virtio_softc *vs, uint32_t pfn)
 	char *base;
 
 	vq = &vs->vs_queues[vs->vs_curq];
-	vq->vq_pfn = pfn;
 	phys = (uint64_t)pfn << VRING_PFN;
 	size = vring_size_aligned(vq->vq_qsize);
 	base = paddr_guest2host(vs->vs_pi->pi_vmctx, phys, size);
+	if (base == NULL) {
+		EPRINTLN("%s: queue %u has invalid GPA %#jx/%#zx",
+		    vs->vs_vc->vc_name, vq->vq_num, (uintmax_t)phys, size);
+		return;
+	}
+	vq->vq_pfn = pfn;
 
 	/* First page(s) are descriptors... */
 	vq->vq_desc = (struct vring_desc *)base;
@@ -349,6 +354,13 @@ vq_getchain(struct vqueue_info *vq, struct iovec *iov, int niov,
 			}
 			vindir = paddr_guest2host(ctx,
 			    vdir->addr, vdir->len);
+			if (vindir == NULL) {
+				EPRINTLN(
+				    "%s: indirect table GPA %#jx/%#x is "
+				    "unmappable", name, (uintmax_t)vdir->addr,
+				    (u_int)vdir->len);
+				return (-1);
+			}
 			/*
 			 * Indirects start at the 0th, then follow
 			 * their own embedded "next"s until those run
@@ -780,6 +792,8 @@ bad:
 			goto done;
 		}
 		vq = &vs->vs_queues[value];
+		if (!vq_ring_ready(vq))
+			goto done;
 		if (vq->vq_notify)
 			(*vq->vq_notify)(DEV_SOFTC(vs), vq);
 		else if (vc->vc_qnotify)