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