Commit 5e61aede authored by Hans de Goede's avatar Hans de Goede Committed by Greg Kroah-Hartman

uas: Do not use scsi_host_find_tag

Using scsi_host_find_tag with tags returned by the device is unsafe for
multiple reasons:

1) It returns tags->rqs[tag], which may be non NULL even when the cmnd is
   not owned by us
2) It returns tags->rqs[tag], without holding any locks protecting it
3) It returns tags->rqs[tag], without doing any boundary checking

Instead keep our own list which maps tags -> inflight cmnds.
Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent e0620001
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
#include "uas-detect.h" #include "uas-detect.h"
#include "scsiglue.h" #include "scsiglue.h"
#define MAX_CMNDS 256
/* /*
* The r00-r01c specs define this version of the SENSE IU data structure. * The r00-r01c specs define this version of the SENSE IU data structure.
* It's still in use by several different firmware releases. * It's still in use by several different firmware releases.
...@@ -56,7 +58,7 @@ struct uas_dev_info { ...@@ -56,7 +58,7 @@ struct uas_dev_info {
unsigned use_streams:1; unsigned use_streams:1;
unsigned uas_sense_old:1; unsigned uas_sense_old:1;
unsigned shutdown:1; unsigned shutdown:1;
struct scsi_cmnd *cmnd; struct scsi_cmnd *cmnd[MAX_CMNDS];
spinlock_t lock; spinlock_t lock;
struct work_struct work; struct work_struct work;
struct list_head inflight_list; struct list_head inflight_list;
...@@ -316,6 +318,7 @@ static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller) ...@@ -316,6 +318,7 @@ static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller)
if (cmdinfo->state & COMMAND_ABORTED) if (cmdinfo->state & COMMAND_ABORTED)
scmd_printk(KERN_INFO, cmnd, "abort completed\n"); scmd_printk(KERN_INFO, cmnd, "abort completed\n");
list_del(&cmdinfo->list); list_del(&cmdinfo->list);
devinfo->cmnd[uas_get_tag(cmnd) - 1] = NULL;
cmnd->scsi_done(cmnd); cmnd->scsi_done(cmnd);
return 0; return 0;
} }
...@@ -341,7 +344,7 @@ static void uas_stat_cmplt(struct urb *urb) ...@@ -341,7 +344,7 @@ static void uas_stat_cmplt(struct urb *urb)
struct scsi_cmnd *cmnd; struct scsi_cmnd *cmnd;
struct uas_cmd_info *cmdinfo; struct uas_cmd_info *cmdinfo;
unsigned long flags; unsigned long flags;
u16 tag; unsigned int idx;
spin_lock_irqsave(&devinfo->lock, flags); spin_lock_irqsave(&devinfo->lock, flags);
...@@ -359,21 +362,17 @@ static void uas_stat_cmplt(struct urb *urb) ...@@ -359,21 +362,17 @@ static void uas_stat_cmplt(struct urb *urb)
goto out; goto out;
} }
tag = be16_to_cpup(&iu->tag) - 1; idx = be16_to_cpup(&iu->tag) - 1;
if (tag == 0) if (idx >= MAX_CMNDS || !devinfo->cmnd[idx]) {
cmnd = devinfo->cmnd; dev_err(&urb->dev->dev,
else "stat urb: no pending cmd for tag %d\n", idx + 1);
cmnd = scsi_host_find_tag(shost, tag - 1);
if (!cmnd)
goto out; goto out;
}
cmnd = devinfo->cmnd[idx];
cmdinfo = (void *)&cmnd->SCp; cmdinfo = (void *)&cmnd->SCp;
switch (iu->iu_id) { switch (iu->iu_id) {
case IU_ID_STATUS: case IU_ID_STATUS:
if (devinfo->cmnd == cmnd)
devinfo->cmnd = NULL;
if (urb->actual_length < 16) if (urb->actual_length < 16)
devinfo->uas_sense_old = 1; devinfo->uas_sense_old = 1;
if (devinfo->uas_sense_old) if (devinfo->uas_sense_old)
...@@ -674,6 +673,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, ...@@ -674,6 +673,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
struct uas_dev_info *devinfo = sdev->hostdata; struct uas_dev_info *devinfo = sdev->hostdata;
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
unsigned long flags; unsigned long flags;
unsigned int stream;
int err; int err;
BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer)); BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer));
...@@ -696,19 +696,16 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, ...@@ -696,19 +696,16 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
return 0; return 0;
} }
if (devinfo->cmnd) { stream = uas_get_tag(cmnd);
if (devinfo->cmnd[stream - 1]) {
spin_unlock_irqrestore(&devinfo->lock, flags); spin_unlock_irqrestore(&devinfo->lock, flags);
return SCSI_MLQUEUE_DEVICE_BUSY; return SCSI_MLQUEUE_DEVICE_BUSY;
} }
memset(cmdinfo, 0, sizeof(*cmdinfo));
if (!blk_rq_tagged(cmnd->request))
devinfo->cmnd = cmnd;
cmnd->scsi_done = done; cmnd->scsi_done = done;
cmdinfo->stream = uas_get_tag(cmnd); memset(cmdinfo, 0, sizeof(*cmdinfo));
cmdinfo->stream = stream;
cmdinfo->state = SUBMIT_STATUS_URB | ALLOC_CMD_URB | SUBMIT_CMD_URB; cmdinfo->state = SUBMIT_STATUS_URB | ALLOC_CMD_URB | SUBMIT_CMD_URB;
switch (cmnd->sc_data_direction) { switch (cmnd->sc_data_direction) {
...@@ -738,6 +735,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, ...@@ -738,6 +735,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
uas_add_work(cmdinfo); uas_add_work(cmdinfo);
} }
devinfo->cmnd[stream - 1] = cmnd;
list_add_tail(&cmdinfo->list, &devinfo->inflight_list); list_add_tail(&cmdinfo->list, &devinfo->inflight_list);
spin_unlock_irqrestore(&devinfo->lock, flags); spin_unlock_irqrestore(&devinfo->lock, flags);
return 0; return 0;
...@@ -877,7 +875,6 @@ static int uas_configure_endpoints(struct uas_dev_info *devinfo) ...@@ -877,7 +875,6 @@ static int uas_configure_endpoints(struct uas_dev_info *devinfo)
int r; int r;
devinfo->uas_sense_old = 0; devinfo->uas_sense_old = 0;
devinfo->cmnd = NULL;
r = uas_find_endpoints(devinfo->intf->cur_altsetting, eps); r = uas_find_endpoints(devinfo->intf->cur_altsetting, eps);
if (r) if (r)
...@@ -897,7 +894,7 @@ static int uas_configure_endpoints(struct uas_dev_info *devinfo) ...@@ -897,7 +894,7 @@ static int uas_configure_endpoints(struct uas_dev_info *devinfo)
devinfo->use_streams = 0; devinfo->use_streams = 0;
} else { } else {
devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1, devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1,
3, 256, GFP_NOIO); 3, MAX_CMNDS, GFP_NOIO);
if (devinfo->qdepth < 0) if (devinfo->qdepth < 0)
return devinfo->qdepth; return devinfo->qdepth;
devinfo->use_streams = 1; devinfo->use_streams = 1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment