From: Yingjie Wang scsi_bsg_sg_io_fn() retains a completed request while copying both sense data and READ bounce data to userspace. A fault on a mapping of the same one-tag SCSI device starts nested I/O that cannot acquire the retained tag. Snapshot the request-backed completion fields and sense data, save the transfer direction, and free the request before either userspace copy. The retained bio can then be unmapped safely. A BSG INQUIRY into a nonresident mapping of the same /dev/sr0 queue previously remained in D state in blk_rq_unmap_user -> filemap_fault. The fixed trigger returned in 0.06 seconds and produced no hung-task report. Fixes: 10e8855b9451 ("bsg: add SG_IO to SG v4") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Yingjie Wang --- drivers/scsi/scsi_bsg.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/scsi_bsg.c b/drivers/scsi/scsi_bsg.c index 5eec248a77a6..4937bc494f79 100644 --- a/drivers/scsi/scsi_bsg.c +++ b/drivers/scsi/scsi_bsg.c @@ -199,6 +199,10 @@ static int scsi_bsg_sg_io_fn(struct request_queue *q, struct sg_io_v4 *hdr, struct scsi_cmnd *scmd; struct request *rq; struct bio *bio; + u8 sense_buffer[SCSI_SENSE_BUFFERSIZE]; + u32 scsi_result, resid_len; + u8 sense_len; + bool is_read; int ret; if (hdr->protocol != BSG_PROTOCOL_SCSI || @@ -243,37 +247,46 @@ static int scsi_bsg_sg_io_fn(struct request_queue *q, struct sg_io_v4 *hdr, bio = rq->bio; blk_execute_rq(rq, !(hdr->flags & BSG_FLAG_Q_AT_TAIL)); + scsi_result = scmd->result; + resid_len = scmd->resid_len; + sense_len = min_t(unsigned int, scmd->sense_len, + sizeof(sense_buffer)); + if (sense_len) + memcpy(sense_buffer, scmd->sense_buffer, sense_len); + is_read = rq_data_dir(rq) == READ; + blk_mq_free_request(rq); /* * fill in all the output members */ - hdr->device_status = scmd->result & 0xff; - hdr->transport_status = host_byte(scmd->result); + hdr->device_status = scsi_result & 0xff; + hdr->transport_status = host_byte(scsi_result); hdr->driver_status = 0; - if (scsi_status_is_check_condition(scmd->result)) + if (scsi_status_is_check_condition(scsi_result)) hdr->driver_status = DRIVER_SENSE; hdr->info = 0; if (hdr->device_status || hdr->transport_status || hdr->driver_status) hdr->info |= SG_INFO_CHECK; hdr->response_len = 0; - if (scmd->sense_len && hdr->response) { + if (sense_len && hdr->response) { int len = min_t(unsigned int, hdr->max_response_len, - scmd->sense_len); + sense_len); - if (copy_to_user(uptr64(hdr->response), scmd->sense_buffer, + if (copy_to_user(uptr64(hdr->response), sense_buffer, len)) ret = -EFAULT; else hdr->response_len = len; } - if (rq_data_dir(rq) == READ) - hdr->din_resid = scmd->resid_len; + if (is_read) + hdr->din_resid = resid_len; else - hdr->dout_resid = scmd->resid_len; + hdr->dout_resid = resid_len; blk_rq_unmap_user(bio); + return ret; out_put_request: blk_mq_free_request(rq); -- 2.43.0