[PATCH 3/6] scsi: bsg: release synchronous request before user copyback
From: Wang Yingjie
Date: Sun Oct 18 2026 - 11:42:47 EST
From: Yingjie Wang <x-professor@xxxxxxxxxxx>
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@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Yingjie Wang <x-professor@xxxxxxxxxxx>
---
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