[PATCH 3/7] Fix bidi handling

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxxx>
Cc: Douglas Gilbert <dgilbert@xxxxxxxxxxxx>
Cc: Hannes Reinecke <hare@xxxxxxxx>
Cc: Christoph Hellwig <hch@xxxxxx>
Fixes: d285203cf647 ("scsi: add support for a blk-mq based I/O path.") # v3.17
Signed-off-by: Bart Van Assche <bvanassche@xxxxxxx>
---
 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..54902afb8d94 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_cmnd *cmd)
+{
+	cmd->sdb.table.sgl = (void *)cmd + sizeof(struct scsi_cmnd) +
+			cmd->device->host->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(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(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




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]

  Powered by Linux