26ba25
From 00c5ee2a99e03dd349061981d6560975ab9707d0 Mon Sep 17 00:00:00 2001
26ba25
From: Paolo Bonzini <pbonzini@redhat.com>
26ba25
Date: Thu, 20 Dec 2018 12:31:02 +0000
26ba25
Subject: [PATCH 7/8] scsi-generic: avoid invalid access to struct when
26ba25
 emulating block limits
26ba25
MIME-Version: 1.0
26ba25
Content-Type: text/plain; charset=UTF-8
26ba25
Content-Transfer-Encoding: 8bit
26ba25
26ba25
RH-Author: Paolo Bonzini <pbonzini@redhat.com>
26ba25
Message-id: <20181220123103.29579-8-pbonzini@redhat.com>
26ba25
Patchwork-id: 83718
26ba25
O-Subject: [PATCH 7/8] scsi-generic: avoid invalid access to struct when emulating block limits
26ba25
Bugzilla: 1639957
26ba25
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
26ba25
RH-Acked-by: Philippe Mathieu-Daudé <philmd@redhat.com>
26ba25
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
26ba25
RH-Acked-by: Laurent Vivier <lvivier@redhat.com>
26ba25
26ba25
Emulation of the block limits VPD page called back into scsi-disk.c,
26ba25
which however expected the request to be for a SCSIDiskState and
26ba25
accessed a scsi-generic device outside the bounds of its struct
26ba25
(namely to retrieve s->max_unmap_size and s->max_io_size).
26ba25
26ba25
To avoid this, move the emulation code to a separate function that
26ba25
takes a new SCSIBlockLimits struct and marshals it into the VPD
26ba25
response format.
26ba25
26ba25
Reported-by: Max Reitz <mreitz@redhat.com>
26ba25
Reviewed-by: Max Reitz <mreitz@redhat.com>
26ba25
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
26ba25
(cherry picked from commit 3d4a8bf0eed68a781e06118e4d1df6e2f106a1f2)
26ba25
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
26ba25
---
26ba25
 hw/scsi/Makefile.objs       |  2 +-
26ba25
 hw/scsi/emulation.c         | 42 +++++++++++++++++++++
26ba25
 hw/scsi/scsi-disk.c         | 92 ++++++++++-----------------------------------
26ba25
 hw/scsi/scsi-generic.c      | 35 ++++++++++++-----
26ba25
 include/hw/scsi/emulation.h | 16 ++++++++
26ba25
 include/hw/scsi/scsi.h      |  1 -
26ba25
 6 files changed, 104 insertions(+), 84 deletions(-)
26ba25
 create mode 100644 hw/scsi/emulation.c
26ba25
 create mode 100644 include/hw/scsi/emulation.h
26ba25
26ba25
diff --git a/hw/scsi/Makefile.objs b/hw/scsi/Makefile.objs
26ba25
index b188f72..c4f249a 100644
26ba25
--- a/hw/scsi/Makefile.objs
26ba25
+++ b/hw/scsi/Makefile.objs
26ba25
@@ -1,4 +1,4 @@
26ba25
-common-obj-y += scsi-disk.o
26ba25
+common-obj-y += scsi-disk.o emulation.o
26ba25
 common-obj-y += scsi-generic.o scsi-bus.o
26ba25
 common-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o
26ba25
 common-obj-$(CONFIG_MPTSAS_SCSI_PCI) += mptsas.o mptconfig.o mptendian.o
26ba25
diff --git a/hw/scsi/emulation.c b/hw/scsi/emulation.c
26ba25
new file mode 100644
26ba25
index 0000000..06d62f3
26ba25
--- /dev/null
26ba25
+++ b/hw/scsi/emulation.c
26ba25
@@ -0,0 +1,42 @@
26ba25
+#include "qemu/osdep.h"
26ba25
+#include "qemu/units.h"
26ba25
+#include "qemu/bswap.h"
26ba25
+#include "hw/scsi/emulation.h"
26ba25
+
26ba25
+int scsi_emulate_block_limits(uint8_t *outbuf, const SCSIBlockLimits *bl)
26ba25
+{
26ba25
+    /* required VPD size with unmap support */
26ba25
+    memset(outbuf, 0, 0x3c);
26ba25
+
26ba25
+    outbuf[0] = bl->wsnz; /* wsnz */
26ba25
+
26ba25
+    if (bl->max_io_sectors) {
26ba25
+        /* optimal transfer length granularity.  This field and the optimal
26ba25
+         * transfer length can't be greater than maximum transfer length.
26ba25
+         */
26ba25
+        stw_be_p(outbuf + 2, MIN(bl->min_io_size, bl->max_io_sectors));
26ba25
+
26ba25
+        /* maximum transfer length */
26ba25
+        stl_be_p(outbuf + 4, bl->max_io_sectors);
26ba25
+
26ba25
+        /* optimal transfer length */
26ba25
+        stl_be_p(outbuf + 8, MIN(bl->opt_io_size, bl->max_io_sectors));
26ba25
+    } else {
26ba25
+        stw_be_p(outbuf + 2, bl->min_io_size);
26ba25
+        stl_be_p(outbuf + 8, bl->opt_io_size);
26ba25
+    }
26ba25
+
26ba25
+    /* max unmap LBA count */
26ba25
+    stl_be_p(outbuf + 16, bl->max_unmap_sectors);
26ba25
+
26ba25
+    /* max unmap descriptors */
26ba25
+    stl_be_p(outbuf + 20, bl->max_unmap_descr);
26ba25
+
26ba25
+    /* optimal unmap granularity; alignment is zero */
26ba25
+    stl_be_p(outbuf + 24, bl->unmap_sectors);
26ba25
+
26ba25
+    /* max write same size, make it the same as maximum transfer length */
26ba25
+    stl_be_p(outbuf + 36, bl->max_io_sectors);
26ba25
+
26ba25
+    return 0x3c;
26ba25
+}
26ba25
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
26ba25
index b8a27d6..a20ef91 100644
26ba25
--- a/hw/scsi/scsi-disk.c
26ba25
+++ b/hw/scsi/scsi-disk.c
26ba25
@@ -32,6 +32,7 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
26ba25
 #include "qapi/error.h"
26ba25
 #include "qemu/error-report.h"
26ba25
 #include "hw/scsi/scsi.h"
26ba25
+#include "hw/scsi/emulation.h"
26ba25
 #include "scsi/constants.h"
26ba25
 #include "sysemu/sysemu.h"
26ba25
 #include "sysemu/block-backend.h"
26ba25
@@ -585,7 +586,7 @@ static uint8_t *scsi_get_buf(SCSIRequest *req)
26ba25
     return (uint8_t *)r->iov.iov_base;
26ba25
 }
26ba25
 
26ba25
-int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf)
26ba25
+static int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf)
26ba25
 {
26ba25
     SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
26ba25
     uint8_t page_code = req->cmd.buf[2];
26ba25
@@ -687,89 +688,36 @@ int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf)
26ba25
     }
26ba25
     case 0xb0: /* block limits */
26ba25
     {
26ba25
-        unsigned int unmap_sectors =
26ba25
-            s->qdev.conf.discard_granularity / s->qdev.blocksize;
26ba25
-        unsigned int min_io_size =
26ba25
-            s->qdev.conf.min_io_size / s->qdev.blocksize;
26ba25
-        unsigned int opt_io_size =
26ba25
-            s->qdev.conf.opt_io_size / s->qdev.blocksize;
26ba25
-        unsigned int max_unmap_sectors =
26ba25
-            s->max_unmap_size / s->qdev.blocksize;
26ba25
-        unsigned int max_io_sectors =
26ba25
-            s->max_io_size / s->qdev.blocksize;
26ba25
+        SCSIBlockLimits bl = {};
26ba25
 
26ba25
         if (s->qdev.type == TYPE_ROM) {
26ba25
             DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n",
26ba25
                     page_code);
26ba25
             return -1;
26ba25
         }
26ba25
+        bl.wsnz = 1;
26ba25
+        bl.unmap_sectors =
26ba25
+            s->qdev.conf.discard_granularity / s->qdev.blocksize;
26ba25
+        bl.min_io_size =
26ba25
+            s->qdev.conf.min_io_size / s->qdev.blocksize;
26ba25
+        bl.opt_io_size =
26ba25
+            s->qdev.conf.opt_io_size / s->qdev.blocksize;
26ba25
+        bl.max_unmap_sectors =
26ba25
+            s->max_unmap_size / s->qdev.blocksize;
26ba25
+        bl.max_io_sectors =
26ba25
+            s->max_io_size / s->qdev.blocksize;
26ba25
+        /* 255 descriptors fit in 4 KiB with an 8-byte header */
26ba25
+        bl.max_unmap_descr = 255;
26ba25
+
26ba25
         if (s->qdev.type == TYPE_DISK) {
26ba25
             int max_transfer_blk = blk_get_max_transfer(s->qdev.conf.blk);
26ba25
             int max_io_sectors_blk =
26ba25
                 max_transfer_blk / s->qdev.blocksize;
26ba25
 
26ba25
-            max_io_sectors =
26ba25
-                MIN_NON_ZERO(max_io_sectors_blk, max_io_sectors);
26ba25
-
26ba25
-            /* min_io_size and opt_io_size can't be greater than
26ba25
-             * max_io_sectors */
26ba25
-            if (min_io_size) {
26ba25
-                min_io_size = MIN(min_io_size, max_io_sectors);
26ba25
-            }
26ba25
-            if (opt_io_size) {
26ba25
-                opt_io_size = MIN(opt_io_size, max_io_sectors);
26ba25
-            }
26ba25
+            bl.max_io_sectors =
26ba25
+                MIN_NON_ZERO(max_io_sectors_blk, bl.max_io_sectors);
26ba25
         }
26ba25
-        /* required VPD size with unmap support */
26ba25
-        buflen = 0x40;
26ba25
-        memset(outbuf + 4, 0, buflen - 4);
26ba25
-
26ba25
-        outbuf[4] = 0x1; /* wsnz */
26ba25
-
26ba25
-        /* optimal transfer length granularity */
26ba25
-        outbuf[6] = (min_io_size >> 8) & 0xff;
26ba25
-        outbuf[7] = min_io_size & 0xff;
26ba25
-
26ba25
-        /* maximum transfer length */
26ba25
-        outbuf[8] = (max_io_sectors >> 24) & 0xff;
26ba25
-        outbuf[9] = (max_io_sectors >> 16) & 0xff;
26ba25
-        outbuf[10] = (max_io_sectors >> 8) & 0xff;
26ba25
-        outbuf[11] = max_io_sectors & 0xff;
26ba25
-
26ba25
-        /* optimal transfer length */
26ba25
-        outbuf[12] = (opt_io_size >> 24) & 0xff;
26ba25
-        outbuf[13] = (opt_io_size >> 16) & 0xff;
26ba25
-        outbuf[14] = (opt_io_size >> 8) & 0xff;
26ba25
-        outbuf[15] = opt_io_size & 0xff;
26ba25
-
26ba25
-        /* max unmap LBA count, default is 1GB */
26ba25
-        outbuf[20] = (max_unmap_sectors >> 24) & 0xff;
26ba25
-        outbuf[21] = (max_unmap_sectors >> 16) & 0xff;
26ba25
-        outbuf[22] = (max_unmap_sectors >> 8) & 0xff;
26ba25
-        outbuf[23] = max_unmap_sectors & 0xff;
26ba25
-
26ba25
-        /* max unmap descriptors, 255 fit in 4 kb with an 8-byte header */
26ba25
-        outbuf[24] = 0;
26ba25
-        outbuf[25] = 0;
26ba25
-        outbuf[26] = 0;
26ba25
-        outbuf[27] = 255;
26ba25
-
26ba25
-        /* optimal unmap granularity */
26ba25
-        outbuf[28] = (unmap_sectors >> 24) & 0xff;
26ba25
-        outbuf[29] = (unmap_sectors >> 16) & 0xff;
26ba25
-        outbuf[30] = (unmap_sectors >> 8) & 0xff;
26ba25
-        outbuf[31] = unmap_sectors & 0xff;
26ba25
-
26ba25
-        /* max write same size */
26ba25
-        outbuf[36] = 0;
26ba25
-        outbuf[37] = 0;
26ba25
-        outbuf[38] = 0;
26ba25
-        outbuf[39] = 0;
26ba25
-
26ba25
-        outbuf[40] = (max_io_sectors >> 24) & 0xff;
26ba25
-        outbuf[41] = (max_io_sectors >> 16) & 0xff;
26ba25
-        outbuf[42] = (max_io_sectors >> 8) & 0xff;
26ba25
-        outbuf[43] = max_io_sectors & 0xff;
26ba25
+        buflen += scsi_emulate_block_limits(outbuf + buflen, &bl);
26ba25
         break;
26ba25
     }
26ba25
     case 0xb1: /* block device characteristics */
26ba25
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
26ba25
index 6d6db69..f218cef 100644
26ba25
--- a/hw/scsi/scsi-generic.c
26ba25
+++ b/hw/scsi/scsi-generic.c
26ba25
@@ -16,6 +16,7 @@
26ba25
 #include "qemu-common.h"
26ba25
 #include "qemu/error-report.h"
26ba25
 #include "hw/scsi/scsi.h"
26ba25
+#include "hw/scsi/emulation.h"
26ba25
 #include "sysemu/block-backend.h"
26ba25
 #include "sysemu/blockdev.h"
26ba25
 
26ba25
@@ -182,7 +183,7 @@ static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s)
26ba25
             /* Also take care of the opt xfer len. */
26ba25
             stl_be_p(&r->buf[12],
26ba25
                     MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12])));
26ba25
-        } else if (page == 0x00 && s->needs_vpd_bl_emulation) {
26ba25
+        } else if (s->needs_vpd_bl_emulation && page == 0x00) {
26ba25
             /*
26ba25
              * Now we're capable of supplying the VPD Block Limits
26ba25
              * response if the hardware can't. Add it in the INQUIRY
26ba25
@@ -210,9 +211,24 @@ static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s)
26ba25
     }
26ba25
 }
26ba25
 
26ba25
-static int scsi_emulate_block_limits(SCSIGenericReq *r)
26ba25
+static int scsi_generic_emulate_block_limits(SCSIGenericReq *r, SCSIDevice *s)
26ba25
 {
26ba25
-    r->buflen = scsi_disk_emulate_vpd_page(&r->req, r->buf);
26ba25
+    int len;
26ba25
+    uint8_t buf[64];
26ba25
+
26ba25
+    SCSIBlockLimits bl = {
26ba25
+        .max_io_sectors = blk_get_max_transfer(s->conf.blk) / s->blocksize
26ba25
+    };
26ba25
+
26ba25
+    memset(r->buf, 0, r->buflen);
26ba25
+    stb_p(buf, s->type);
26ba25
+    stb_p(buf + 1, 0xb0);
26ba25
+    len = scsi_emulate_block_limits(buf + 4, &bl);
26ba25
+    assert(len <= sizeof(buf) - 4);
26ba25
+    stw_be_p(buf + 2, len);
26ba25
+
26ba25
+    memcpy(r->buf, buf, MIN(r->buflen, len + 4));
26ba25
+
26ba25
     r->io_header.sb_len_wr = 0;
26ba25
 
26ba25
     /*
26ba25
@@ -254,13 +270,12 @@ static void scsi_read_complete(void * opaque, int ret)
26ba25
      * resulted in sense error but would need emulation.
26ba25
      * In this case, emulate a valid VPD response.
26ba25
      */
26ba25
-    if (s->needs_vpd_bl_emulation) {
26ba25
-        int is_vpd_bl = r->req.cmd.buf[0] == INQUIRY &&
26ba25
-                         r->req.cmd.buf[1] & 0x01 &&
26ba25
-                         r->req.cmd.buf[2] == 0xb0;
26ba25
-
26ba25
-        if (is_vpd_bl && sg_io_sense_from_errno(-ret, &r->io_header, &sense)) {
26ba25
-            len = scsi_emulate_block_limits(r);
26ba25
+    if (s->needs_vpd_bl_emulation &&
26ba25
+        r->req.cmd.buf[0] == INQUIRY &&
26ba25
+        (r->req.cmd.buf[1] & 0x01) &&
26ba25
+        r->req.cmd.buf[2] == 0xb0) {
26ba25
+        if (sg_io_sense_from_errno(-ret, &r->io_header, &sense)) {
26ba25
+            len = scsi_generic_emulate_block_limits(r, s);
26ba25
             /*
26ba25
              * No need to let scsi_read_complete go on and handle an
26ba25
              * INQUIRY VPD BL request we created manually.
26ba25
diff --git a/include/hw/scsi/emulation.h b/include/hw/scsi/emulation.h
26ba25
new file mode 100644
26ba25
index 0000000..09fba1f
26ba25
--- /dev/null
26ba25
+++ b/include/hw/scsi/emulation.h
26ba25
@@ -0,0 +1,16 @@
26ba25
+#ifndef HW_SCSI_EMULATION_H
26ba25
+#define HW_SCSI_EMULATION_H 1
26ba25
+
26ba25
+typedef struct SCSIBlockLimits {
26ba25
+    bool wsnz;
26ba25
+    uint16_t min_io_size;
26ba25
+    uint32_t max_unmap_descr;
26ba25
+    uint32_t opt_io_size;
26ba25
+    uint32_t max_unmap_sectors;
26ba25
+    uint32_t unmap_sectors;
26ba25
+    uint32_t max_io_sectors;
26ba25
+} SCSIBlockLimits;
26ba25
+
26ba25
+int scsi_emulate_block_limits(uint8_t *outbuf, const SCSIBlockLimits *bl);
26ba25
+
26ba25
+#endif
26ba25
diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h
26ba25
index ee3a411..acef25f 100644
26ba25
--- a/include/hw/scsi/scsi.h
26ba25
+++ b/include/hw/scsi/scsi.h
26ba25
@@ -189,7 +189,6 @@ void scsi_device_report_change(SCSIDevice *dev, SCSISense sense);
26ba25
 void scsi_device_unit_attention_reported(SCSIDevice *dev);
26ba25
 void scsi_generic_read_device_inquiry(SCSIDevice *dev);
26ba25
 int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed);
26ba25
-int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf);
26ba25
 int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size,
26ba25
                         uint8_t *buf, uint8_t buf_size);
26ba25
 SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun);
26ba25
-- 
26ba25
1.8.3.1
26ba25