Instead of using a static buffer for inquiry data we should
rather use the command-provided buffer and implement proper
bounds checking when writing to it.
Inquiry is by no means time-critical ...

Signed-off-by: Hannes Reinecke <h...@suse.de>
---
 drivers/target/target_core_spc.c     | 391 +++++++++++++++++++++--------------
 include/target/target_core_backend.h |   2 +-
 2 files changed, 235 insertions(+), 158 deletions(-)

diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c
index f9889fd..942f72e 100644
--- a/drivers/target/target_core_spc.c
+++ b/drivers/target/target_core_spc.c
@@ -71,6 +71,10 @@ spc_emulate_inquiry_std(struct se_cmd *cmd, unsigned char 
*buf)
 {
        struct se_lun *lun = cmd->se_lun;
        struct se_device *dev = cmd->se_dev;
+       int len;
+
+       if (cmd->data_length < 6)
+               return TCM_INVALID_CDB_FIELD;
 
        /* Set RMB (removable media) for tape devices */
        if (dev->transport->get_device_type(dev) == TYPE_TAPE)
@@ -101,14 +105,27 @@ spc_emulate_inquiry_std(struct se_cmd *cmd, unsigned char 
*buf)
        if (dev->dev_attrib.emulate_3pc)
                buf[5] |= 0x8;
 
-       buf[7] = 0x2; /* CmdQue=1 */
+       if (cmd->data_length > 7)
+               buf[7] = 0x2; /* CmdQue=1 */
+
+       if (cmd->data_length > 8) {
+               len = min_t(size_t, cmd->data_length - 8, 8);
+               memset(&buf[8], 0x20, len);
+               memcpy(&buf[8], "LIO-ORG", len);
+       }
+       if (cmd->data_length > 16) {
+               memset(&buf[16], 0x20,
+                      min_t(size_t, cmd->data_length - 16, 16));
+               len = min_t(size_t, strlen(dev->t10_wwn.model), 16);
+               memcpy(&buf[16], dev->t10_wwn.model,
+                      min_t(size_t, len, cmd->data_length - 16));
+       }
+       if (cmd->data_length > 32) {
+               len = min_t(size_t, strlen(dev->t10_wwn.revision), 4);
+               memcpy(&buf[32], dev->t10_wwn.revision,
+                      min_t(size_t, len, cmd->data_length - 32));
+       }
 
-       memcpy(&buf[8], "LIO-ORG ", 8);
-       memset(&buf[16], 0x20, 16);
-       memcpy(&buf[16], dev->t10_wwn.model,
-              min_t(size_t, strlen(dev->t10_wwn.model), 16));
-       memcpy(&buf[32], dev->t10_wwn.revision,
-              min_t(size_t, strlen(dev->t10_wwn.revision), 4));
        buf[4] = 31; /* Set additional length to 31 */
 
        return 0;
@@ -117,32 +134,53 @@ EXPORT_SYMBOL(spc_emulate_inquiry_std);
 
 /* unit serial number */
 static sense_reason_t
-spc_emulate_evpd_80(struct se_cmd *cmd, unsigned char *buf)
+spc_emulate_evpd_80(struct se_cmd *cmd, unsigned char *buf, int buf_len)
 {
        struct se_device *dev = cmd->se_dev;
        u16 len = 0;
 
+       if (buf_len < 4)
+               return TCM_INVALID_CDB_FIELD;
+
        if (dev->dev_flags & DF_EMULATED_VPD_UNIT_SERIAL) {
                u32 unit_serial_len;
 
                unit_serial_len = strlen(dev->t10_wwn.unit_serial);
                unit_serial_len++; /* For NULL Terminator */
 
-               len += sprintf(&buf[4], "%s", dev->t10_wwn.unit_serial);
+               if (buf_len > unit_serial_len + 5)
+                       len += sprintf(&buf[4], "%s",
+                                      dev->t10_wwn.unit_serial);
                len++; /* Extra Byte for NULL Terminator */
                buf[3] = len;
        }
        return 0;
 }
 
-void spc_parse_naa_6h_vendor_specific(struct se_device *dev,
-                                     unsigned char *buf)
+int spc_parse_naa_6h_vendor_specific(struct se_device *dev,
+                                     unsigned char *buf, int buf_len)
 {
        unsigned char *p = &dev->t10_wwn.unit_serial[0];
-       int cnt;
+       int cnt = 0, end = buf_len;
        bool next = true;
 
        /*
+        * NAA IEEE Registered Extended Identifier/Designator
+        */
+       if (cnt < end)
+               buf[cnt++] = 0x6 << 4;
+
+       /*
+        * Use OpenFabrics IEEE Company ID: 00 14 05
+        */
+       if (cnt < end)
+               buf[cnt++] |= 0x01;
+       if (cnt < end)
+               buf[cnt++] = 0x40;
+       if (cnt < end)
+               buf[cnt] = 0x5 << 4;
+
+       /*
         * Generate up to 36 bits of VENDOR SPECIFIC IDENTIFIER starting on
         * byte 3 bit 3-0 for NAA IEEE Registered Extended DESIGNATOR field
         * format, followed by 64 bits of VENDOR SPECIFIC IDENTIFIER EXTENSION
@@ -150,7 +188,9 @@ void spc_parse_naa_6h_vendor_specific(struct se_device *dev,
         * NUMBER set via vpd_unit_serial in target_core_configfs.c to ensure
         * per device uniqeness.
         */
-       for (cnt = 0; *p && cnt < 13; p++) {
+       if (end > 16)
+               end = 16;
+       for (; *p && cnt < end; p++) {
                int val = hex_to_bin(*p);
 
                if (val < 0)
@@ -158,20 +198,24 @@ void spc_parse_naa_6h_vendor_specific(struct se_device 
*dev,
 
                if (next) {
                        next = false;
-                       buf[cnt++] |= val;
+                       buf[cnt++] |= (val & 0xf);
                } else {
                        next = true;
-                       buf[cnt] = val << 4;
+                       buf[cnt] = (val & 0xf) << 4;
                }
        }
+       return cnt;
 }
 
+#define SET_VPD_DATA(b,l,o,d)                  \
+       if ((l) > (o)) b[o] = d; (o)++;
+
 /*
  * Device identification VPD, for a complete list of
  * DESIGNATOR TYPEs see spc4r17 Table 459.
  */
 sense_reason_t
-spc_emulate_evpd_83(struct se_cmd *cmd, unsigned char *buf)
+spc_emulate_evpd_83(struct se_cmd *cmd, unsigned char *buf, int buf_len)
 {
        struct se_device *dev = cmd->se_dev;
        struct se_lun *lun = cmd->se_lun;
@@ -181,9 +225,12 @@ spc_emulate_evpd_83(struct se_cmd *cmd, unsigned char *buf)
        struct t10_alua_tg_pt_gp *tg_pt_gp;
        struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem;
        unsigned char *prod = &dev->t10_wwn.model[0];
-       u32 prod_len;
-       u32 unit_serial_len, off = 0;
-       u16 len = 0, id_len;
+       u16 off, id_len;
+       size_t len;
+       int serial_off;
+
+       if (buf_len < 4)
+               return TCM_INVALID_CDB_FIELD;
 
        off = 4;
 
@@ -199,68 +246,61 @@ spc_emulate_evpd_83(struct se_cmd *cmd, unsigned char 
*buf)
                goto check_t10_vend_desc;
 
        /* CODE SET == Binary */
-       buf[off++] = 0x1;
+       SET_VPD_DATA(buf, buf_len, off, 0x1);
 
        /* Set ASSOCIATION == addressed logical unit: 0)b */
-       buf[off] = 0x00;
-
        /* Identifier/Designator type == NAA identifier */
-       buf[off++] |= 0x3;
+       SET_VPD_DATA(buf, buf_len, off, 0x3);
        off++;
 
        /* Identifier/Designator length */
-       buf[off++] = 0x10;
-
-       /*
-        * Start NAA IEEE Registered Extended Identifier/Designator
-        */
-       buf[off++] = (0x6 << 4);
-
-       /*
-        * Use OpenFabrics IEEE Company ID: 00 14 05
-        */
-       buf[off++] = 0x01;
-       buf[off++] = 0x40;
-       buf[off] = (0x5 << 4);
+       SET_VPD_DATA(buf, buf_len, off, 0x10);
 
        /*
         * Return ConfigFS Unit Serial Number information for
         * VENDOR_SPECIFIC_IDENTIFIER and
         * VENDOR_SPECIFIC_IDENTIFIER_EXTENTION
         */
-       spc_parse_naa_6h_vendor_specific(dev, &buf[off]);
-
-       len = 20;
-       off = (len + 4);
+       off += spc_parse_naa_6h_vendor_specific(dev, &buf[off],
+                                               buf_len - off);
 
 check_t10_vend_desc:
        /*
         * T10 Vendor Identifier Page, see spc4r17 section 7.7.3.4
         */
-       id_len = 8; /* For Vendor field */
-       prod_len = 4; /* For VPD Header */
-       prod_len += 8; /* For Vendor field */
-       prod_len += strlen(prod);
-       prod_len++; /* For : */
+       /* Vendor field: 8 characters */
+       id_len = buf_len - off - 4 > 8 ? 8 : buf_len - off - 4;
+
+       len = min_t(size_t, strlen(prod), buf_len - off - 12);
+       if (buf_len > off + 12)
+               memcpy(&buf[off + 12], prod, len);
+
+       id_len += len;
+       serial_off = off + 12 + len;
 
        if (dev->dev_flags & DF_EMULATED_VPD_UNIT_SERIAL) {
-               unit_serial_len = strlen(&dev->t10_wwn.unit_serial[0]);
-               unit_serial_len++; /* For NULL Terminator */
+               buf[serial_off] = ':';
+               serial_off++;
+               len = min_t(size_t, buf_len - serial_off,
+                           strlen(&dev->t10_wwn.unit_serial[0]));
+               if (len > 0)
+                       memcpy(&buf[serial_off],
+                              &dev->t10_wwn.unit_serial[0], len);
+               id_len += len;
+       }
 
-               id_len += sprintf(&buf[off+12], "%s:%s", prod,
-                               &dev->t10_wwn.unit_serial[0]);
+       SET_VPD_DATA(buf, buf_len, off, 0x2); /* ASCII */
+       SET_VPD_DATA(buf, buf_len, off, 0x1); /* T10 Vendor ID */
+       SET_VPD_DATA(buf, buf_len, off, 0x0);
+       SET_VPD_DATA(buf, buf_len, off, id_len);/* Identifier Length */
+       if (buf_len > off) {
+               memset(&buf[off], 0x20, id_len > 8 ? 8 : id_len);
+               memcpy(&buf[off], "LIO-ORG", id_len > 8 ? 8 : id_len);
        }
-       buf[off] = 0x2; /* ASCII */
-       buf[off+1] = 0x1; /* T10 Vendor ID */
-       buf[off+2] = 0x0;
-       memcpy(&buf[off+4], "LIO-ORG", 8);
-       /* Extra Byte for NULL Terminator */
-       id_len++;
-       /* Identifier Length */
-       buf[off+3] = id_len;
+
        /* Header size for Designation descriptor */
-       len += (id_len + 4);
-       off += (id_len + 4);
+       off += id_len;
+
        /*
         * struct se_port is only set for INQUIRY VPD=1 through $FABRIC_MOD
         */
@@ -271,6 +311,7 @@ check_t10_vend_desc:
                u16 lu_gp_id = 0;
                u16 tg_pt_gp_id = 0;
                u16 tpgt;
+               u8 d;
 
                tpg = port->sep_tpg;
                /*
@@ -280,22 +321,22 @@ check_t10_vend_desc:
                 * Get the PROTOCOL IDENTIFIER as defined by spc4r17
                 * section 7.5.1 Table 362
                 */
-               buf[off] =
-                       (tpg->se_tpg_tfo->get_fabric_proto_ident(tpg) << 4);
-               buf[off++] |= 0x1; /* CODE SET == Binary */
-               buf[off] = 0x80; /* Set PIV=1 */
+               d = tpg->se_tpg_tfo->get_fabric_proto_ident(tpg) << 4;
+               d |= 0x1; /* CODE SET == Binary */;
+               SET_VPD_DATA(buf, buf_len, off, d);
+               d =  0x80; /* Set PIV=1 */
                /* Set ASSOCIATION == target port: 01b */
-               buf[off] |= 0x10;
+               d |= 0x10;
                /* DESIGNATOR TYPE == Relative target port identifer */
-               buf[off++] |= 0x4;
-               off++; /* Skip over Reserved */
-               buf[off++] = 4; /* DESIGNATOR LENGTH */
+               d |= 0x4;
+               SET_VPD_DATA(buf, buf_len, off, d);
+               off ++; /* Skip over Reserved */
+               SET_VPD_DATA(buf, buf_len, off, 4); /* DESIGNATOR LENGTH */
                /* Skip over Obsolete field in RTPI payload
                 * in Table 472 */
                off += 2;
-               buf[off++] = ((port->sep_rtpi >> 8) & 0xff);
-               buf[off++] = (port->sep_rtpi & 0xff);
-               len += 8; /* Header size + Designation descriptor */
+               SET_VPD_DATA(buf, buf_len, off, (port->sep_rtpi >> 8) & 0xff);
+               SET_VPD_DATA(buf, buf_len, off, port->sep_rtpi & 0xff);
                /*
                 * Target port group identifier, see spc4r17
                 * section 7.7.3.8
@@ -316,20 +357,20 @@ check_t10_vend_desc:
                tg_pt_gp_id = tg_pt_gp->tg_pt_gp_id;
                spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
 
-               buf[off] =
-                       (tpg->se_tpg_tfo->get_fabric_proto_ident(tpg) << 4);
-               buf[off++] |= 0x1; /* CODE SET == Binary */
-               buf[off] = 0x80; /* Set PIV=1 */
+               d = tpg->se_tpg_tfo->get_fabric_proto_ident(tpg) << 4;
+               d |= 0x1; /* CODE SET == Binary */
+               SET_VPD_DATA(buf, buf_len, off, d);
+               d = 0x80; /* Set PIV=1 */
                /* Set ASSOCIATION == target port: 01b */
-               buf[off] |= 0x10;
+               d |= 0x10;
                /* DESIGNATOR TYPE == Target port group identifier */
-               buf[off++] |= 0x5;
-               off++; /* Skip over Reserved */
-               buf[off++] = 4; /* DESIGNATOR LENGTH */
+               d |= 0x5;
+               SET_VPD_DATA(buf, buf_len, off, d);
+               off ++; /* Skip over Reserved */
+               SET_VPD_DATA(buf, buf_len, off, 4); /* DESIGNATOR LENGTH */
                off += 2; /* Skip over Reserved Field */
-               buf[off++] = ((tg_pt_gp_id >> 8) & 0xff);
-               buf[off++] = (tg_pt_gp_id & 0xff);
-               len += 8; /* Header size + Designation descriptor */
+               SET_VPD_DATA(buf, buf_len, off, (tg_pt_gp_id >> 8) & 0xff);
+               SET_VPD_DATA(buf, buf_len, off, tg_pt_gp_id & 0xff);
                /*
                 * Logical Unit Group identifier, see spc4r17
                 * section 7.7.3.8
@@ -348,15 +389,14 @@ check_lu_gp:
                lu_gp_id = lu_gp->lu_gp_id;
                spin_unlock(&lu_gp_mem->lu_gp_mem_lock);
 
-               buf[off++] |= 0x1; /* CODE SET == Binary */
+               SET_VPD_DATA(buf, buf_len, off, 0x1);/* CODE SET == Binary */
                /* DESIGNATOR TYPE == Logical Unit Group identifier */
-               buf[off++] |= 0x6;
-               off++; /* Skip over Reserved */
-               buf[off++] = 4; /* DESIGNATOR LENGTH */
+               SET_VPD_DATA(buf, buf_len, off, 0x6);
+               off ++; /* Skip over Reserved */
+               SET_VPD_DATA(buf, buf_len, off, 4); /* DESIGNATOR LENGTH */
                off += 2; /* Skip over Reserved Field */
-               buf[off++] = ((lu_gp_id >> 8) & 0xff);
-               buf[off++] = (lu_gp_id & 0xff);
-               len += 8; /* Header size + Designation descriptor */
+               SET_VPD_DATA(buf, buf_len, off,(lu_gp_id >> 8) & 0xff);
+               SET_VPD_DATA(buf, buf_len, off, lu_gp_id & 0xff);
                /*
                 * SCSI name string designator, see spc4r17
                 * section 7.7.3.11
@@ -365,14 +405,15 @@ check_lu_gp:
                 * section 7.5.1 Table 362
                 */
 check_scsi_name:
-               buf[off] =
-                       (tpg->se_tpg_tfo->get_fabric_proto_ident(tpg) << 4);
-               buf[off++] |= 0x3; /* CODE SET == UTF-8 */
-               buf[off] = 0x80; /* Set PIV=1 */
+               d = (tpg->se_tpg_tfo->get_fabric_proto_ident(tpg) << 4);
+               d |= 0x3; /* CODE SET == UTF-8 */
+               SET_VPD_DATA(buf, buf_len, off, d);
+               d = 0x80; /* Set PIV=1 */
                /* Set ASSOCIATION == target port: 01b */
-               buf[off] |= 0x10;
+               d |= 0x10;
                /* DESIGNATOR TYPE == SCSI name string */
-               buf[off++] |= 0x8;
+               d |= 0x8;
+               SET_VPD_DATA(buf, buf_len, off, d);
                off += 2; /* Skip over Reserved and length */
                /*
                 * SCSI name string identifer containing, $FABRIC_MOD
@@ -381,8 +422,10 @@ check_scsi_name:
                 * UTF-8 encoding.
                 */
                tpgt = tpg->se_tpg_tfo->tpg_get_tag(tpg);
-               scsi_name_len = sprintf(&buf[off], "%s,t,0x%04x",
-                                       tpg->se_tpg_tfo->tpg_get_wwn(tpg), 
tpgt);
+               scsi_name_len = snprintf(&buf[off], buf_len - off,
+                                        "%s,t,0x%04x",
+                                        tpg->se_tpg_tfo->tpg_get_wwn(tpg),
+                                        tpgt);
                scsi_name_len += 1 /* Include  NULL terminator */;
                /*
                 * The null-terminated, null-padded (see 4.4.2) SCSI
@@ -397,23 +440,22 @@ check_scsi_name:
                        scsi_name_len += padding;
                if (scsi_name_len > 256)
                        scsi_name_len = 256;
-
-               buf[off-1] = scsi_name_len;
+               if (buf_len > off -1 )
+                       buf[off-1] = scsi_name_len;
                off += scsi_name_len;
-               /* Header size + Designation descriptor */
-               len += (scsi_name_len + 4);
 
                /*
                 * Target device designator
                 */
-               buf[off] =
-                       (tpg->se_tpg_tfo->get_fabric_proto_ident(tpg) << 4);
-               buf[off++] |= 0x3; /* CODE SET == UTF-8 */
-               buf[off] = 0x80; /* Set PIV=1 */
+               d = (tpg->se_tpg_tfo->get_fabric_proto_ident(tpg) << 4);
+               d |= 0x3; /* CODE SET == UTF-8 */
+               SET_VPD_DATA(buf, buf_len, off, d);
+               d = 0x80; /* Set PIV=1 */
                /* Set ASSOCIATION == target device: 10b */
-               buf[off] |= 0x20;
+               d |= 0x20;
                /* DESIGNATOR TYPE == SCSI name string */
-               buf[off++] |= 0x8;
+               d |= 0x8;
+               SET_VPD_DATA(buf, buf_len, off, d);
                off += 2; /* Skip over Reserved and length */
                /*
                 * SCSI name string identifer containing, $FABRIC_MOD
@@ -421,7 +463,7 @@ check_scsi_name:
                 * Target Port, this means "<iSCSI name>" in
                 * UTF-8 encoding.
                 */
-               scsi_target_len = sprintf(&buf[off], "%s",
+               scsi_target_len = snprintf(&buf[off], buf_len - off, "%s",
                                          tpg->se_tpg_tfo->tpg_get_wwn(tpg));
                scsi_target_len += 1 /* Include  NULL terminator */;
                /*
@@ -438,14 +480,13 @@ check_scsi_name:
                if (scsi_name_len > 256)
                        scsi_name_len = 256;
 
-               buf[off-1] = scsi_target_len;
+               if (buf_len > off -1 )
+                       buf[off-1] = scsi_target_len;
                off += scsi_target_len;
-
-               /* Header size + Designation descriptor */
-               len += (scsi_target_len + 4);
        }
-       buf[2] = ((len >> 8) & 0xff);
-       buf[3] = (len & 0xff); /* Page Length for VPD 0x83 */
+       off -= 4;
+       buf[2] = ((off >> 8) & 0xff);
+       buf[3] = (off & 0xff); /* Page Length for VPD 0x83 */
        return 0;
 }
 EXPORT_SYMBOL(spc_emulate_evpd_83);
@@ -465,34 +506,43 @@ spc_check_dev_wce(struct se_device *dev)
 
 /* Extended INQUIRY Data VPD Page */
 static sense_reason_t
-spc_emulate_evpd_86(struct se_cmd *cmd, unsigned char *buf)
+spc_emulate_evpd_86(struct se_cmd *cmd, unsigned char *buf, int buf_len)
 {
        struct se_device *dev = cmd->se_dev;
 
+       if (buf_len < 4)
+               return TCM_INVALID_CDB_FIELD;
+
        buf[3] = 0x3c;
        /* Set HEADSUP, ORDSUP, SIMPSUP */
-       buf[5] = 0x07;
+       if (buf_len > 5)
+               buf[5] = 0x07;
 
        /* If WriteCache emulation is enabled, set V_SUP */
-       if (spc_check_dev_wce(dev))
+       if (buf_len > 6 && spc_check_dev_wce(dev))
                buf[6] = 0x01;
        /* If an LBA map is present set R_SUP */
-       spin_lock(&cmd->se_dev->t10_alua.lba_map_lock);
-       if (!list_empty(&dev->t10_alua.lba_map_list))
-               buf[8] = 0x10;
-       spin_unlock(&cmd->se_dev->t10_alua.lba_map_lock);
+       if (buf_len > 8) {
+               spin_lock(&cmd->se_dev->t10_alua.lba_map_lock);
+               if (!list_empty(&dev->t10_alua.lba_map_list))
+                       buf[8] = 0x10;
+               spin_unlock(&cmd->se_dev->t10_alua.lba_map_lock);
+       }
        return 0;
 }
 
 /* Block Limits VPD page */
 static sense_reason_t
-spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf)
+spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf, int buf_len)
 {
        struct se_device *dev = cmd->se_dev;
        u32 max_sectors;
        int have_tp = 0;
        int opt, min;
 
+       if (buf_len < 4)
+               return TCM_INVALID_CDB_FIELD;
+
        /*
         * Following spc3r22 section 6.5.3 Block Limits VPD page, when
         * emulate_tpu=1 or emulate_tpws=1 we will be expect a
@@ -505,35 +555,45 @@ spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char 
*buf)
        buf[3] = have_tp ? 0x3c : 0x10;
 
        /* Set WSNZ to 1 */
-       buf[4] = 0x01;
+       if (buf_len > 4)
+               buf[4] = 0x01;
        /*
         * Set MAXIMUM COMPARE AND WRITE LENGTH
         */
-       if (dev->dev_attrib.emulate_caw)
+       if (buf_len > 5 && dev->dev_attrib.emulate_caw)
                buf[5] = 0x01;
 
        /*
         * Set OPTIMAL TRANSFER LENGTH GRANULARITY
         */
-       if (dev->transport->get_io_min && (min = 
dev->transport->get_io_min(dev)))
-               put_unaligned_be16(min / dev->dev_attrib.block_size, &buf[6]);
-       else
-               put_unaligned_be16(1, &buf[6]);
+       if (buf_len > 7) {
+               if (dev->transport->get_io_min &&
+                   (min = dev->transport->get_io_min(dev)))
+                       min /= dev->dev_attrib.block_size;
+               else
+                       min = 1;
+               put_unaligned_be16(min, &buf[6]);
+       }
 
        /*
         * Set MAXIMUM TRANSFER LENGTH
         */
        max_sectors = min(dev->dev_attrib.fabric_max_sectors,
                          dev->dev_attrib.hw_max_sectors);
-       put_unaligned_be32(max_sectors, &buf[8]);
+       if (buf_len > 11)
+               put_unaligned_be32(max_sectors, &buf[8]);
 
        /*
         * Set OPTIMAL TRANSFER LENGTH
         */
-       if (dev->transport->get_io_opt && (opt = 
dev->transport->get_io_opt(dev)))
-               put_unaligned_be32(opt / dev->dev_attrib.block_size, &buf[12]);
-       else
-               put_unaligned_be32(dev->dev_attrib.optimal_sectors, &buf[12]);
+       if (buf_len > 15) {
+               if (dev->transport->get_io_opt &&
+                   (opt = dev->transport->get_io_opt(dev)))
+                       opt /= dev->dev_attrib.block_size;
+               else
+                       opt = dev->dev_attrib.optimal_sectors;
+               put_unaligned_be32(opt, &buf[12]);
+       }
 
        /*
         * Exit now if we don't support TP.
@@ -544,55 +604,69 @@ spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char 
*buf)
        /*
         * Set MAXIMUM UNMAP LBA COUNT
         */
-       put_unaligned_be32(dev->dev_attrib.max_unmap_lba_count, &buf[20]);
+       if (buf_len > 23)
+               put_unaligned_be32(dev->dev_attrib.max_unmap_lba_count,
+                                  &buf[20]);
 
        /*
         * Set MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT
         */
-       put_unaligned_be32(dev->dev_attrib.max_unmap_block_desc_count,
-                          &buf[24]);
+       if (buf_len > 27)
+               put_unaligned_be32(dev->dev_attrib.max_unmap_block_desc_count,
+                                  &buf[24]);
 
        /*
         * Set OPTIMAL UNMAP GRANULARITY
         */
-       put_unaligned_be32(dev->dev_attrib.unmap_granularity, &buf[28]);
+       if (buf_len > 31)
+               put_unaligned_be32(dev->dev_attrib.unmap_granularity, &buf[28]);
 
        /*
         * UNMAP GRANULARITY ALIGNMENT
         */
-       put_unaligned_be32(dev->dev_attrib.unmap_granularity_alignment,
-                          &buf[32]);
-       if (dev->dev_attrib.unmap_granularity_alignment != 0)
+       if (buf_len > 35)
+               put_unaligned_be32(dev->dev_attrib.unmap_granularity_alignment,
+                                  &buf[32]);
+       if (buf_len > 32 && dev->dev_attrib.unmap_granularity_alignment != 0)
                buf[32] |= 0x80; /* Set the UGAVALID bit */
 
        /*
         * MAXIMUM WRITE SAME LENGTH
         */
 max_write_same:
-       put_unaligned_be64(dev->dev_attrib.max_write_same_len, &buf[36]);
+       if (buf_len > 43)
+               put_unaligned_be64(dev->dev_attrib.max_write_same_len,
+                                  &buf[36]);
 
        return 0;
 }
 
 /* Block Device Characteristics VPD page */
 static sense_reason_t
-spc_emulate_evpd_b1(struct se_cmd *cmd, unsigned char *buf)
+spc_emulate_evpd_b1(struct se_cmd *cmd, unsigned char *buf, int buf_len)
 {
        struct se_device *dev = cmd->se_dev;
 
+       if (buf_len < 4)
+               return TCM_INVALID_CDB_FIELD;
+
        buf[0] = dev->transport->get_device_type(dev);
        buf[3] = 0x3c;
-       buf[5] = dev->dev_attrib.is_nonrot ? 1 : 0;
+       if (buf_len > 5)
+               buf[5] = dev->dev_attrib.is_nonrot ? 1 : 0;
 
        return 0;
 }
 
 /* Thin Provisioning VPD */
 static sense_reason_t
-spc_emulate_evpd_b2(struct se_cmd *cmd, unsigned char *buf)
+spc_emulate_evpd_b2(struct se_cmd *cmd, unsigned char *buf, int buf_len)
 {
        struct se_device *dev = cmd->se_dev;
 
+       if (buf_len < 8)
+               return TCM_INVALID_CDB_FIELD;
+
        /*
         * From spc3r22 section 6.5.4 Thin Provisioning VPD page:
         *
@@ -641,10 +715,13 @@ spc_emulate_evpd_b2(struct se_cmd *cmd, unsigned char 
*buf)
 
 /* Referrals VPD page */
 static sense_reason_t
-spc_emulate_evpd_b3(struct se_cmd *cmd, unsigned char *buf)
+spc_emulate_evpd_b3(struct se_cmd *cmd, unsigned char *buf, int buf_len)
 {
        struct se_device *dev = cmd->se_dev;
 
+       if (buf_len < 16)
+               return TCM_INVALID_CDB_FIELD;
+
        buf[0] = dev->transport->get_device_type(dev);
        buf[3] = 0x0c;
        put_unaligned_be32(dev->t10_alua.lba_map_segment_size, &buf[8]);
@@ -654,11 +731,11 @@ spc_emulate_evpd_b3(struct se_cmd *cmd, unsigned char 
*buf)
 }
 
 static sense_reason_t
-spc_emulate_evpd_00(struct se_cmd *cmd, unsigned char *buf);
+spc_emulate_evpd_00(struct se_cmd *cmd, unsigned char *buf, int buf_len);
 
 static struct {
        uint8_t         page;
-       sense_reason_t  (*emulate)(struct se_cmd *, unsigned char *);
+       sense_reason_t  (*emulate)(struct se_cmd *, unsigned char *, int);
 } evpd_handlers[] = {
        { .page = 0x00, .emulate = spc_emulate_evpd_00 },
        { .page = 0x80, .emulate = spc_emulate_evpd_80 },
@@ -672,10 +749,13 @@ static struct {
 
 /* supported vital product data pages */
 static sense_reason_t
-spc_emulate_evpd_00(struct se_cmd *cmd, unsigned char *buf)
+spc_emulate_evpd_00(struct se_cmd *cmd, unsigned char *buf, int buf_len)
 {
        int p;
 
+       if (buf_len < 4)
+               return TCM_INVALID_CDB_FIELD;
+
        /*
         * Only report the INQUIRY EVPD=1 pages after a valid NAA
         * Registered Extended LUN WWN has been set via ConfigFS
@@ -684,7 +764,8 @@ spc_emulate_evpd_00(struct se_cmd *cmd, unsigned char *buf)
        if (cmd->se_dev->dev_flags & DF_EMULATED_VPD_UNIT_SERIAL) {
                buf[3] = ARRAY_SIZE(evpd_handlers);
                for (p = 0; p < ARRAY_SIZE(evpd_handlers); ++p)
-                       buf[p + 4] = evpd_handlers[p].page;
+                       if (buf_len > p + 4)
+                               buf[p + 4] = evpd_handlers[p].page;
        }
 
        return 0;
@@ -695,13 +776,12 @@ spc_emulate_inquiry(struct se_cmd *cmd)
 {
        struct se_device *dev = cmd->se_dev;
        struct se_portal_group *tpg = cmd->se_lun->lun_sep->sep_tpg;
-       unsigned char *rbuf;
        unsigned char *cdb = cmd->t_task_cdb;
-       unsigned char buf[SE_INQUIRY_BUF];
+       unsigned char *buf;
        sense_reason_t ret;
        int p;
 
-       memset(buf, 0, SE_INQUIRY_BUF);
+       buf = transport_kmap_data_sg(cmd);
 
        if (dev == tpg->tpg_virt_lun0.lun_se_dev)
                buf[0] = 0x3f; /* Not connected */
@@ -723,7 +803,8 @@ spc_emulate_inquiry(struct se_cmd *cmd)
        for (p = 0; p < ARRAY_SIZE(evpd_handlers); ++p) {
                if (cdb[2] == evpd_handlers[p].page) {
                        buf[1] = cdb[2];
-                       ret = evpd_handlers[p].emulate(cmd, buf);
+                       ret = evpd_handlers[p].emulate(cmd, buf,
+                                                      cmd->data_length);
                        goto out;
                }
        }
@@ -732,11 +813,7 @@ spc_emulate_inquiry(struct se_cmd *cmd)
        ret = TCM_INVALID_CDB_FIELD;
 
 out:
-       rbuf = transport_kmap_data_sg(cmd);
-       if (rbuf) {
-               memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length));
-               transport_kunmap_data_sg(cmd);
-       }
+       transport_kunmap_data_sg(cmd);
 
        if (!ret)
                target_complete_cmd(cmd, GOOD);
diff --git a/include/target/target_core_backend.h 
b/include/target/target_core_backend.h
index 39e0114..98d5a0d 100644
--- a/include/target/target_core_backend.h
+++ b/include/target/target_core_backend.h
@@ -60,7 +60,7 @@ void  target_complete_cmd(struct se_cmd *, u8);
 sense_reason_t spc_parse_cdb(struct se_cmd *cmd, unsigned int *size);
 sense_reason_t spc_emulate_report_luns(struct se_cmd *cmd);
 sense_reason_t spc_emulate_inquiry_std(struct se_cmd *, unsigned char *);
-sense_reason_t spc_emulate_evpd_83(struct se_cmd *, unsigned char *);
+sense_reason_t spc_emulate_evpd_83(struct se_cmd *, unsigned char *, int);
 
 sense_reason_t sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops);
 u32    sbc_get_device_rev(struct se_device *dev);
-- 
1.7.12.4

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to