virtio-scsi: pass the same cdb_size to virtio_scsi_pop_req and virtio_scsi_handle_cmd_req_prepare
Ensure that there is no allocation/usage mismatch when requests are processed in virtio_scsi_handle_cmd_vq. To do this, retrieve the value once and pass it to both functions. For other calls to virtio_scsi_pop_req the extra size can be 0, because control and event requests fit entirely in VirtIOSCSIReq. Reported-by: Jihe Wang <wangjihe.mail@gmail.com> Tested-by: Jihe Wang <wangjihe.mail@gmail.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Fixes: CVE-2026-5763 Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Paolo Bonzini committed
Mar 27, 2026 at 22:00 UTC
79971302935472232a68073faddb085177e3ca54
1 file changed
+15
-11
hw/scsi/virtio-scsi.c
+15
-11
@@ -227,16 +227,16 @@ static int virtio_scsi_parse_req(VirtIOSCSIReq *req,
227
return 0;
228
}
229
230
-static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq, QemuMutex *vq_lock)
230
+static VirtIOSCSIReq *virtio_scsi_pop_req(VirtIOSCSI *s, VirtQueue *vq, size_t extra_req_size,
231
+ QemuMutex *vq_lock)
232
{
232
- VirtIOSCSICommon *vs = (VirtIOSCSICommon *)s;
233
VirtIOSCSIReq *req;
234
235
if (vq_lock) {
236
qemu_mutex_lock(vq_lock);
237
}
238
239
- req = virtqueue_pop(vq, sizeof(VirtIOSCSIReq) + vs->cdb_size);
239
+ req = virtqueue_pop(vq, sizeof(VirtIOSCSIReq) + extra_req_size);
240
241
if (vq_lock) {
242
qemu_mutex_unlock(vq_lock);
@@ -682,7 +682,7 @@ static void virtio_scsi_handle_ctrl_vq(VirtIOSCSI *s, VirtQueue *vq)
682
{
683
VirtIOSCSIReq *req;
684
685
- while ((req = virtio_scsi_pop_req(s, vq, &s->ctrl_lock))) {
685
+ while ((req = virtio_scsi_pop_req(s, vq, 0, &s->ctrl_lock))) {
686
virtio_scsi_handle_ctrl_req(s, req);
687
}
688
}
@@ -850,13 +850,14 @@ static void virtio_scsi_fail_cmd_req(VirtIOSCSIReq *req)
850
virtio_scsi_complete_cmd_req(req);
851
}
852
853
-static int virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req)
853
+static int virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req,
854
+ size_t cdb_size)
855
{
856
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
857
SCSIDevice *d;
858
int rc;
859
859
- rc = virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + vs->cdb_size,
860
+ rc = virtio_scsi_parse_req(req, sizeof(VirtIOSCSICmdReq) + cdb_size,
861
sizeof(VirtIOSCSICmdResp) + vs->sense_size);
862
if (rc < 0) {
863
if (rc == -ENOTSUP) {
@@ -878,7 +879,7 @@ static int virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req)
879
}
880
req->sreq = scsi_req_new(d, req->req.cmd.tag,
881
virtio_scsi_get_lun(req->req.cmd.lun),
881
- req->req.cmd.cdb, vs->cdb_size, req);
882
+ req->req.cmd.cdb, cdb_size, req);
883
884
if (req->sreq->cmd.mode != SCSI_XFER_NONE
885
&& (req->sreq->cmd.mode != req->mode ||
@@ -913,12 +914,15 @@ static void virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq)
914
QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs);
915
916
do {
917
+ VirtIOSCSICommon *vs = (VirtIOSCSICommon *)s;
918
+ size_t cdb_size = qatomic_read(&vs->cdb_size);
919
+
920
if (suppress_notifications) {
921
virtio_queue_set_notification(vq, 0);
922
}
923
920
- while ((req = virtio_scsi_pop_req(s, vq, NULL))) {
921
- ret = virtio_scsi_handle_cmd_req_prepare(s, req);
924
+ while ((req = virtio_scsi_pop_req(s, vq, cdb_size, NULL))) {
925
+ ret = virtio_scsi_handle_cmd_req_prepare(s, req, cdb_size);
926
if (!ret) {
927
QTAILQ_INSERT_TAIL(&reqs, req, next);
928
} else if (ret == -EINVAL) {
@@ -989,7 +993,7 @@ static void virtio_scsi_set_config(VirtIODevice *vdev,
993
}
994
995
vs->sense_size = virtio_ldl_p(vdev, &scsiconf->sense_size);
992
- vs->cdb_size = virtio_ldl_p(vdev, &scsiconf->cdb_size);
996
+ qatomic_set(&vs->cdb_size, virtio_ldl_p(vdev, &scsiconf->cdb_size));
997
}
998
999
static uint64_t virtio_scsi_get_features(VirtIODevice *vdev,
@@ -1050,7 +1054,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s,
1054
return;
1055
}
1056
1053
- req = virtio_scsi_pop_req(s, vs->event_vq, &s->event_lock);
1057
+ req = virtio_scsi_pop_req(s, vs->event_vq, 0, &s->event_lock);
1058
WITH_QEMU_LOCK_GUARD(&s->event_lock) {
1059
if (!req) {
1060
s->events_dropped = true;