@@ -556,15 +556,10 @@ static void scsi_uninit_cmd(struct scsi_cmnd *cmd)
static void scsi_mq_free_sgtables(struct scsi_cmnd *cmd)
{
- struct scsi_data_buffer *sdb;
-
if (cmd->sdb.table.nents)
sg_free_table_chained(&cmd->sdb.table, true);
- if (cmd->request->next_rq) {
- sdb = cmd->request->next_rq->special;
- if (sdb)
- sg_free_table_chained(&sdb->table, true);
- }
+ if (scsi_bidi_cmnd(cmd))
+ sg_free_table_chained(&scsi_in(cmd)->table, true);
if (scsi_prot_sg_count(cmd))
sg_free_table_chained(&cmd->prot_sdb.table, true);
}
@@ -1059,7 +1054,7 @@ blk_status_t scsi_init_io(struct scsi_cmnd *cmd)
return ret;
if (blk_bidi_rq(rq)) {
- ret = scsi_init_sgtable(rq->next_rq, rq->next_rq->special);
+ ret = scsi_init_sgtable(rq->next_rq, scsi_in(cmd));
if (ret)
goto out_free_sgtables;
}
@@ -1595,12 +1590,17 @@ static unsigned int scsi_mq_sgl_size(struct Scsi_Host *shost)
sizeof(struct scatterlist);
}
+static void scsi_init_sdb(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
+{
+ cmd->sdb.table.sgl = (void *)cmd + sizeof(struct scsi_cmnd) +
+ shost->hostt->cmd_size;
+}
+
static blk_status_t scsi_mq_prep_fn(struct request *req)
{
struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(req);
struct scsi_device *sdev = req->q->queuedata;
struct Scsi_Host *shost = sdev->host;
- struct scatterlist *sg;
scsi_init_command(sdev, cmd);
@@ -1611,8 +1611,7 @@ static blk_status_t scsi_mq_prep_fn(struct request *req)
cmd->tag = req->tag;
cmd->prot_op = SCSI_PROT_NORMAL;
- sg = (void *)cmd + sizeof(struct scsi_cmnd) + shost->hostt->cmd_size;
- cmd->sdb.table.sgl = sg;
+ scsi_init_sdb(shost, cmd);
/*
* Always initialize cmd->prot_sdb.nents such that
@@ -1620,17 +1619,13 @@ static blk_status_t scsi_mq_prep_fn(struct request *req)
*/
memset(&cmd->prot_sdb, 0, sizeof(struct scsi_data_buffer));
if (scsi_host_get_prot(shost))
- cmd->prot_sdb.table.sgl = (void *)&sg + scsi_mq_sgl_size(shost);
+ cmd->prot_sdb.table.sgl = (void *)&cmd->sdb +
+ scsi_mq_sgl_size(shost);
if (blk_bidi_rq(req)) {
- struct request *next_rq = req->next_rq;
- struct scsi_data_buffer *bidi_sdb = blk_mq_rq_to_pdu(next_rq);
-
- memset(bidi_sdb, 0, sizeof(struct scsi_data_buffer));
- bidi_sdb->table.sgl =
- (struct scatterlist *)(bidi_sdb + 1);
-
- next_rq->special = bidi_sdb;
+ memset(&scsi_in_cmd(cmd)->sdb, 0,
+ sizeof(scsi_in_cmd(cmd)->sdb));
+ scsi_init_sdb(shost, scsi_in_cmd(cmd));
}
blk_mq_start_request(req);
@@ -215,14 +215,19 @@ static inline int scsi_get_resid(struct scsi_cmnd *cmd)
static inline int scsi_bidi_cmnd(struct scsi_cmnd *cmd)
{
- return blk_bidi_rq(cmd->request) &&
- (cmd->request->next_rq->special != NULL);
+ return blk_bidi_rq(cmd->request);
+}
+
+static inline struct scsi_cmnd *scsi_in_cmd(struct scsi_cmnd *cmd)
+{
+ if (likely(!scsi_bidi_cmnd(cmd)))
+ return cmd;
+ return blk_mq_rq_to_pdu(cmd->request->next_rq);
}
static inline struct scsi_data_buffer *scsi_in(struct scsi_cmnd *cmd)
{
- return scsi_bidi_cmnd(cmd) ?
- cmd->request->next_rq->special : &cmd->sdb;
+ return &scsi_in_cmd(cmd)->sdb;
}
static inline struct scsi_data_buffer *scsi_out(struct scsi_cmnd *cmd)
Some code in the SCSI core interprets blk_mq_rq_to_pdu(cmd->request->next_rq) as a struct scsi_data_buffer, e.g. scsi_mq_prep_fn(). Other code in the SCSI core interprets the same data structure as a struct scsi_request, e.g. scsi_io_completion(). Avoid this confusion by using the SCSI data buffer associated with "next_rq" for bidi requests. This patch avoids that submitting a bidi command triggers a NULL pointer dereference. Reported-by: Douglas Gilbert <dgilbert@interlog.com> Cc: Douglas Gilbert <dgilbert@interlog.com> Cc: Hannes Reinecke <hare@suse.com> Cc: Christoph Hellwig <hch@lst.de> Fixes: d285203cf647 ("scsi: add support for a blk-mq based I/O path.") # v3.17 Signed-off-by: Bart Van Assche <bvanassche@acm.org> --- drivers/scsi/scsi_lib.c | 35 +++++++++++++++-------------------- include/scsi/scsi_cmnd.h | 13 +++++++++---- 2 files changed, 24 insertions(+), 24 deletions(-)