From nobody Mon Jun 8 17:38:57 2026 Received: from out-03.smtp.spacemail.com (out-03.smtp.spacemail.com [63.250.43.88]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A577634CFA7; Wed, 27 May 2026 19:18:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=63.250.43.88 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779909521; cv=none; b=BrsBLrZOr/jRGHWK5XKUXtNpMLkOb/1g1nvOKC1KGTvDUwzJQG8SzIeedBlw+HCjRRysdcdWUPKpuWqPSbXXxYVjHQXNy3dtfBa58Go26oQ189TrraA7lzyeGj2Xd6FcAebLxD4ug+/8K6FD+UwA77WKiZdgPONAOdsoBr2xNUo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779909521; c=relaxed/simple; bh=kvGJAsELGXPJZk9NrIuTIvUOIwiNkTJ+kqt3GBsOj7E=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=diDBvCMn4gyFdIIgCcLqgQSb0JTf5VlHt0ALdnbXWLJOjmzRweXl4xtqQ7P7FddvCl8ZA6vHCmYzzMPa75Oroszug+rfYaxcAAlljoNLx1vb6jo+BSqosvc4tUkS1ABB2QYDE0Y7cXoStzJwaFuXfS5+ZLIUD7wn7uVk8C6ewrg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=rexion.ai; spf=pass smtp.mailfrom=rexion.ai; dkim=fail (0-bit key) header.d=rexion.ai header.i=@rexion.ai header.b=EjRuH8fk reason="key not found in DNS"; arc=none smtp.client-ip=63.250.43.88 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=rexion.ai Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=rexion.ai Authentication-Results: smtp.subspace.kernel.org; dkim=fail reason="key not found in DNS" (0-bit key) header.d=rexion.ai header.i=@rexion.ai header.b="EjRuH8fk" Received: from Kyren (unknown [49.207.213.66]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mail.spacemail.com (Postfix) with ESMTPSA id 4gQfXz1DXhz2x9B; Wed, 27 May 2026 19:18:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=rexion.ai; s=spacemail; t=1779909511; bh=WIUEG4zzn5ErfYY0sg41nrfwYjNTMLRAi91DY7rABh8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=EjRuH8fkN/sKcyDD/1ESBz24ZL4/qv+1gT0ShRF5ooMigtOU3T+TS5bg87K7LWW31 q4wndmGXchDtpI+kDbxzk7sgSc/Ifv7qnwhk3efvuFx4Z4PAikZ8c+wyPF2O6McaLY vUw970HuKC4/t9V44aaBJ2O8D/w75ciTxru4Otu0XRK84bcZxmR0RzFWdfGFZKOnl1 7m319TvwdbM1y1w2FRTI6mC33sP2482hlnBNiFkZrluh+Iub4AwMxIiE/63FLgtuLR MwVNt+F9kOrWMRP/lqUtT5ij7NT5dJAjAuksxh9IxmgXTTgwR6yHXr7z+0/z6BrlYo V6TRvLdqQ/b+w== From: Rahul Chandelkar To: rc@rexion.ai, "James E . J . Bottomley" , "Martin K . Petersen" , Jens Axboe , FUJITA Tomonori Cc: linux-scsi@vger.kernel.org, linux-block@vger.kernel.org, io-uring@vger.kernel.org, linux-kernel@vger.kernel.org, Yang Xiuwei , Bart Van Assche , Caleb Sander Mateos , stable@vger.kernel.org Subject: [PATCH v2] scsi: bsg: read io_uring command fields once Date: Thu, 28 May 2026 00:47:41 +0530 Message-ID: <20260527191817.142769-1-rc@rexion.ai> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260527105931.3950913-1-rc@rexion.ai> References: <20260527105931.3950913-1-rc@rexion.ai> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Envelope-From: rc@rexion.ai Content-Type: text/plain; charset="utf-8" scsi_bsg_uring_cmd() reads struct bsg_uring_cmd fields directly from the shared mmap'd io_uring SQE. On the inline execution path, io_uring may still point at userspace-visible SQE storage, so a concurrent userspace thread can change fields between validation and use. request_len is checked against the size of scmd->cmnd, then used again for scmd->cmd_len and copy_from_user(). If userspace changes request_len after the bounds check, the later copy can overflow the 32-byte scmd->cmnd buffer. Transfer fields are also read again by scsi_bsg_map_user_buffer(), leaving direction, address and length open to the same race. Use READ_ONCE() to load each bsg_uring_cmd field needed by scsi_bsg_uring_cmd() into a local variable, then use those locals for both validation and execution. Pass the stable transfer direction, address and length into scsi_bsg_map_user_buffer() so the helper no longer re-derives them from the SQE. This fixes the double-fetch without copying the whole io_uring command payload. Tested with KASAN on QEMU (virtio-scsi, 2 vCPUs). Without this fix, a two-thread race produces: BUG: KASAN: wild-memory-access in scsi_queue_rq+0x4a3/0x58a0 Write of size 96 at addr dead000000001000 by task poc/67 Call Trace: kasan_report+0xce/0x100 __asan_memset+0x23/0x50 scsi_queue_rq+0x4a3/0x58a0 scsi_bsg_uring_cmd+0x942/0x1570 io_uring_cmd+0x2f6/0x950 io_issue_sqe+0xe5/0x22d0 Link: https://lore.kernel.org/all/20260527105931.3950913-1-rc@rexion.ai/T/#u Fixes: 7b6d3255e7f8 ("scsi: bsg: add io_uring passthrough handler") Cc: stable@vger.kernel.org Signed-off-by: Rahul Chandelkar Reviewed-by: Yang Xiuwei --- Changes in v2: - Use READ_ONCE() for individual fields instead of memcpying the command payload. - Pass stable transfer parameters to scsi_bsg_map_user_buffer() so it does not re-read the SQE. - Do not carry the Reviewed-by tag from v1 because the implementation strategy changed. drivers/scsi/scsi_bsg.c | 54 ++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/drivers/scsi/scsi_bsg.c b/drivers/scsi/scsi_bsg.c index e80dec53174e..ccbe3d98e4ff 100644 --- a/drivers/scsi/scsi_bsg.c +++ b/drivers/scsi/scsi_bsg.c @@ -76,12 +76,10 @@ static enum rq_end_io_ret scsi_bsg_uring_cmd_done(struc= t request *req, =20 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 =3D io_uring_sqe128_cmd(ioucmd->sqe, stru= ct bsg_uring_cmd); - bool is_write =3D cmd->dout_xfer_len > 0; - u64 buf_addr =3D is_write ? cmd->dout_xferp : cmd->din_xferp; - unsigned long buf_len =3D is_write ? cmd->dout_xfer_len : cmd->din_xfer_l= en; struct iov_iter iter; int ret; =20 @@ -104,26 +102,40 @@ 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 =3D scsi_bsg_uring_cmd_pdu(ioucmd); - const struct bsg_uring_cmd *cmd =3D io_uring_sqe128_cmd(ioucmd->sqe, stru= ct bsg_uring_cmd); + const struct bsg_uring_cmd *cmd =3D + io_uring_sqe128_cmd(ioucmd->sqe, struct bsg_uring_cmd); struct scsi_cmnd *scmd; struct request *req; blk_mq_req_flags_t blk_flags =3D 0; gfp_t gfp_mask =3D GFP_KERNEL; + u64 request =3D READ_ONCE(cmd->request); + u32 request_len =3D READ_ONCE(cmd->request_len); + u32 protocol =3D READ_ONCE(cmd->protocol); + u32 subprotocol =3D READ_ONCE(cmd->subprotocol); + u32 max_response_len =3D READ_ONCE(cmd->max_response_len); + u64 response =3D READ_ONCE(cmd->response); + u64 dout_xferp =3D READ_ONCE(cmd->dout_xferp); + u32 dout_xfer_len =3D READ_ONCE(cmd->dout_xfer_len); + u32 dout_iovec_count =3D READ_ONCE(cmd->dout_iovec_count); + u64 din_xferp =3D READ_ONCE(cmd->din_xferp); + u32 din_xfer_len =3D READ_ONCE(cmd->din_xfer_len); + u32 din_iovec_count =3D READ_ONCE(cmd->din_iovec_count); + u32 timeout_ms =3D READ_ONCE(cmd->timeout_ms); int ret; =20 - if (cmd->protocol !=3D BSG_PROTOCOL_SCSI || - cmd->subprotocol !=3D BSG_SUB_PROTOCOL_SCSI_CMD) + if (protocol !=3D BSG_PROTOCOL_SCSI || + subprotocol !=3D BSG_SUB_PROTOCOL_SCSI_CMD) return -EINVAL; =20 - if (!cmd->request || cmd->request_len =3D=3D 0) + if (!request || request_len =3D=3D 0) return -EINVAL; =20 - 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; } =20 - if (cmd->dout_iovec_count > 0 || cmd->din_iovec_count > 0) + if (dout_iovec_count > 0 || din_iovec_count > 0) return -EOPNOTSUPP; =20 if (issue_flags & IO_URING_F_NONBLOCK) { @@ -131,20 +143,20 @@ static int scsi_bsg_uring_cmd(struct request_queue *q= , struct io_uring_cmd *iouc gfp_mask =3D GFP_NOWAIT; } =20 - req =3D scsi_alloc_request(q, cmd->dout_xfer_len ? + req =3D 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); =20 scmd =3D blk_mq_rq_to_pdu(req); - if (cmd->request_len > sizeof(scmd->cmnd)) { + if (request_len > sizeof(scmd->cmnd)) { ret =3D -EINVAL; goto out_free_req; } - scmd->cmd_len =3D cmd->request_len; + scmd->cmd_len =3D request_len; scmd->allowed =3D SG_DEFAULT_RETRIES; =20 - if (copy_from_user(scmd->cmnd, uptr64(cmd->request), cmd->request_len)) { + if (copy_from_user(scmd->cmnd, uptr64(request), request_len)) { ret =3D -EFAULT; goto out_free_req; } @@ -154,12 +166,18 @@ static int scsi_bsg_uring_cmd(struct request_queue *q= , struct io_uring_cmd *iouc goto out_free_req; } =20 - pdu->response_addr =3D cmd->response; - scmd->sense_len =3D cmd->max_response_len ? - min(cmd->max_response_len, SCSI_SENSE_BUFFERSIZE) : SCSI_SENSE_BUFFERSIZ= E; + pdu->response_addr =3D response; + scmd->sense_len =3D max_response_len ? + min(max_response_len, SCSI_SENSE_BUFFERSIZE) : SCSI_SENSE_BUFFERSIZE; =20 - if (cmd->dout_xfer_len || cmd->din_xfer_len) { - ret =3D scsi_bsg_map_user_buffer(req, ioucmd, issue_flags, gfp_mask); + if (dout_xfer_len || din_xfer_len) { + bool is_write =3D dout_xfer_len > 0; + u64 buf_addr =3D is_write ? dout_xferp : din_xferp; + unsigned long buf_len =3D is_write ? dout_xfer_len : din_xfer_len; + + ret =3D 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 =3D req->bio; @@ -167,8 +185,8 @@ static int scsi_bsg_uring_cmd(struct request_queue *q, = struct io_uring_cmd *iouc pdu->bio =3D NULL; } =20 - req->timeout =3D cmd->timeout_ms ? - msecs_to_jiffies(cmd->timeout_ms) : BLK_DEFAULT_SG_TIMEOUT; + req->timeout =3D timeout_ms ? + msecs_to_jiffies(timeout_ms) : BLK_DEFAULT_SG_TIMEOUT; =20 req->end_io =3D scsi_bsg_uring_cmd_done; req->end_io_data =3D ioucmd; --=20 2.54.0