The branch main has been updated by emaste:

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

commit 4dfeadc32e464557c2aa450212ac419bc567d1e6
Author:     Hans Rosenfeld <[email protected]>
AuthorDate: 2025-10-28 08:51:26 +0000
Commit:     Ed Maste <[email protected]>
CommitDate: 2026-03-04 17:30:02 +0000

    bhyve/virtio-scsi: Check LUN address validity
    
    Instead of blindly trusting the guest OS driver that it sends us well-
    formed LUN addresses, check the LUN address for validity and fail the
    request if it is invalid. While here, constify the members of the virtio
    requests which aren't device-writable anyway.
    
    Reviewed by:    markj
    Differential Revision: https://reviews.freebsd.org/D53470
---
 usr.sbin/bhyve/pci_virtio_scsi.c | 123 ++++++++++++++++++++++++++++++++++-----
 1 file changed, 108 insertions(+), 15 deletions(-)

diff --git a/usr.sbin/bhyve/pci_virtio_scsi.c b/usr.sbin/bhyve/pci_virtio_scsi.c
index 758e2643f6a0..dafff50fa531 100644
--- a/usr.sbin/bhyve/pci_virtio_scsi.c
+++ b/usr.sbin/bhyve/pci_virtio_scsi.c
@@ -200,10 +200,10 @@ struct pci_vtscsi_softc {
 #define        VIRTIO_SCSI_S_FUNCTION_REJECTED         11
 
 struct pci_vtscsi_ctrl_tmf {
-       uint32_t type;
-       uint32_t subtype;
-       uint8_t lun[8];
-       uint64_t id;
+       const uint32_t type;
+       const uint32_t subtype;
+       const uint8_t lun[8];
+       const uint64_t id;
        uint8_t response;
 } __attribute__((packed));
 
@@ -216,9 +216,9 @@ struct pci_vtscsi_ctrl_tmf {
 #define        VIRTIO_SCSI_EVT_ASYNC_DEVICE_BUSY       64
 
 struct pci_vtscsi_ctrl_an {
-       uint32_t type;
-       uint8_t lun[8];
-       uint32_t event_requested;
+       const uint32_t type;
+       const uint8_t lun[8];
+       const uint32_t event_requested;
        uint32_t event_actual;
        uint8_t response;
 } __attribute__((packed));
@@ -249,12 +249,12 @@ struct pci_vtscsi_event {
 } __attribute__((packed));
 
 struct pci_vtscsi_req_cmd_rd {
-       uint8_t lun[8];
-       uint64_t id;
-       uint8_t task_attr;
-       uint8_t prio;
-       uint8_t crn;
-       uint8_t cdb[];
+       const uint8_t lun[8];
+       const uint64_t id;
+       const uint8_t task_attr;
+       const uint8_t prio;
+       const uint8_t crn;
+       const uint8_t cdb[];
 } __attribute__((packed));
 
 struct pci_vtscsi_req_cmd_wr {
@@ -271,7 +271,10 @@ static void pci_vtscsi_reset(void *);
 static void pci_vtscsi_neg_features(void *, uint64_t);
 static int pci_vtscsi_cfgread(void *, int, int, uint32_t *);
 static int pci_vtscsi_cfgwrite(void *, int, int, uint32_t);
-static inline int pci_vtscsi_get_lun(uint8_t *);
+
+static inline bool pci_vtscsi_check_lun(const uint8_t *);
+static inline int pci_vtscsi_get_lun(const uint8_t *);
+
 static void pci_vtscsi_control_handle(struct pci_vtscsi_softc *, void *, 
size_t);
 static void pci_vtscsi_tmf_handle(struct pci_vtscsi_softc *,
     struct pci_vtscsi_ctrl_tmf *);
@@ -398,9 +401,76 @@ pci_vtscsi_cfgwrite(void *vsc __unused, int offset 
__unused, int size __unused,
        return (0);
 }
 
+/*
+ * LUN address parsing
+ *
+ * The LUN address consists of 8 bytes. While the spec describes this as 0x01,
+ * followed by the target byte, followed by a "single-level LUN structure",
+ * this is actually the same as a hierarchical LUN address as defined by SAM-5,
+ * consisting of four levels of addressing, where in each level the two MSB of
+ * byte 0 select the address mode used in the remaining bits and bytes.
+ *
+ *
+ * Only the first two levels are acutally used by virtio-scsi:
+ *
+ * Level 1: 0x01, 0xTT: Peripheral Device Addressing: Bus 1, Target 0-255
+ * Level 2: 0xLL, 0xLL: Peripheral Device Addressing: Bus MBZ, LUN 0-255
+ *                  or: Flat Space Addressing: LUN (0-16383)
+ * Level 3 and 4: not used, MBZ
+ *
+ * Currently, we only support Target 0.
+ *
+ * Alternatively, the first level may contain an extended LUN address to select
+ * the REPORT_LUNS well-known logical unit:
+ *
+ * Level 1: 0xC1, 0x01: Extended LUN Adressing, Well-Known LUN 1 (REPORT_LUNS)
+ * Level 2, 3, and 4: not used, MBZ
+ *
+ * The virtio spec says that we SHOULD implement the REPORT_LUNS well-known
+ * logical unit  but we currently don't.
+ *
+ * According to the virtio spec, these are the only LUNS address formats to be
+ * used with virtio-scsi.
+ */
+
+/*
+ * Check that the given LUN address conforms to the virtio spec, does not
+ * address an unknown target, and especially does not address the REPORT_LUNS
+ * well-known logical unit.
+ */
+static inline bool
+pci_vtscsi_check_lun(const uint8_t *lun)
+{
+       if (lun[0] == 0xC1)
+               return (false);
+
+       if (lun[0] != 0x01)
+               return (false);
+
+       if (lun[1] != 0x00)
+               return (false);
+
+       if (lun[2] != 0x00 && (lun[2] & 0xc0) != 0x40)
+               return (false);
+
+       if (lun[4] != 0 || lun[5] != 0 || lun[6] != 0 || lun[7] != 0)
+               return (false);
+
+       return (true);
+}
+
+/*
+ * Get the LUN id from a LUN address.
+ *
+ * Every code path using this function must have called pci_vtscsi_check_lun()
+ * before to make sure the LUN address is valid.
+ */
 static inline int
-pci_vtscsi_get_lun(uint8_t *lun)
+pci_vtscsi_get_lun(const uint8_t *lun)
 {
+       assert(lun[0] == 0x01);
+       assert(lun[1] == 0x00);
+       assert(lun[2] == 0x00 || (lun[2] & 0xc0) == 0x40);
 
        return (((lun[2] << 8) | lun[3]) & 0x3fff);
 }
@@ -444,6 +514,16 @@ pci_vtscsi_tmf_handle(struct pci_vtscsi_softc *sc,
        union ctl_io *io;
        int err;
 
+       if (pci_vtscsi_check_lun(tmf->lun) == false) {
+               DPRINTF("TMF request to invalid LUN %.2hhx%.2hhx-%.2hhx%.2hhx-"
+                   "%.2hhx%.2hhx-%.2hhx%.2hhx", tmf->lun[0], tmf->lun[1],
+                   tmf->lun[2], tmf->lun[3], tmf->lun[4], tmf->lun[5],
+                   tmf->lun[6], tmf->lun[7]);
+
+               tmf->response = VIRTIO_SCSI_S_BAD_TARGET;
+               return;
+       }
+
        io = ctl_scsi_alloc_io(sc->vss_iid);
        if (io == NULL) {
                WPRINTF("failed to allocate ctl_io: err=%d (%s)",
@@ -670,6 +750,19 @@ pci_vtscsi_queue_request(struct pci_vtscsi_softc *sc, 
struct vqueue_info *vq)
        assert(iov_to_buf(req->vsr_iov_in, req->vsr_niov_in,
            (void **)&req->vsr_cmd_rd) == VTSCSI_IN_HEADER_LEN(q->vsq_sc));
 
+       /* Make sure this request addresses a valid LUN. */
+       if (pci_vtscsi_check_lun(req->vsr_cmd_rd->lun) == false) {
+               DPRINTF("I/O request to invalid LUN "
+                   "%.2hhx%.2hhx-%.2hhx%.2hhx-%.2hhx%.2hhx-%.2hhx%.2hhx",
+                   req->vsr_cmd_rd->lun[0], req->vsr_cmd_rd->lun[1],
+                   req->vsr_cmd_rd->lun[2], req->vsr_cmd_rd->lun[3],
+                   req->vsr_cmd_rd->lun[4], req->vsr_cmd_rd->lun[5],
+                   req->vsr_cmd_rd->lun[6], req->vsr_cmd_rd->lun[7]);
+               req->vsr_cmd_wr->response = VIRTIO_SCSI_S_BAD_TARGET;
+               pci_vtscsi_return_request(q, req, 1);
+               return;
+       }
+
        pthread_mutex_lock(&q->vsq_rmtx);
        pci_vtscsi_put_request(&q->vsq_requests, req);
        pthread_cond_signal(&q->vsq_cv);

Reply via email to