summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Cave-Ayland <mark.cave-ayland@ilande.co.uk>2022-07-30 13:26:56 +0100
committerPaolo Bonzini <pbonzini@redhat.com>2022-08-01 15:22:39 +0200
commit55794c904df723109b228da28b5db778e0df3110 (patch)
tree38c20583260471c78820164d2c4e63a656cf9236
parent54a53a006ed9c1fe027fd89045d6de1e9128d7f4 (diff)
scsi-disk: ensure block size is non-zero and changes limited to bits 8-15
The existing code assumes that the block size can be generated from p[1] << 8 in multiple places which ignores the top and bottom 8 bits. If the block size is allowed to be set to an arbitrary value then this causes a mismatch between the value written by the guest in the block descriptor and the value subsequently read back using READ CAPACITY causing the guest to generate requests that can crash QEMU. For now restrict block size changes to bits 8-15 and also ignore requests to set the block size to 0 which causes the SCSI emulation to crash in at least one place with a divide by zero error. Fixes: 356c4c441e ("scsi-disk: allow MODE SELECT block descriptor to set the block size") Closes: https://gitlab.com/qemu-project/qemu/-/issues/1112 Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Message-Id: <20220730122656.253448-3-mark.cave-ayland@ilande.co.uk> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-rw-r--r--hw/scsi/scsi-disk.c18
1 files changed, 14 insertions, 4 deletions
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 3027ac3b1e..efee6739f9 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -1591,7 +1591,7 @@ static void scsi_disk_emulate_mode_select(SCSIDiskReq *r, uint8_t *inbuf)
int cmd = r->req.cmd.buf[0];
int len = r->req.cmd.xfer;
int hdr_len = (cmd == MODE_SELECT ? 4 : 8);
- int bd_len;
+ int bd_len, bs;
int pass;
if ((r->req.cmd.buf[1] & 0x11) != 0x10) {
@@ -1617,9 +1617,19 @@ static void scsi_disk_emulate_mode_select(SCSIDiskReq *r, uint8_t *inbuf)
}
/* Allow changing the block size */
- if (bd_len && p[6] != (s->qdev.blocksize >> 8)) {
- s->qdev.blocksize = p[6] << 8;
- trace_scsi_disk_mode_select_set_blocksize(s->qdev.blocksize);
+ if (bd_len) {
+ bs = p[5] << 16 | p[6] << 8 | p[7];
+
+ /*
+ * Since the existing code only checks/updates bits 8-15 of the block
+ * size, restrict ourselves to the same requirement for now to ensure
+ * that a block size set by a block descriptor and then read back by
+ * a subsequent SCSI command will be the same
+ */
+ if (bs && !(bs & ~0xff00) && bs != s->qdev.blocksize) {
+ s->qdev.blocksize = bs;
+ trace_scsi_disk_mode_select_set_blocksize(s->qdev.blocksize);
+ }
}
len -= bd_len;