@samitouri / QOSamiQemu / commits / d30254aec9

scsi-disk: fix out-of-bound read in WRITE SAME

A guest with an attached scsi-hd can force QEMU's SCSI disk emulation to read roughly 60 KiB past the end of a heap buffer, copying that out of bounds host memory into the guest's own disk image. WRITE SAME computes the request transfer length at dev->blocksize when the request is prepared and sets cmd->xfer from dev->blocksize. scsi_disk_emulate_command() then uses cmd->xfer as the size of the request buffer. However, MODE SELECT can race with the WRITE SAME command and guest raise the logical block size to any value whose low bits fit 0xfe00, up to 65024. In the presence of this race, scsi_disk_emulate_write_same() will read from memory as many bytes as indicated by the *new* dev->blocksize, and write it to disk. The read length in WRITE SAME must be bounded by the buffer that was actually allocated, not by the mutable s->qdev.blocksize, so clamp the length used against inbuf to r->buflen. Re-validating req->cmd.xfer against the current block size would not work because the race is intrinsic in the SCSI protocol. I am not sure if this is exploitable with virtio-scsi and other SG-capable HBAs, because it should process the WRITE SAME input immediately, without letting the MODE SELECT command race with it. Fixes: 356c4c441ec ("scsi-disk: allow MODE SELECT block descriptor to set the block size", 2022-07-13) Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4365 Cc: qemu-stable@nongnu.org Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

Paolo Bonzini committed Aug 26, 2026 at 19:22 UTC d30254aec93141d6b89262986a62764b2ae07177
1 file changed +3 -2
hw/scsi/scsi-disk.c
+3 -2
@@ -1911,6 +1911,7 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf)
1911 SCSIRequest *req = &r->req;
1912 SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
1913 uint32_t nb_sectors = scsi_data_cdb_xfer(r->req.cmd.buf);
1914 + uint32_t buflen = MIN(s->qdev.blocksize, r->buflen);
1915 WriteSameCBData *data;
1916 uint8_t *buf;
1917 int i, l;
@@ -1930,7 +1931,7 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf)
1931 return;
1932 }
1933
1933 - if ((req->cmd.buf[1] & 0x1) || buffer_is_zero(inbuf, s->qdev.blocksize)) {
1934 + if ((req->cmd.buf[1] & 0x1) || buffer_is_zero(inbuf, buflen)) {
1935 int flags = (req->cmd.buf[1] & 0x8) ? BDRV_REQ_MAY_UNMAP : 0;
1936
1937 /* The request is used as the AIO opaque value, so add a ref. */
@@ -1956,7 +1957,7 @@ static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf)
1957 qemu_iovec_init_external(&data->qiov, &data->iov, 1);
1958
1959 for (i = 0; i < data->iov.iov_len; i += l) {
1959 - l = MIN(s->qdev.blocksize, data->iov.iov_len - i);
1960 + l = MIN(buflen, data->iov.iov_len - i);
1961 memcpy(&buf[i], inbuf, l);
1962 }
1963