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 <dgilb...@interlog.com>
Cc: Douglas Gilbert <dgilb...@interlog.com>
Cc: Hannes Reinecke <h...@suse.com>
Cc: Christoph Hellwig <h...@lst.de>
Fixes: d285203cf647 ("scsi: add support for a blk-mq based I/O path.") # v3.17
Signed-off-by: Bart Van Assche <bvanass...@acm.org>
---
 drivers/scsi/scsi_lib.c  | 35 +++++++++++++++--------------------
 include/scsi/scsi_cmnd.h | 13 +++++++++----
 2 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 6bfbe50ef38e..bcbf266e4172 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -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);
diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h
index 0406c0fbee3e..78183e851a0d 100644
--- a/include/scsi/scsi_cmnd.h
+++ b/include/scsi/scsi_cmnd.h
@@ -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)
-- 
2.20.1.321.g9e740568ce-goog

Reply via email to