From cc0ce3fe8fac073ecded4f32c8ee5b89da1397e8 Mon Sep 17 00:00:00 2001 From: Stanley Jhu Date: Thu, 15 Oct 2026 08:18:51 +0800 Subject: [PATCH 1/5] scsi: ufs: bsg: Complete the job on error When ufs_bsg_request() fails, it returns a non-zero status without calling bsg_job_done(). bsg_queue_rq() then ends the request with BLK_STS_IOERR without running bsg_complete(), leaking the job's device reference and mapped scatterlists. Always complete the job via bsg_job_done() and return 0, matching other BSG transports. Requests failing with a negative errno now return it from SG_IO with the 4-byte result in the reply, while positive status codes still return 0 from SG_IO with the reply header. Fixes: b13a3539eb2a ("scsi: ufs-bsg: complete ufs-bsg job only if no error") Cc: stable@vger.kernel.org Signed-off-by: Stanley Jhu Reviewed-by: Bean Huo Reviewed-by: Peter Wang Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20261015001855.3450573-2-stanleyjhu@google.com Signed-off-by: Martin K. Petersen (Oracle) --- drivers/ufs/core/ufs_bsg.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/ufs/core/ufs_bsg.c b/drivers/ufs/core/ufs_bsg.c index 7a66917b3fc0..18c318d78122 100644 --- a/drivers/ufs/core/ufs_bsg.c +++ b/drivers/ufs/core/ufs_bsg.c @@ -195,14 +195,12 @@ static int ufs_bsg_request(struct bsg_job *job) ufshcd_rpm_put_sync(hba); kfree(buff); bsg_reply->result = ret; - /* complete the job here only if no error */ - if (ret == 0) { + if (ret == 0) job->reply_len = rpmb ? sizeof(struct ufs_rpmb_reply) : sizeof(struct ufs_bsg_reply); - bsg_job_done(job, ret, bsg_reply->reply_payload_rcv_len); - } + bsg_job_done(job, ret, bsg_reply->reply_payload_rcv_len); - return ret; + return 0; } /** From 2e63ac76cc08ea376ba61fef52f7b7849e35024d Mon Sep 17 00:00:00 2001 From: Stanley Jhu Date: Thu, 15 Oct 2026 08:18:52 +0800 Subject: [PATCH 2/5] scsi: ufs: bsg: Clear the reply buffer before use The reply buffer is allocated once per tag and reused. Requests that complete with a positive status do not fill all of it, so user space receives bytes from an earlier request on the same tag. Clear the whole reply buffer on entry to ufs_bsg_request(). Fixes: 95e34bf930ea ("scsi: ufs-bsg: Add support for raw upiu in ufs_bsg_request()") Cc: stable@vger.kernel.org Signed-off-by: Stanley Jhu Reviewed-by: Bean Huo Reviewed-by: Peter Wang Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20261015001855.3450573-3-stanleyjhu@google.com Signed-off-by: Martin K. Petersen (Oracle) --- drivers/ufs/core/ufs_bsg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ufs/core/ufs_bsg.c b/drivers/ufs/core/ufs_bsg.c index 18c318d78122..545ef7b79ceb 100644 --- a/drivers/ufs/core/ufs_bsg.c +++ b/drivers/ufs/core/ufs_bsg.c @@ -140,7 +140,7 @@ static int ufs_bsg_request(struct bsg_job *job) int ret; bool rpmb = false; - bsg_reply->reply_payload_rcv_len = 0; + memset(job->reply, 0, job->reply_len); ufshcd_rpm_get_sync(hba); From 42aa443718104d10dbdb6f258810b425eb2451f8 Mon Sep 17 00:00:00 2001 From: Stanley Jhu Date: Thu, 15 Oct 2026 08:18:53 +0800 Subject: [PATCH 3/5] scsi: ufs: bsg: Validate request_len before accessing request ufs_bsg_request() and ufs_bsg_exec_advanced_rpmb_req() read the request buffer without checking job->request_len, which userspace controls. A short request causes an out-of-bounds read. Found by syzkaller. Reject requests shorter than the structure each path reads. Fixes: 64d4864714c2 ("scsi: ufs: bsg: Remove unnecessary length checkup") Fixes: 6ff265fc5ef6 ("scsi: ufs: core: bsg: Add advanced RPMB support in ufs_bsg") Cc: stable@vger.kernel.org Reviewed-by: Bean Huo Signed-off-by: Stanley Jhu Reviewed-by: Peter Wang Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20261015001855.3450573-4-stanleyjhu@google.com Signed-off-by: Martin K. Petersen (Oracle) --- drivers/ufs/core/ufs_bsg.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/ufs/core/ufs_bsg.c b/drivers/ufs/core/ufs_bsg.c index 545ef7b79ceb..0db830e54ef0 100644 --- a/drivers/ufs/core/ufs_bsg.c +++ b/drivers/ufs/core/ufs_bsg.c @@ -73,6 +73,9 @@ static int ufs_bsg_exec_advanced_rpmb_req(struct ufs_hba *hba, struct bsg_job *j if (hba->ufs_version < ufshci_version(4, 0) || !hba->dev_info.b_advanced_rpmb_en) return -EINVAL; + if (job->request_len < sizeof(*rpmb_request)) + return -EINVAL; + if (rpmb_request->ehs_req.length != 2 || rpmb_request->ehs_req.ehs_type != 1) return -EINVAL; @@ -142,6 +145,11 @@ static int ufs_bsg_request(struct bsg_job *job) memset(job->reply, 0, job->reply_len); + if (job->request_len < sizeof(*bsg_request)) { + ret = -EINVAL; + goto out_complete; + } + ufshcd_rpm_get_sync(hba); msgcode = bsg_request->msgcode; @@ -194,6 +202,7 @@ static int ufs_bsg_request(struct bsg_job *job) out: ufshcd_rpm_put_sync(hba); kfree(buff); +out_complete: bsg_reply->result = ret; if (ret == 0) job->reply_len = rpmb ? sizeof(struct ufs_rpmb_reply) : From 1ae42513205a38b811afedb6797f9f9c4de37b90 Mon Sep 17 00:00:00 2001 From: Stanley Jhu Date: Thu, 15 Oct 2026 08:18:54 +0800 Subject: [PATCH 4/5] scsi: ufs: bsg: Reject UIC commands with a non-zero result byte ufs_bsg_request() passes the user-supplied UIC argument2 unchanged, including the result byte (bits 7:0) that only the host controller writes. A non-zero value triggers the WARN_ON_ONCE() in ufshcd_dispatch_uic_cmd() and corrupts the reported result. Found by syzkaller. Reject UIC commands that have the result byte set with -EINVAL. Fixes: e77044c5a842 ("scsi: ufs-bsg: Add support for uic commands in ufs_bsg_request()") Fixes: 9fb4c793223b ("scsi: ufs: core: Complain if UIC argument 2 is invalid") Cc: stable@vger.kernel.org Signed-off-by: Stanley Jhu Reviewed-by: Bean Huo Reviewed-by: Peter Wang Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20261015001855.3450573-5-stanleyjhu@google.com Signed-off-by: Martin K. Petersen (Oracle) --- drivers/ufs/core/ufs_bsg.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/ufs/core/ufs_bsg.c b/drivers/ufs/core/ufs_bsg.c index 0db830e54ef0..cadde515f040 100644 --- a/drivers/ufs/core/ufs_bsg.c +++ b/drivers/ufs/core/ufs_bsg.c @@ -179,6 +179,10 @@ static int ufs_bsg_request(struct bsg_job *job) break; case UPIU_TRANSACTION_UIC_CMD: memcpy(&uc, &bsg_request->upiu_req.uc, UIC_CMD_SIZE); + if (uc.argument2 & MASK_UIC_COMMAND_RESULT) { + ret = -EINVAL; + break; + } ret = ufshcd_send_bsg_uic_cmd(hba, &uc); if (ret) dev_err(hba->dev, "send uic cmd: error code %d\n", ret); From e4a9b7640914b248eb7537d4507aa42db080586a Mon Sep 17 00:00:00 2001 From: Stanley Jhu Date: Thu, 15 Oct 2026 08:18:55 +0800 Subject: [PATCH 5/5] scsi: ufs: core: Clear stale UIC result byte before peer DME retries Peer DME get and set retry a failed attempt with the same struct uic_command. UIC completion ORs the ConfigResultCode into argument2[7:0], and nothing clears it before the next attempt. Once an attempt fails, every retry keeps reporting that error code, even when the device returns success, so a transient failure becomes a hard failure after three retries. Each such retry also triggers the WARN_ON_ONCE() in ufshcd_dispatch_uic_cmd(). In ufshcd_dme_set_attr(), ufshcd_uic_cmd_compl() also overwrites argument3 with the register readback before the next attempt. Clear the result byte and restore argument3 in ufshcd_dme_set_attr() when an attempt fails. Fixes: 64238fbd15ea ("scsi: ufs: add retries to dme_peer get and set attribute") Cc: stable@vger.kernel.org Reviewed-by: Bean Huo Signed-off-by: Stanley Jhu Reviewed-by: Peter Wang Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20261015001855.3450573-6-stanleyjhu@google.com Signed-off-by: Martin K. Petersen (Oracle) --- drivers/ufs/core/ufshcd.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 2ba244cf40ac..d0728e919d4e 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -4359,9 +4359,12 @@ int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel, do { /* for peer attributes we retry upon failure */ ret = ufshcd_send_uic_cmd(hba, &uic_cmd); - if (ret) + if (ret) { dev_dbg(hba->dev, "%s: attr-id 0x%x val 0x%x error code %d\n", set, UIC_GET_ATTR_ID(attr_sel), mib_val, ret); + uic_cmd.argument2 &= ~MASK_UIC_COMMAND_RESULT; + uic_cmd.argument3 = mib_val; + } } while (ret && peer && --retries); if (ret) @@ -4426,9 +4429,11 @@ int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel, do { /* for peer attributes we retry upon failure */ ret = ufshcd_send_uic_cmd(hba, &uic_cmd); - if (ret) + if (ret) { dev_dbg(hba->dev, "%s: attr-id 0x%x error code %d\n", get, UIC_GET_ATTR_ID(attr_sel), ret); + uic_cmd.argument2 &= ~MASK_UIC_COMMAND_RESULT; + } } while (ret && peer && --retries); if (ret)