summaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
authorMartin K. Petersen <martin.petersen@oracle.com>2022-03-02 00:35:49 -0500
committerMartin K. Petersen <martin.petersen@oracle.com>2022-05-02 16:59:10 -0400
commite17d63403076affccd72d195f93bbf3f39514005 (patch)
treefaf3a163106234c3195c14bc354f0e58221ec692 /drivers/scsi
parentc92a6b5d63359dd6d2ce6ea88ecd8e31dd769f6b (diff)
downloadlinux-e17d63403076affccd72d195f93bbf3f39514005.tar.bz2
scsi: core: Pick suitable allocation length in scsi_report_opcode()
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>
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/scsi.c17
1 files changed, 13 insertions, 4 deletions
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index a17392880da2..3db2a928faec 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -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;