diff mbox series

[v2,3/7] Fix bidi handling

Message ID 20190123191013.119684-4-bvanassche@acm.org (mailing list archive)
State Not Applicable
Headers show
Series Fix handling of bidi commands | expand

Commit Message

Bart Van Assche Jan. 23, 2019, 7:10 p.m. UTC
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(-)
diff mbox series

Patch

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)