diff options
| author | Hans de Goede <hdegoede@redhat.com> | 2014-09-13 12:26:32 +0200 | 
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-09-23 21:42:10 -0700 | 
| commit | 5e61aede477ee108de3f9e57f19cacd8ce3ffe52 (patch) | |
| tree | f02bddfe141a29490935ef859f779523afde94a9 /drivers/usb/storage/uas.c | |
| parent | e0620001e4e318d85ebf43a95eec15fae26ed706 (diff) | |
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: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/storage/uas.c')
| -rw-r--r-- | drivers/usb/storage/uas.c | 39 | 
1 files changed, 18 insertions, 21 deletions
| diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 3debefbd858f..33db53f1ce9e 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -30,6 +30,8 @@  #include "uas-detect.h"  #include "scsiglue.h" +#define MAX_CMNDS 256 +  /*   * The r00-r01c specs define this version of the SENSE IU data structure.   * It's still in use by several different firmware releases. @@ -56,7 +58,7 @@ struct uas_dev_info {  	unsigned use_streams:1;  	unsigned uas_sense_old:1;  	unsigned shutdown:1; -	struct scsi_cmnd *cmnd; +	struct scsi_cmnd *cmnd[MAX_CMNDS];  	spinlock_t lock;  	struct work_struct work;  	struct list_head inflight_list; @@ -316,6 +318,7 @@ static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller)  	if (cmdinfo->state & COMMAND_ABORTED)  		scmd_printk(KERN_INFO, cmnd, "abort completed\n");  	list_del(&cmdinfo->list); +	devinfo->cmnd[uas_get_tag(cmnd) - 1] = NULL;  	cmnd->scsi_done(cmnd);  	return 0;  } @@ -341,7 +344,7 @@ static void uas_stat_cmplt(struct urb *urb)  	struct scsi_cmnd *cmnd;  	struct uas_cmd_info *cmdinfo;  	unsigned long flags; -	u16 tag; +	unsigned int idx;  	spin_lock_irqsave(&devinfo->lock, flags); @@ -359,21 +362,17 @@ static void uas_stat_cmplt(struct urb *urb)  		goto out;  	} -	tag = be16_to_cpup(&iu->tag) - 1; -	if (tag == 0) -		cmnd = devinfo->cmnd; -	else -		cmnd = scsi_host_find_tag(shost, tag - 1); - -	if (!cmnd) +	idx = be16_to_cpup(&iu->tag) - 1; +	if (idx >= MAX_CMNDS || !devinfo->cmnd[idx]) { +		dev_err(&urb->dev->dev, +			"stat urb: no pending cmd for tag %d\n", idx + 1);  		goto out; +	} +	cmnd = devinfo->cmnd[idx];  	cmdinfo = (void *)&cmnd->SCp;  	switch (iu->iu_id) {  	case IU_ID_STATUS: -		if (devinfo->cmnd == cmnd) -			devinfo->cmnd = NULL; -  		if (urb->actual_length < 16)  			devinfo->uas_sense_old = 1;  		if (devinfo->uas_sense_old) @@ -674,6 +673,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,  	struct uas_dev_info *devinfo = sdev->hostdata;  	struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;  	unsigned long flags; +	unsigned int stream;  	int err;  	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,  		return 0;  	} -	if (devinfo->cmnd) { +	stream = uas_get_tag(cmnd); +	if (devinfo->cmnd[stream - 1]) {  		spin_unlock_irqrestore(&devinfo->lock, flags);  		return SCSI_MLQUEUE_DEVICE_BUSY;  	} -	memset(cmdinfo, 0, sizeof(*cmdinfo)); - -	if (!blk_rq_tagged(cmnd->request)) -		devinfo->cmnd = cmnd; -  	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;  	switch (cmnd->sc_data_direction) { @@ -738,6 +735,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,  		uas_add_work(cmdinfo);  	} +	devinfo->cmnd[stream - 1] = cmnd;  	list_add_tail(&cmdinfo->list, &devinfo->inflight_list);  	spin_unlock_irqrestore(&devinfo->lock, flags);  	return 0; @@ -877,7 +875,6 @@ static int uas_configure_endpoints(struct uas_dev_info *devinfo)  	int r;  	devinfo->uas_sense_old = 0; -	devinfo->cmnd = NULL;  	r = uas_find_endpoints(devinfo->intf->cur_altsetting, eps);  	if (r) @@ -897,7 +894,7 @@ static int uas_configure_endpoints(struct uas_dev_info *devinfo)  		devinfo->use_streams = 0;  	} else {  		devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1, -						    3, 256, GFP_NOIO); +						    3, MAX_CMNDS, GFP_NOIO);  		if (devinfo->qdepth < 0)  			return devinfo->qdepth;  		devinfo->use_streams = 1; | 
