git: ed03c3099086 - stable/14 - bhyve: avoid TOCTOU on iov_len in virtio_vq_recordon()

From: Ed Maste <emaste_at_FreeBSD.org>
Date: Wed, 02 Oct 2024 20:37:22 UTC
The branch stable/14 has been updated by emaste:

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

commit ed03c309908687bdb9f71dc6d9c9c8a92c54fc20
Author:     Pierre Pronchery <pierre@freebsdfoundation.org>
AuthorDate: 2024-08-27 13:57:32 +0000
Commit:     Ed Maste <emaste@FreeBSD.org>
CommitDate: 2024-10-02 20:37:09 +0000

    bhyve: avoid TOCTOU on iov_len in virtio_vq_recordon()
    
    Avoid a race condition when accessing guest memory, by reading memory
    contents only once.
    
    This has also been applied to _vq_record() in
    sys/dev/beri/virtio/virtio.c, as per markj@'s suggestion.
    
    Reported by:    Synacktiv
    Reviewed by:    markj
    Security:       HYP-10
    Sponsored by:   The Alpha-Omega Project
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D45735
    
    (cherry picked from commit 869d760cb9d7a307faa2fbe8c1c2b238a81b74d4)
---
 sys/dev/beri/virtio/virtio.c | 11 ++++++++---
 usr.sbin/bhyve/virtio.c      |  9 +++++++--
 2 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/sys/dev/beri/virtio/virtio.c b/sys/dev/beri/virtio/virtio.c
index c249d1c9d37b..7302bc39d799 100644
--- a/sys/dev/beri/virtio/virtio.c
+++ b/sys/dev/beri/virtio/virtio.c
@@ -107,12 +107,17 @@ paddr_unmap(void *phys, uint32_t size)
 static inline void
 _vq_record(uint32_t offs, int i, volatile struct vring_desc *vd,
 	struct iovec *iov, int n_iov, uint16_t *flags) {
+	uint32_t len;
+	uint64_t addr;
+
 	if (i >= n_iov)
 		return;
 
-	iov[i].iov_base = paddr_map(offs, be64toh(vd->addr),
-				be32toh(vd->len));
-	iov[i].iov_len = be32toh(vd->len);
+	len = atomic_load_32(&vd->len);
+	addr = atomic_load_64(&vd->addr);
+	iov[i].iov_base = paddr_map(offs, be64toh(addr),
+				be32toh(len));
+	iov[i].iov_len = be32toh(len);
 	if (flags != NULL)
 		flags[i] = be16toh(vd->flags);
 }
diff --git a/usr.sbin/bhyve/virtio.c b/usr.sbin/bhyve/virtio.c
index 0aeafa011bbc..5e8f8a8d322a 100644
--- a/usr.sbin/bhyve/virtio.c
+++ b/usr.sbin/bhyve/virtio.c
@@ -217,10 +217,15 @@ static inline void
 _vq_record(int i, struct vring_desc *vd, struct vmctx *ctx, struct iovec *iov,
     int n_iov, struct vi_req *reqp)
 {
+	uint32_t len;
+	uint64_t addr;
+
 	if (i >= n_iov)
 		return;
-	iov[i].iov_base = paddr_guest2host(ctx, vd->addr, vd->len);
-	iov[i].iov_len = vd->len;
+	len = atomic_load_32(&vd->len);
+	addr = atomic_load_64(&vd->addr);
+	iov[i].iov_len = len;
+	iov[i].iov_base = paddr_guest2host(ctx, addr, len);
 	if ((vd->flags & VRING_DESC_F_WRITE) == 0)
 		reqp->readable++;
 	else