]> git.baikalelectronics.ru Git - kernel.git/commitdiff
scsi: core: Pick suitable allocation length in scsi_report_opcode()
authorMartin K. Petersen <martin.petersen@oracle.com>
Wed, 2 Mar 2022 05:35:49 +0000 (00:35 -0500)
committerMartin K. Petersen <martin.petersen@oracle.com>
Mon, 2 May 2022 20:59:10 +0000 (16:59 -0400)
Some devices hang when a buffer size larger than expected is passed in the
ALLOCATION LENGTH field. For REPORT SUPPORTED OPERATION CODES we currently
only request a single command descriptor at a time and therefore the actual
size of the command is known ahead of time. Limit the ALLOCATION LENGTH to
the header size plus the command length of the opcode we are asking about.

Link: https://lore.kernel.org/r/20220302053559.32147-5-martin.petersen@oracle.com
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/scsi.c

index a17392880da27b6961a113cc5812c755f2c03be9..3db2a928faecfd93c4987dceb3bd2c02f3961e1d 100644 (file)
@@ -503,21 +503,30 @@ int scsi_report_opcode(struct scsi_device *sdev, unsigned char *buffer,
 {
        unsigned char cmd[16];
        struct scsi_sense_hdr sshdr;
-       int result;
+       int result, request_len;
 
        if (sdev->no_report_opcodes || sdev->scsi_level < SCSI_SPC_3)
                return -EINVAL;
 
+       /* RSOC header + size of command we are asking about */
+       request_len = 4 + COMMAND_SIZE(opcode);
+       if (request_len > len) {
+               dev_warn_once(&sdev->sdev_gendev,
+                             "%s: len %u bytes, opcode 0x%02x needs %u\n",
+                             __func__, len, opcode, request_len);
+               return -EINVAL;
+       }
+
        memset(cmd, 0, 16);
        cmd[0] = MAINTENANCE_IN;
        cmd[1] = MI_REPORT_SUPPORTED_OPERATION_CODES;
        cmd[2] = 1;             /* One command format */
        cmd[3] = opcode;
-       put_unaligned_be32(len, &cmd[6]);
+       put_unaligned_be32(request_len, &cmd[6]);
        memset(buffer, 0, len);
 
-       result = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buffer, len,
-                                 &sshdr, 30 * HZ, 3, NULL);
+       result = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buffer,
+                                 request_len, &sshdr, 30 * HZ, 3, NULL);
 
        if (result < 0)
                return result;