mirror of
https://github.com/torvalds/linux.git
synced 2026-09-12 20:53:03 +02:00
scsi: bsg: Fix TOCTOU in io_uring passthrough command setup
scsi_bsg_uring_cmd() reads bsg_uring_cmd from the shared mmap'd SQE.
Userspace can change a field after we check it and before we use it.
request_len is the sharp case: it can grow past sizeof(scmd->cmnd) after
the bound check and overflow scmd->cmnd in copy_from_user().
READ_ONCE() the SQE fields we check or use into locals before use.
Fixes: 7b6d3255e7 ("scsi: bsg: add io_uring passthrough handler")
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/20260527105931.3950913-1-rc@rexion.ai
Signed-off-by: Rahul Chandelkar <rc@rexion.ai>
Co-developed-by: Yang Xiuwei <yangxiuwei@kylinos.cn>
Signed-off-by: Yang Xiuwei <yangxiuwei@kylinos.cn>
Link: https://patch.msgid.link/20260817080730.967879-3-yangxiuwei@kylinos.cn
Signed-off-by: Martin K. Petersen (Oracle) <mkp@kernel.org>
This commit is contained in:
parent
ece06de726
commit
4b3c5965fc
|
|
@ -77,12 +77,10 @@ static enum rq_end_io_ret scsi_bsg_uring_cmd_done(struct request *req,
|
|||
|
||||
static int scsi_bsg_map_user_buffer(struct request *req,
|
||||
struct io_uring_cmd *ioucmd,
|
||||
unsigned int issue_flags, gfp_t gfp_mask)
|
||||
unsigned int issue_flags, gfp_t gfp_mask,
|
||||
bool is_write, u64 buf_addr,
|
||||
unsigned long buf_len)
|
||||
{
|
||||
const struct bsg_uring_cmd *cmd = io_uring_sqe128_cmd(ioucmd->sqe, struct bsg_uring_cmd);
|
||||
bool is_write = cmd->dout_xfer_len > 0;
|
||||
u64 buf_addr = is_write ? cmd->dout_xferp : cmd->din_xferp;
|
||||
unsigned long buf_len = is_write ? cmd->dout_xfer_len : cmd->din_xfer_len;
|
||||
struct iov_iter iter;
|
||||
int ret;
|
||||
|
||||
|
|
@ -105,21 +103,28 @@ static int scsi_bsg_uring_cmd(struct request_queue *q, struct io_uring_cmd *iouc
|
|||
unsigned int issue_flags, bool open_for_write)
|
||||
{
|
||||
struct scsi_bsg_uring_cmd_pdu *pdu = scsi_bsg_uring_cmd_pdu(ioucmd);
|
||||
const struct bsg_uring_cmd *cmd = io_uring_sqe128_cmd(ioucmd->sqe, struct bsg_uring_cmd);
|
||||
const struct bsg_uring_cmd *cmd =
|
||||
io_uring_sqe128_cmd(ioucmd->sqe, struct bsg_uring_cmd);
|
||||
struct scsi_cmnd *scmd;
|
||||
struct request *req;
|
||||
blk_mq_req_flags_t blk_flags = 0;
|
||||
gfp_t gfp_mask = GFP_KERNEL;
|
||||
u64 request = READ_ONCE(cmd->request);
|
||||
u32 request_len = READ_ONCE(cmd->request_len);
|
||||
u64 dout_xferp = READ_ONCE(cmd->dout_xferp);
|
||||
u32 dout_xfer_len = READ_ONCE(cmd->dout_xfer_len);
|
||||
u64 din_xferp = READ_ONCE(cmd->din_xferp);
|
||||
u32 din_xfer_len = READ_ONCE(cmd->din_xfer_len);
|
||||
int ret;
|
||||
|
||||
if (cmd->protocol != BSG_PROTOCOL_SCSI ||
|
||||
cmd->subprotocol != BSG_SUB_PROTOCOL_SCSI_CMD)
|
||||
return -EINVAL;
|
||||
|
||||
if (!cmd->request || cmd->request_len == 0)
|
||||
if (!request || request_len == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (cmd->dout_xfer_len && cmd->din_xfer_len) {
|
||||
if (dout_xfer_len && din_xfer_len) {
|
||||
pr_warn_once("BIDI support in bsg has been removed.\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
|
@ -132,20 +137,20 @@ static int scsi_bsg_uring_cmd(struct request_queue *q, struct io_uring_cmd *iouc
|
|||
gfp_mask = GFP_NOWAIT;
|
||||
}
|
||||
|
||||
req = scsi_alloc_request(q, cmd->dout_xfer_len ?
|
||||
req = scsi_alloc_request(q, dout_xfer_len ?
|
||||
REQ_OP_DRV_OUT : REQ_OP_DRV_IN, blk_flags);
|
||||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
|
||||
scmd = blk_mq_rq_to_pdu(req);
|
||||
if (cmd->request_len > sizeof(scmd->cmnd)) {
|
||||
if (request_len > sizeof(scmd->cmnd)) {
|
||||
ret = -EINVAL;
|
||||
goto out_free_req;
|
||||
}
|
||||
scmd->cmd_len = cmd->request_len;
|
||||
scmd->cmd_len = request_len;
|
||||
scmd->allowed = SG_DEFAULT_RETRIES;
|
||||
|
||||
if (copy_from_user(scmd->cmnd, uptr64(cmd->request), cmd->request_len)) {
|
||||
if (copy_from_user(scmd->cmnd, uptr64(request), request_len)) {
|
||||
ret = -EFAULT;
|
||||
goto out_free_req;
|
||||
}
|
||||
|
|
@ -158,8 +163,14 @@ static int scsi_bsg_uring_cmd(struct request_queue *q, struct io_uring_cmd *iouc
|
|||
pdu->response_addr = cmd->response;
|
||||
pdu->max_response_len = cmd->max_response_len;
|
||||
|
||||
if (cmd->dout_xfer_len || cmd->din_xfer_len) {
|
||||
ret = scsi_bsg_map_user_buffer(req, ioucmd, issue_flags, gfp_mask);
|
||||
if (dout_xfer_len || din_xfer_len) {
|
||||
bool is_write = dout_xfer_len > 0;
|
||||
u64 buf_addr = is_write ? dout_xferp : din_xferp;
|
||||
unsigned long buf_len = is_write ? dout_xfer_len : din_xfer_len;
|
||||
|
||||
ret = scsi_bsg_map_user_buffer(req, ioucmd, issue_flags,
|
||||
gfp_mask, is_write, buf_addr,
|
||||
buf_len);
|
||||
if (ret)
|
||||
goto out_free_req;
|
||||
pdu->bio = req->bio;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user