[PATCH 1/6] scsi: core: release SG_IO request before copying to userspace

From: Wang Yingjie

Date: Sun Oct 18 2026 - 11:41:49 EST


From: Yingjie Wang <x-professor@xxxxxxxxxxx>

The block-device SG_IO path copies sense and READ bounce data to
userspace while the completed request still owns its queue tag. Either
copy can fault. If the fault reads from the same one-tag device, the
nested I/O cannot obtain a tag and the ioctl remains stuck in
uninterruptible sleep.

Snapshot all request-backed status, residual, and sense data, then free
the request before either userspace copy. Preserve the existing error
precedence between sense copy and data copyback.

A same-device INQUIRY on a one-tag /dev/sr0 queue hung beyond 10 seconds
before this change, with sg_io -> blk_rq_unmap_user -> filemap_fault in
the stack. The fixed case completed without a stuck task or hung-task
warning.

Fixes: 31151ba2cef1 ("fix mismerge in ll_rw_blk.c")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Yingjie Wang <x-professor@xxxxxxxxxxx>
---
drivers/scsi/scsi_ioctl.c | 45 +++++++++++++++++++++++++--------------
1 file changed, 29 insertions(+), 16 deletions(-)

diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c
index c14f81403a09..0d1f8eb22dfb 100644
--- a/drivers/scsi/scsi_ioctl.c
+++ b/drivers/scsi/scsi_ioctl.c
@@ -388,41 +388,38 @@ static int scsi_fill_sghdr_rq(struct scsi_device *sdev, struct request *rq,
return 0;
}

-static int scsi_complete_sghdr_rq(struct request *rq, struct sg_io_hdr *hdr,
- struct bio *bio)
+static int scsi_complete_sghdr_rq(struct sg_io_hdr *hdr, int result,
+ unsigned int resid_len,
+ const u8 *sense_buffer,
+ unsigned int sense_len)
{
- struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq);
- int r, ret = 0;
+ int ret = 0;

/*
* fill in all the output members
*/
- hdr->status = scmd->result & 0xff;
- hdr->masked_status = sg_status_byte(scmd->result);
+ hdr->status = result & 0xff;
+ hdr->masked_status = sg_status_byte(result);
hdr->msg_status = COMMAND_COMPLETE;
- hdr->host_status = host_byte(scmd->result);
+ hdr->host_status = host_byte(result);
hdr->driver_status = 0;
if (scsi_status_is_check_condition(hdr->status))
hdr->driver_status = DRIVER_SENSE;
hdr->info = 0;
if (hdr->masked_status || hdr->host_status || hdr->driver_status)
hdr->info |= SG_INFO_CHECK;
- hdr->resid = scmd->resid_len;
+ hdr->resid = resid_len;
hdr->sb_len_wr = 0;

- if (scmd->sense_len && hdr->sbp) {
- int len = min((unsigned int) hdr->mx_sb_len, scmd->sense_len);
+ if (sense_len && hdr->sbp) {
+ int len = min_t(unsigned int, hdr->mx_sb_len, sense_len);

- if (!copy_to_user(hdr->sbp, scmd->sense_buffer, len))
+ if (!copy_to_user(hdr->sbp, sense_buffer, len))
hdr->sb_len_wr = len;
else
ret = -EFAULT;
}

- r = blk_rq_unmap_user(bio);
- if (!ret)
- ret = r;
-
return ret;
}

@@ -436,6 +433,9 @@ static int sg_io(struct scsi_device *sdev, struct sg_io_hdr *hdr,
struct request *rq;
struct scsi_cmnd *scmd;
struct bio *bio;
+ u8 sense_buffer[SCSI_SENSE_BUFFERSIZE];
+ unsigned int sense_len, resid_len;
+ int result, unmap_ret;

if (hdr->interface_id != 'S')
return -EINVAL;
@@ -487,7 +487,20 @@ static int sg_io(struct scsi_device *sdev, struct sg_io_hdr *hdr,

hdr->duration = jiffies_to_msecs(jiffies - start_time);

- ret = scsi_complete_sghdr_rq(rq, hdr, bio);
+ 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);
+
+ blk_mq_free_request(rq);
+ ret = scsi_complete_sghdr_rq(hdr, result, resid_len, sense_buffer,
+ sense_len);
+ unmap_ret = blk_rq_unmap_user(bio);
+ if (!ret)
+ ret = unmap_ret;
+ return ret;

out_put_request:
blk_mq_free_request(rq);
--
2.43.0