* [PATCH 1/5] scsi: ufs: core: Validate string descriptors
2026-07-16 6:19 [PATCH 0/5] scsi: ufs: Fix descriptor parsing and invalid input handling liqiang
@ 2026-07-16 6:19 ` liqiang
2026-07-16 17:47 ` Bart Van Assche
2026-07-16 6:19 ` [PATCH 2/5] scsi: ufs: Fix NULL dereferences after tag lookup liqiang
` (5 subsequent siblings)
6 siblings, 1 reply; 20+ messages in thread
From: liqiang @ 2026-07-16 6:19 UTC (permalink / raw)
To: linux-scsi
Cc: linux-kernel, alim.akhtar, avri.altman, bvanassche,
James.Bottomley, martin.petersen, peter.wang, beanhuo, can.guo,
adrian.hunter, tomas.winkler
From: Li Qiang <liqiang01@kylinos.cn>
The string descriptor length includes a two-byte header while the
UTF-16 payload starts after it. utf16s_to_utf8s() expects a count
of UTF-16 code units, not bytes. Passing the payload byte count can
make it read beyond the descriptor buffer.
Validate that the payload has an even byte count, pass a code-unit
count to the converter, and allocate sufficient UTF-8 output space.
The raw string buffer starts after the descriptor header but its size
is bLength. Copying bLength bytes from that pointer can read beyond
the response buffer.
Allocate a zeroed bLength-sized buffer and copy only the UTF-16
payload. This preserves the raw buffer size consumed by the RPMB
device-ID ABI while avoiding the overread.
Fixes: 4b828fe156a6 ("scsi: ufs: revamp string descriptor reading")
Fixes: d794b499f948 ("scsi: ufs: core: fix incorrect buffer duplication in ufshcd_read_string_desc()")
Signed-off-by: Li Qiang <liqiang01@kylinos.cn>
---
drivers/ufs/core/ufshcd.c | 24 +++++++++++++++++++-----
1 file changed, 19 insertions(+), 5 deletions(-)
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index d3044a3089b5..3541da5b6f4d 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -3865,7 +3865,7 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, u8 **buf, enum u
{
struct uc_string_id *uc_str;
u8 *str;
- int ret;
+ int ret, uc_len;
if (!buf)
return -EINVAL;
@@ -3890,11 +3890,19 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, u8 **buf, enum u
goto out;
}
+ uc_len = uc_str->len - QUERY_DESC_HDR_SIZE;
+ if (uc_len % sizeof(*uc_str->uc)) {
+ dev_err(hba->dev, "String Desc has an odd UTF-16 payload length\n");
+ str = NULL;
+ ret = -EINVAL;
+ goto out;
+ }
+
if (fmt == SD_ASCII_STD) {
ssize_t ascii_len;
int i;
- /* remove header and divide by 2 to move from UTF16 to UTF8 */
- ascii_len = (uc_str->len - QUERY_DESC_HDR_SIZE) / 2 + 1;
+ /* Allow up to three UTF-8 bytes per UTF-16 code unit plus a NUL. */
+ ascii_len = uc_len / sizeof(*uc_str->uc) * 3 + 1;
str = kzalloc(ascii_len, GFP_KERNEL);
if (!str) {
ret = -ENOMEM;
@@ -3906,7 +3914,7 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, u8 **buf, enum u
* we need to convert to utf-8 so it can be displayed
*/
ret = utf16s_to_utf8s(uc_str->uc,
- uc_str->len - QUERY_DESC_HDR_SIZE,
+ uc_len / sizeof(*uc_str->uc),
UTF16_BIG_ENDIAN, str, ascii_len - 1);
/* replace non-printable or non-ASCII characters with spaces */
@@ -3916,11 +3924,17 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, u8 **buf, enum u
str[ret++] = '\0';
} else {
- str = kmemdup(uc_str->uc, uc_str->len, GFP_KERNEL);
+ /*
+ * Keep the bLength-sized raw output for the RPMB device ID ABI.
+ * The two bytes beyond the UTF-16 payload are explicitly zeroed
+ * instead of being read past the descriptor buffer.
+ */
+ str = kzalloc(uc_str->len, GFP_KERNEL);
if (!str) {
ret = -ENOMEM;
goto out;
}
+ memcpy(str, uc_str->uc, uc_len);
ret = uc_str->len;
}
out:
--
2.43.0
^ permalink raw reply [flat|nested] 20+ messages in thread* Re: [PATCH 1/5] scsi: ufs: core: Validate string descriptors
2026-07-16 6:19 ` [PATCH 1/5] scsi: ufs: core: Validate string descriptors liqiang
@ 2026-07-16 17:47 ` Bart Van Assche
2026-07-17 15:35 ` Li Qiang
0 siblings, 1 reply; 20+ messages in thread
From: Bart Van Assche @ 2026-07-16 17:47 UTC (permalink / raw)
To: liqiang, linux-scsi
Cc: linux-kernel, alim.akhtar, avri.altman, James.Bottomley,
martin.petersen, peter.wang, beanhuo, can.guo, adrian.hunter,
tomas.winkler
On 7/15/26 11:19 PM, liqiang wrote:
> @@ -3916,11 +3924,17 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, u8 **buf, enum u
> str[ret++] = '\0';
>
> } else {
> - str = kmemdup(uc_str->uc, uc_str->len, GFP_KERNEL);
> + /*
> + * Keep the bLength-sized raw output for the RPMB device ID ABI.
> + * The two bytes beyond the UTF-16 payload are explicitly zeroed
> + * instead of being read past the descriptor buffer.
> + */
> + str = kzalloc(uc_str->len, GFP_KERNEL);
> if (!str) {
> ret = -ENOMEM;
> goto out;
> }
> + memcpy(str, uc_str->uc, uc_len);
> ret = uc_str->len;
> }
The above code can be simplified by using kasprintf(GFP_KERNEL, "%.*s",
...), isn't it?
Thanks,
Bart.
^ permalink raw reply [flat|nested] 20+ messages in thread* Re: [PATCH 1/5] scsi: ufs: core: Validate string descriptors
2026-07-16 17:47 ` Bart Van Assche
@ 2026-07-17 15:35 ` Li Qiang
0 siblings, 0 replies; 20+ messages in thread
From: Li Qiang @ 2026-07-17 15:35 UTC (permalink / raw)
To: Bart Van Assche
Cc: linux-scsi, linux-kernel, alim.akhtar, avri.altman,
James.Bottomley, martin.petersen, peter.wang, beanhuo, can.guo,
adrian.hunter, tomas.winkler
Hi Bart,
I don't think kasprintf() is suitable here. This path must preserve the raw UTF-16 bytes and return a buffer of uc_str->len bytes for the RPMB device-ID ABI; %.*s treats the data as a C string and would not preserve embedded NUL bytes or the required two-byte padding.
Thanks,
Li Qiang
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 2/5] scsi: ufs: Fix NULL dereferences after tag lookup
2026-07-16 6:19 [PATCH 0/5] scsi: ufs: Fix descriptor parsing and invalid input handling liqiang
2026-07-16 6:19 ` [PATCH 1/5] scsi: ufs: core: Validate string descriptors liqiang
@ 2026-07-16 6:19 ` liqiang
2026-07-16 17:52 ` Bart Van Assche
2026-07-16 6:19 ` [PATCH 3/5] scsi: ufs: core: Validate connected lane counts liqiang
` (4 subsequent siblings)
6 siblings, 1 reply; 20+ messages in thread
From: liqiang @ 2026-07-16 6:19 UTC (permalink / raw)
To: linux-scsi
Cc: linux-kernel, alim.akhtar, avri.altman, bvanassche,
James.Bottomley, martin.petersen, peter.wang, beanhuo, can.guo,
adrian.hunter, tomas.winkler
From: Li Qiang <liqiang01@kylinos.cn>
ufshcd_tag_to_cmd() can return NULL when no command is associated
with a tag. The MCQ cleanup and completion paths dereferenced the
result before checking it.
Move private-command and request lookups after the NULL checks. Also
avoid dereferencing cqe while reporting an invalid tag because the
single-doorbell completion path passes a NULL CQE.
Fixes: 22089c218037 ("scsi: ufs: core: Optimize the hot path")
Signed-off-by: Li Qiang <liqiang01@kylinos.cn>
---
drivers/ufs/core/ufs-mcq.c | 6 ++++--
drivers/ufs/core/ufshcd.c | 7 ++++---
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/drivers/ufs/core/ufs-mcq.c b/drivers/ufs/core/ufs-mcq.c
index 13b60a2d06db..15850b0658f9 100644
--- a/drivers/ufs/core/ufs-mcq.c
+++ b/drivers/ufs/core/ufs-mcq.c
@@ -555,8 +555,8 @@ static int ufshcd_mcq_sq_start(struct ufs_hba *hba, struct ufs_hw_queue *hwq)
int ufshcd_mcq_sq_cleanup(struct ufs_hba *hba, int task_tag)
{
struct scsi_cmnd *cmd = ufshcd_tag_to_cmd(hba, task_tag);
- struct ufshcd_lrb *lrbp = scsi_cmd_priv(cmd);
- struct request *rq = scsi_cmd_to_rq(cmd);
+ struct ufshcd_lrb *lrbp;
+ struct request *rq;
struct ufs_hw_queue *hwq;
void __iomem *reg, *opr_sqd_base;
u32 nexus, id, val;
@@ -568,6 +568,8 @@ int ufshcd_mcq_sq_cleanup(struct ufs_hba *hba, int task_tag)
if (!cmd)
return -EINVAL;
+ lrbp = scsi_cmd_priv(cmd);
+ rq = scsi_cmd_to_rq(cmd);
hwq = ufshcd_mcq_req_to_hwq(hba, rq);
if (!hwq)
return 0;
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 3541da5b6f4d..f2a5fa624e74 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -5857,13 +5857,14 @@ void ufshcd_compl_one_cqe(struct ufs_hba *hba, int task_tag,
struct cq_entry *cqe)
{
struct scsi_cmnd *cmd = ufshcd_tag_to_cmd(hba, task_tag);
- struct ufshcd_lrb *lrbp = scsi_cmd_priv(cmd);
+ struct ufshcd_lrb *lrbp;
enum utp_ocs ocs;
- if (WARN_ONCE(!cmd, "cqe->command_desc_base_addr = %#llx\n",
- le64_to_cpu(cqe->command_desc_base_addr)))
+ if (WARN_ONCE(!cmd, "invalid completion tag %d, cqe->command_desc_base_addr = %#llx\n",
+ task_tag, cqe ? le64_to_cpu(cqe->command_desc_base_addr) : 0ULL))
return;
+ lrbp = scsi_cmd_priv(cmd);
if (hba->monitor.enabled) {
lrbp->compl_time_stamp = ktime_get();
lrbp->compl_time_stamp_local_clock = local_clock();
--
2.43.0
^ permalink raw reply [flat|nested] 20+ messages in thread* Re: [PATCH 2/5] scsi: ufs: Fix NULL dereferences after tag lookup
2026-07-16 6:19 ` [PATCH 2/5] scsi: ufs: Fix NULL dereferences after tag lookup liqiang
@ 2026-07-16 17:52 ` Bart Van Assche
0 siblings, 0 replies; 20+ messages in thread
From: Bart Van Assche @ 2026-07-16 17:52 UTC (permalink / raw)
To: liqiang, linux-scsi
Cc: linux-kernel, alim.akhtar, avri.altman, James.Bottomley,
martin.petersen, peter.wang, beanhuo, can.guo, adrian.hunter,
tomas.winkler
On 7/15/26 11:19 PM, liqiang wrote:
> diff --git a/drivers/ufs/core/ufs-mcq.c b/drivers/ufs/core/ufs-mcq.c
> index 13b60a2d06db..15850b0658f9 100644
> --- a/drivers/ufs/core/ufs-mcq.c
> +++ b/drivers/ufs/core/ufs-mcq.c
> @@ -555,8 +555,8 @@ static int ufshcd_mcq_sq_start(struct ufs_hba *hba, struct ufs_hw_queue *hwq)
> int ufshcd_mcq_sq_cleanup(struct ufs_hba *hba, int task_tag)
> {
> struct scsi_cmnd *cmd = ufshcd_tag_to_cmd(hba, task_tag);
> - struct ufshcd_lrb *lrbp = scsi_cmd_priv(cmd);
> - struct request *rq = scsi_cmd_to_rq(cmd);
> + struct ufshcd_lrb *lrbp;
> + struct request *rq;
> struct ufs_hw_queue *hwq;
> void __iomem *reg, *opr_sqd_base;
> u32 nexus, id, val;
> @@ -568,6 +568,8 @@ int ufshcd_mcq_sq_cleanup(struct ufs_hba *hba, int task_tag)
> if (!cmd)
> return -EINVAL;
>
> + lrbp = scsi_cmd_priv(cmd);
> + rq = scsi_cmd_to_rq(cmd);
> hwq = ufshcd_mcq_req_to_hwq(hba, rq);
> if (!hwq)
> return 0;
Please drop this change. Calling scsi_cmd_priv(NULL) or
scsi_cmd_to_rq(NULL) is safe if the pointers returned by these functions
are not used.
> diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
> index 3541da5b6f4d..f2a5fa624e74 100644
> --- a/drivers/ufs/core/ufshcd.c
> +++ b/drivers/ufs/core/ufshcd.c
> @@ -5857,13 +5857,14 @@ void ufshcd_compl_one_cqe(struct ufs_hba *hba, int task_tag,
> struct cq_entry *cqe)
> {
> struct scsi_cmnd *cmd = ufshcd_tag_to_cmd(hba, task_tag);
> - struct ufshcd_lrb *lrbp = scsi_cmd_priv(cmd);
> + struct ufshcd_lrb *lrbp;
> enum utp_ocs ocs;
Same comment here: calling scsi_cmd_priv(NULL) is safe if the returned
pointer is not used as is the case here.
>
> - if (WARN_ONCE(!cmd, "cqe->command_desc_base_addr = %#llx\n",
> - le64_to_cpu(cqe->command_desc_base_addr)))
> + if (WARN_ONCE(!cmd, "invalid completion tag %d, cqe->command_desc_base_addr = %#llx\n",
> + task_tag, cqe ? le64_to_cpu(cqe->command_desc_base_addr) : 0ULL))
> return;
The above change looks good to me.
Thanks,
Bart.
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 3/5] scsi: ufs: core: Validate connected lane counts
2026-07-16 6:19 [PATCH 0/5] scsi: ufs: Fix descriptor parsing and invalid input handling liqiang
2026-07-16 6:19 ` [PATCH 1/5] scsi: ufs: core: Validate string descriptors liqiang
2026-07-16 6:19 ` [PATCH 2/5] scsi: ufs: Fix NULL dereferences after tag lookup liqiang
@ 2026-07-16 6:19 ` liqiang
2026-07-16 6:19 ` [PATCH 4/5] scsi: ufs: rpmb: Validate frame before parsing liqiang
` (3 subsequent siblings)
6 siblings, 0 replies; 20+ messages in thread
From: liqiang @ 2026-07-16 6:19 UTC (permalink / raw)
To: linux-scsi
Cc: linux-kernel, alim.akhtar, avri.altman, bvanassche,
James.Bottomley, martin.petersen, peter.wang, beanhuo, can.guo,
adrian.hunter, tomas.winkler
From: Li Qiang <liqiang01@kylinos.cn>
The connected lane count is used by TX equalization code to index
arrays sized by UFS_MAX_LANES. Reject zero and out-of-range RX or TX
lane counts before they can be propagated.
Fixes: 03e5d38e2f98 ("scsi: ufs: core: Add support for TX Equalization")
Signed-off-by: Li Qiang <liqiang01@kylinos.cn>
---
drivers/ufs/core/ufshcd.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index f2a5fa624e74..1319e1071025 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -4729,7 +4729,9 @@ static int ufshcd_get_max_pwr_mode(struct ufs_hba *hba)
ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES),
&pwr_info->lane_tx);
- if (!pwr_info->lane_rx || !pwr_info->lane_tx) {
+ if (!pwr_info->lane_rx || !pwr_info->lane_tx ||
+ pwr_info->lane_rx > UFS_MAX_LANES ||
+ pwr_info->lane_tx > UFS_MAX_LANES) {
dev_err(hba->dev, "%s: invalid connected lanes value. rx=%d, tx=%d\n",
__func__,
pwr_info->lane_rx,
--
2.43.0
^ permalink raw reply [flat|nested] 20+ messages in thread* [PATCH 4/5] scsi: ufs: rpmb: Validate frame before parsing
2026-07-16 6:19 [PATCH 0/5] scsi: ufs: Fix descriptor parsing and invalid input handling liqiang
` (2 preceding siblings ...)
2026-07-16 6:19 ` [PATCH 3/5] scsi: ufs: core: Validate connected lane counts liqiang
@ 2026-07-16 6:19 ` liqiang
2026-07-16 17:58 ` Bart Van Assche
2026-07-16 6:19 ` [PATCH 5/5] scsi: ufs: debugfs: Reserve space for a string terminator liqiang
` (2 subsequent siblings)
6 siblings, 1 reply; 20+ messages in thread
From: liqiang @ 2026-07-16 6:19 UTC (permalink / raw)
To: linux-scsi
Cc: linux-kernel, alim.akhtar, avri.altman, bvanassche,
James.Bottomley, martin.petersen, peter.wang, beanhuo, can.guo,
adrian.hunter, tomas.winkler
From: Li Qiang <liqiang01@kylinos.cn>
The RPMB core only verifies that request and response buffers are
nonempty. This callback reads req_resp at the end of the first request
frame before validating the request length.
Require a complete frame before that access. Use unaligned accessors
because RPMB buffers are passed as u8 pointers and do not have an
alignment guarantee.
Fixes: b06b8c421485 ("scsi: ufs: core: Add OP-TEE based RPMB driver for UFS devices")
Signed-off-by: Li Qiang <liqiang01@kylinos.cn>
---
drivers/ufs/core/ufs-rpmb.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/ufs/core/ufs-rpmb.c b/drivers/ufs/core/ufs-rpmb.c
index ffad049872b9..53f66b274aca 100644
--- a/drivers/ufs/core/ufs-rpmb.c
+++ b/drivers/ufs/core/ufs-rpmb.c
@@ -69,7 +69,11 @@ static int ufs_rpmb_route_frames(struct device *dev, u8 *req, unsigned int req_l
hba = ufs_rpmb->hba;
- req_type = be16_to_cpu(frm_out->req_resp);
+ /* req_resp is at the end of an RPMB frame. */
+ if (req_len < sizeof(*frm_out))
+ return -EINVAL;
+
+ req_type = get_unaligned_be16(&frm_out->req_resp);
switch (req_type) {
case RPMB_PROGRAM_KEY:
@@ -107,7 +111,7 @@ static int ufs_rpmb_route_frames(struct device *dev, u8 *req, unsigned int req_l
struct rpmb_frame *frm_resp = (struct rpmb_frame *)resp;
memset(frm_resp, 0, sizeof(*frm_resp));
- frm_resp->req_resp = cpu_to_be16(RPMB_RESULT_READ);
+ put_unaligned_be16(RPMB_RESULT_READ, &frm_resp->req_resp);
ret = ufs_sec_submit(hba, protocol_id, resp, resp_len, true);
if (ret) {
dev_err(dev, "Result read request failed with ret=%d\n", ret);
--
2.43.0
^ permalink raw reply [flat|nested] 20+ messages in thread* Re: [PATCH 4/5] scsi: ufs: rpmb: Validate frame before parsing
2026-07-16 6:19 ` [PATCH 4/5] scsi: ufs: rpmb: Validate frame before parsing liqiang
@ 2026-07-16 17:58 ` Bart Van Assche
0 siblings, 0 replies; 20+ messages in thread
From: Bart Van Assche @ 2026-07-16 17:58 UTC (permalink / raw)
To: liqiang, linux-scsi
Cc: linux-kernel, alim.akhtar, avri.altman, James.Bottomley,
martin.petersen, peter.wang, beanhuo, can.guo, adrian.hunter,
tomas.winkler
On 7/15/26 11:19 PM, liqiang wrote:
> From: Li Qiang <liqiang01@kylinos.cn>
>
> The RPMB core only verifies that request and response buffers are
> nonempty. This callback reads req_resp at the end of the first request
> frame before validating the request length.
>
> Require a complete frame before that access. Use unaligned accessors
> because RPMB buffers are passed as u8 pointers and do not have an
> alignment guarantee.
>
> Fixes: b06b8c421485 ("scsi: ufs: core: Add OP-TEE based RPMB driver for UFS devices")
> Signed-off-by: Li Qiang <liqiang01@kylinos.cn>
> ---
> drivers/ufs/core/ufs-rpmb.c | 8 ++++++--
> 1 file changed, 6 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/ufs/core/ufs-rpmb.c b/drivers/ufs/core/ufs-rpmb.c
> index ffad049872b9..53f66b274aca 100644
> --- a/drivers/ufs/core/ufs-rpmb.c
> +++ b/drivers/ufs/core/ufs-rpmb.c
> @@ -69,7 +69,11 @@ static int ufs_rpmb_route_frames(struct device *dev, u8 *req, unsigned int req_l
>
> hba = ufs_rpmb->hba;
>
> - req_type = be16_to_cpu(frm_out->req_resp);
> + /* req_resp is at the end of an RPMB frame. */
> + if (req_len < sizeof(*frm_out))
> + return -EINVAL;
> +
> + req_type = get_unaligned_be16(&frm_out->req_resp);
>
> switch (req_type) {
> case RPMB_PROGRAM_KEY:
> @@ -107,7 +111,7 @@ static int ufs_rpmb_route_frames(struct device *dev, u8 *req, unsigned int req_l
> struct rpmb_frame *frm_resp = (struct rpmb_frame *)resp;
>
> memset(frm_resp, 0, sizeof(*frm_resp));
> - frm_resp->req_resp = cpu_to_be16(RPMB_RESULT_READ);
> + put_unaligned_be16(RPMB_RESULT_READ, &frm_resp->req_resp);
> ret = ufs_sec_submit(hba, protocol_id, resp, resp_len, true);
> if (ret) {
> dev_err(dev, "Result read request failed with ret=%d\n", ret);
Please split this patch into two patches because the two above changes
are independent of each other.
Thanks,
Bart.
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 5/5] scsi: ufs: debugfs: Reserve space for a string terminator
2026-07-16 6:19 [PATCH 0/5] scsi: ufs: Fix descriptor parsing and invalid input handling liqiang
` (3 preceding siblings ...)
2026-07-16 6:19 ` [PATCH 4/5] scsi: ufs: rpmb: Validate frame before parsing liqiang
@ 2026-07-16 6:19 ` liqiang
2026-07-16 17:56 ` Bart Van Assche
2026-07-16 17:42 ` [PATCH 0/5] scsi: ufs: Fix descriptor parsing and invalid input handling Bart Van Assche
2026-07-17 15:39 ` [PATCH v2 0/6] " Li Qiang
6 siblings, 1 reply; 20+ messages in thread
From: liqiang @ 2026-07-16 6:19 UTC (permalink / raw)
To: linux-scsi
Cc: linux-kernel, alim.akhtar, avri.altman, bvanassche,
James.Bottomley, martin.petersen, peter.wang, beanhuo, can.guo,
adrian.hunter, tomas.winkler
From: Li Qiang <liqiang01@kylinos.cn>
ufs_saved_err_write() copies user input into a zero-initialized stack
buffer and passes it to kstrtoint(). A write that fills the entire
buffer overwrites its only terminator.
Reject an input whose length leaves no room for the trailing NUL.
Fixes: 7340faae9474 ("scsi: ufs: core: Add debugfs attributes for triggering the UFS EH")
Signed-off-by: Li Qiang <liqiang01@kylinos.cn>
---
drivers/ufs/core/ufs-debugfs.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/ufs/core/ufs-debugfs.c b/drivers/ufs/core/ufs-debugfs.c
index e3dd81d6fe82..be527209540d 100644
--- a/drivers/ufs/core/ufs-debugfs.c
+++ b/drivers/ufs/core/ufs-debugfs.c
@@ -165,7 +165,7 @@ static ssize_t ufs_saved_err_write(struct file *file, const char __user *buf,
char val_str[16] = { };
int val, ret;
- if (count > sizeof(val_str))
+ if (count >= sizeof(val_str))
return -EINVAL;
if (copy_from_user(val_str, buf, count))
return -EFAULT;
--
2.43.0
^ permalink raw reply [flat|nested] 20+ messages in thread* Re: [PATCH 5/5] scsi: ufs: debugfs: Reserve space for a string terminator
2026-07-16 6:19 ` [PATCH 5/5] scsi: ufs: debugfs: Reserve space for a string terminator liqiang
@ 2026-07-16 17:56 ` Bart Van Assche
0 siblings, 0 replies; 20+ messages in thread
From: Bart Van Assche @ 2026-07-16 17:56 UTC (permalink / raw)
To: liqiang, linux-scsi
Cc: linux-kernel, alim.akhtar, avri.altman, James.Bottomley,
martin.petersen, peter.wang, beanhuo, can.guo, adrian.hunter,
tomas.winkler
On 7/15/26 11:19 PM, liqiang wrote:
> From: Li Qiang <liqiang01@kylinos.cn>
>
> ufs_saved_err_write() copies user input into a zero-initialized stack
> buffer and passes it to kstrtoint(). A write that fills the entire
> buffer overwrites its only terminator.
>
> Reject an input whose length leaves no room for the trailing NUL.
>
> Fixes: 7340faae9474 ("scsi: ufs: core: Add debugfs attributes for triggering the UFS EH")
> Signed-off-by: Li Qiang <liqiang01@kylinos.cn>
> ---
> drivers/ufs/core/ufs-debugfs.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/ufs/core/ufs-debugfs.c b/drivers/ufs/core/ufs-debugfs.c
> index e3dd81d6fe82..be527209540d 100644
> --- a/drivers/ufs/core/ufs-debugfs.c
> +++ b/drivers/ufs/core/ufs-debugfs.c
> @@ -165,7 +165,7 @@ static ssize_t ufs_saved_err_write(struct file *file, const char __user *buf,
> char val_str[16] = { };
> int val, ret;
>
> - if (count > sizeof(val_str))
> + if (count >= sizeof(val_str))
> return -EINVAL;
> if (copy_from_user(val_str, buf, count))
> return -EFAULT;
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 0/5] scsi: ufs: Fix descriptor parsing and invalid input handling
2026-07-16 6:19 [PATCH 0/5] scsi: ufs: Fix descriptor parsing and invalid input handling liqiang
` (4 preceding siblings ...)
2026-07-16 6:19 ` [PATCH 5/5] scsi: ufs: debugfs: Reserve space for a string terminator liqiang
@ 2026-07-16 17:42 ` Bart Van Assche
2026-07-17 15:16 ` Li Qiang
2026-07-17 15:39 ` [PATCH v2 0/6] " Li Qiang
6 siblings, 1 reply; 20+ messages in thread
From: Bart Van Assche @ 2026-07-16 17:42 UTC (permalink / raw)
To: liqiang, linux-scsi
Cc: linux-kernel, alim.akhtar, avri.altman, James.Bottomley,
martin.petersen, peter.wang, beanhuo, can.guo, adrian.hunter,
tomas.winkler
On 7/15/26 11:19 PM, liqiang wrote:
> The first patch validates string descriptor payload sizes and avoids raw
> descriptor overreads. The remaining patches protect command completion and
> MCQ cleanup, validate connected lane counts, validate RPMB frame sizes before
> parsing, and retain a NUL terminator for debugfs input.
Since this is the first time that you are contributing to the UFS
driver: how have these issues been discovered? How have these issues
been tested? Is my understanding correct that these only have been
compile-tested?
Bart.
^ permalink raw reply [flat|nested] 20+ messages in thread* Re: [PATCH 0/5] scsi: ufs: Fix descriptor parsing and invalid input handling
2026-07-16 17:42 ` [PATCH 0/5] scsi: ufs: Fix descriptor parsing and invalid input handling Bart Van Assche
@ 2026-07-17 15:16 ` Li Qiang
0 siblings, 0 replies; 20+ messages in thread
From: Li Qiang @ 2026-07-17 15:16 UTC (permalink / raw)
To: Bart Van Assche
Cc: linux-scsi, linux-kernel, alim.akhtar, avri.altman,
James.Bottomley, martin.petersen, peter.wang, beanhuo, can.guo,
adrian.hunter, tomas.winkler
Hi Bart,
These issues were discovered with the assistance of an AI-based static code analysis tool. After manual review, I confirmed that they are real issues, so I submitted patches to fix them.
I have not tested them on physical hardware. Since these patches do not change the main functionality and only add security hardening, I think compile testing is sufficient.
Thanks,
Li Qiang
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 0/6] scsi: ufs: Fix descriptor parsing and invalid input handling
2026-07-16 6:19 [PATCH 0/5] scsi: ufs: Fix descriptor parsing and invalid input handling liqiang
` (5 preceding siblings ...)
2026-07-16 17:42 ` [PATCH 0/5] scsi: ufs: Fix descriptor parsing and invalid input handling Bart Van Assche
@ 2026-07-17 15:39 ` Li Qiang
2026-07-17 15:39 ` [PATCH v2 1/6] scsi: ufs: core: Validate string descriptors Li Qiang
` (5 more replies)
6 siblings, 6 replies; 20+ messages in thread
From: Li Qiang @ 2026-07-17 15:39 UTC (permalink / raw)
To: linux-scsi
Cc: bvanassche, linux-kernel, alim.akhtar, avri.altman,
James.Bottomley, martin.petersen, peter.wang, beanhuo, can.guo,
adrian.hunter, tomas.winkler
The first patch validates string descriptor payload sizes and avoids raw
descriptor overreads. The remaining patches protect invalid completion
diagnostics, validate connected lane counts, validate RPMB frame sizes,
use unaligned RPMB frame accesses, and retain a NUL terminator for
debugfs input.
Changes in v2:
- Reworked patch 2 as requested: drop changes to the safe
scsi_cmd_priv(NULL) and scsi_cmd_to_rq(NULL) calls and retain only the
invalid-CQE diagnostic fix.
- Split the previous RPMB patch into a request-length validation patch
and an independent unaligned-access patch.
Li Qiang (6):
scsi: ufs: core: Validate string descriptors
scsi: ufs: Avoid NULL CQE dereference when reporting invalid tags
scsi: ufs: core: Validate connected lane counts
scsi: ufs: rpmb: Validate request frame length before parsing
scsi: ufs: rpmb: Use unaligned accessors for RPMB frames
scsi: ufs: debugfs: Reserve space for a string terminator
drivers/ufs/core/ufs-debugfs.c | 2 +-
drivers/ufs/core/ufs-rpmb.c | 8 ++++++--
drivers/ufs/core/ufshcd.c | 32 ++++++++++++++++++++++++--------
3 files changed, 31 insertions(+), 11 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 20+ messages in thread* [PATCH v2 1/6] scsi: ufs: core: Validate string descriptors
2026-07-17 15:39 ` [PATCH v2 0/6] " Li Qiang
@ 2026-07-17 15:39 ` Li Qiang
2026-07-17 15:39 ` [PATCH v2 2/6] scsi: ufs: Avoid NULL CQE dereference when reporting invalid tags Li Qiang
` (4 subsequent siblings)
5 siblings, 0 replies; 20+ messages in thread
From: Li Qiang @ 2026-07-17 15:39 UTC (permalink / raw)
To: linux-scsi
Cc: bvanassche, linux-kernel, alim.akhtar, avri.altman,
James.Bottomley, martin.petersen, peter.wang, beanhuo, can.guo,
adrian.hunter, tomas.winkler
The string descriptor length includes a two-byte header while the
UTF-16 payload starts after it. utf16s_to_utf8s() expects a count
of UTF-16 code units, not bytes. Passing the payload byte count can
make it read beyond the descriptor buffer.
Validate that the payload has an even byte count, pass a code-unit
count to the converter, and allocate sufficient UTF-8 output space.
The raw string buffer starts after the descriptor header but its size
is bLength. Copying bLength bytes from that pointer can read beyond
the response buffer.
Allocate a zeroed bLength-sized buffer and copy only the UTF-16
payload. This preserves the raw buffer size consumed by the RPMB
device-ID ABI while avoiding the overread.
Fixes: 4b828fe156a6 ("scsi: ufs: revamp string descriptor reading")
Fixes: d794b499f948 ("scsi: ufs: core: fix incorrect buffer duplication in ufshcd_read_string_desc()")
Signed-off-by: Li Qiang <liqiang01@kylinos.cn>
---
drivers/ufs/core/ufshcd.c | 24 +++++++++++++++++++-----
1 file changed, 19 insertions(+), 5 deletions(-)
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index d3044a3089b5..3541da5b6f4d 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -3865,7 +3865,7 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, u8 **buf, enum u
{
struct uc_string_id *uc_str;
u8 *str;
- int ret;
+ int ret, uc_len;
if (!buf)
return -EINVAL;
@@ -3890,11 +3890,19 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, u8 **buf, enum u
goto out;
}
+ uc_len = uc_str->len - QUERY_DESC_HDR_SIZE;
+ if (uc_len % sizeof(*uc_str->uc)) {
+ dev_err(hba->dev, "String Desc has an odd UTF-16 payload length\n");
+ str = NULL;
+ ret = -EINVAL;
+ goto out;
+ }
+
if (fmt == SD_ASCII_STD) {
ssize_t ascii_len;
int i;
- /* remove header and divide by 2 to move from UTF16 to UTF8 */
- ascii_len = (uc_str->len - QUERY_DESC_HDR_SIZE) / 2 + 1;
+ /* Allow up to three UTF-8 bytes per UTF-16 code unit plus a NUL. */
+ ascii_len = uc_len / sizeof(*uc_str->uc) * 3 + 1;
str = kzalloc(ascii_len, GFP_KERNEL);
if (!str) {
ret = -ENOMEM;
@@ -3906,7 +3914,7 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, u8 **buf, enum u
* we need to convert to utf-8 so it can be displayed
*/
ret = utf16s_to_utf8s(uc_str->uc,
- uc_str->len - QUERY_DESC_HDR_SIZE,
+ uc_len / sizeof(*uc_str->uc),
UTF16_BIG_ENDIAN, str, ascii_len - 1);
/* replace non-printable or non-ASCII characters with spaces */
@@ -3916,11 +3924,17 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, u8 **buf, enum u
str[ret++] = '\0';
} else {
- str = kmemdup(uc_str->uc, uc_str->len, GFP_KERNEL);
+ /*
+ * Keep the bLength-sized raw output for the RPMB device ID ABI.
+ * The two bytes beyond the UTF-16 payload are explicitly zeroed
+ * instead of being read past the descriptor buffer.
+ */
+ str = kzalloc(uc_str->len, GFP_KERNEL);
if (!str) {
ret = -ENOMEM;
goto out;
}
+ memcpy(str, uc_str->uc, uc_len);
ret = uc_str->len;
}
out:
--
2.43.0
^ permalink raw reply [flat|nested] 20+ messages in thread* [PATCH v2 2/6] scsi: ufs: Avoid NULL CQE dereference when reporting invalid tags
2026-07-17 15:39 ` [PATCH v2 0/6] " Li Qiang
2026-07-17 15:39 ` [PATCH v2 1/6] scsi: ufs: core: Validate string descriptors Li Qiang
@ 2026-07-17 15:39 ` Li Qiang
2026-07-17 15:39 ` [PATCH v2 3/6] scsi: ufs: core: Validate connected lane counts Li Qiang
` (3 subsequent siblings)
5 siblings, 0 replies; 20+ messages in thread
From: Li Qiang @ 2026-07-17 15:39 UTC (permalink / raw)
To: linux-scsi
Cc: bvanassche, linux-kernel, alim.akhtar, avri.altman,
James.Bottomley, martin.petersen, peter.wang, beanhuo, can.guo,
adrian.hunter, tomas.winkler
The single-doorbell completion path can call ufshcd_compl_one_cqe() with
a NULL CQE. If no command is associated with the completion tag, the
warning message dereferences the CQE while reporting the error.
Avoid that dereference and include the invalid tag in the warning.
Fixes: 22089c218037 ("scsi: ufs: core: Optimize the hot path")
Signed-off-by: Li Qiang <liqiang01@kylinos.cn>
---
drivers/ufs/core/ufshcd.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 3541da5b6f4d..d1b989abc9e2 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -5860,8 +5860,8 @@ void ufshcd_compl_one_cqe(struct ufs_hba *hba, int task_tag,
struct ufshcd_lrb *lrbp = scsi_cmd_priv(cmd);
enum utp_ocs ocs;
- if (WARN_ONCE(!cmd, "cqe->command_desc_base_addr = %#llx\n",
- le64_to_cpu(cqe->command_desc_base_addr)))
+ if (WARN_ONCE(!cmd, "invalid completion tag %d, cqe->command_desc_base_addr = %#llx\n",
+ task_tag, cqe ? le64_to_cpu(cqe->command_desc_base_addr) : 0ULL))
return;
if (hba->monitor.enabled) {
--
2.43.0
^ permalink raw reply [flat|nested] 20+ messages in thread* [PATCH v2 3/6] scsi: ufs: core: Validate connected lane counts
2026-07-17 15:39 ` [PATCH v2 0/6] " Li Qiang
2026-07-17 15:39 ` [PATCH v2 1/6] scsi: ufs: core: Validate string descriptors Li Qiang
2026-07-17 15:39 ` [PATCH v2 2/6] scsi: ufs: Avoid NULL CQE dereference when reporting invalid tags Li Qiang
@ 2026-07-17 15:39 ` Li Qiang
2026-07-17 15:39 ` [PATCH v2 4/6] scsi: ufs: rpmb: Validate request frame length before parsing Li Qiang
` (2 subsequent siblings)
5 siblings, 0 replies; 20+ messages in thread
From: Li Qiang @ 2026-07-17 15:39 UTC (permalink / raw)
To: linux-scsi
Cc: bvanassche, linux-kernel, alim.akhtar, avri.altman,
James.Bottomley, martin.petersen, peter.wang, beanhuo, can.guo,
adrian.hunter, tomas.winkler
The connected lane count is used by TX equalization code to index
arrays sized by UFS_MAX_LANES. Reject zero and out-of-range RX or TX
lane counts before they can be propagated.
Fixes: 03e5d38e2f98 ("scsi: ufs: core: Add support for TX Equalization")
Signed-off-by: Li Qiang <liqiang01@kylinos.cn>
---
drivers/ufs/core/ufshcd.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index d1b989abc9e2..bbadddcd661a 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -4729,7 +4729,9 @@ static int ufshcd_get_max_pwr_mode(struct ufs_hba *hba)
ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES),
&pwr_info->lane_tx);
- if (!pwr_info->lane_rx || !pwr_info->lane_tx) {
+ if (!pwr_info->lane_rx || !pwr_info->lane_tx ||
+ pwr_info->lane_rx > UFS_MAX_LANES ||
+ pwr_info->lane_tx > UFS_MAX_LANES) {
dev_err(hba->dev, "%s: invalid connected lanes value. rx=%d, tx=%d\n",
__func__,
pwr_info->lane_rx,
--
2.43.0
^ permalink raw reply [flat|nested] 20+ messages in thread* [PATCH v2 4/6] scsi: ufs: rpmb: Validate request frame length before parsing
2026-07-17 15:39 ` [PATCH v2 0/6] " Li Qiang
` (2 preceding siblings ...)
2026-07-17 15:39 ` [PATCH v2 3/6] scsi: ufs: core: Validate connected lane counts Li Qiang
@ 2026-07-17 15:39 ` Li Qiang
2026-07-17 15:39 ` [PATCH v2 5/6] scsi: ufs: rpmb: Use unaligned accessors for RPMB frames Li Qiang
2026-07-17 15:39 ` [PATCH v2 6/6] scsi: ufs: debugfs: Reserve space for a string terminator Li Qiang
5 siblings, 0 replies; 20+ messages in thread
From: Li Qiang @ 2026-07-17 15:39 UTC (permalink / raw)
To: linux-scsi
Cc: bvanassche, linux-kernel, alim.akhtar, avri.altman,
James.Bottomley, martin.petersen, peter.wang, beanhuo, can.guo,
adrian.hunter, tomas.winkler
The RPMB core only verifies that request and response buffers are
nonempty. This callback reads req_resp at the end of the first request
frame before validating the request length.
Require a complete frame before that access.
Fixes: b06b8c421485 ("scsi: ufs: core: Add OP-TEE based RPMB driver for UFS devices")
Signed-off-by: Li Qiang <liqiang01@kylinos.cn>
---
drivers/ufs/core/ufs-rpmb.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/ufs/core/ufs-rpmb.c b/drivers/ufs/core/ufs-rpmb.c
index ffad049872b9..431f85b36876 100644
--- a/drivers/ufs/core/ufs-rpmb.c
+++ b/drivers/ufs/core/ufs-rpmb.c
@@ -69,6 +69,10 @@ static int ufs_rpmb_route_frames(struct device *dev, u8 *req, unsigned int req_l
hba = ufs_rpmb->hba;
+ /* req_resp is at the end of an RPMB frame. */
+ if (req_len < sizeof(*frm_out))
+ return -EINVAL;
+
req_type = be16_to_cpu(frm_out->req_resp);
switch (req_type) {
--
2.43.0
^ permalink raw reply [flat|nested] 20+ messages in thread* [PATCH v2 5/6] scsi: ufs: rpmb: Use unaligned accessors for RPMB frames
2026-07-17 15:39 ` [PATCH v2 0/6] " Li Qiang
` (3 preceding siblings ...)
2026-07-17 15:39 ` [PATCH v2 4/6] scsi: ufs: rpmb: Validate request frame length before parsing Li Qiang
@ 2026-07-17 15:39 ` Li Qiang
2026-07-17 15:39 ` [PATCH v2 6/6] scsi: ufs: debugfs: Reserve space for a string terminator Li Qiang
5 siblings, 0 replies; 20+ messages in thread
From: Li Qiang @ 2026-07-17 15:39 UTC (permalink / raw)
To: linux-scsi
Cc: bvanassche, linux-kernel, alim.akhtar, avri.altman,
James.Bottomley, martin.petersen, peter.wang, beanhuo, can.guo,
adrian.hunter, tomas.winkler
RPMB frame buffers are passed as u8 pointers and do not have an
alignment guarantee. Use unaligned accessors for the req_resp field.
Signed-off-by: Li Qiang <liqiang01@kylinos.cn>
---
drivers/ufs/core/ufs-rpmb.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/ufs/core/ufs-rpmb.c b/drivers/ufs/core/ufs-rpmb.c
index 431f85b36876..53f66b274aca 100644
--- a/drivers/ufs/core/ufs-rpmb.c
+++ b/drivers/ufs/core/ufs-rpmb.c
@@ -73,7 +73,7 @@ static int ufs_rpmb_route_frames(struct device *dev, u8 *req, unsigned int req_l
if (req_len < sizeof(*frm_out))
return -EINVAL;
- req_type = be16_to_cpu(frm_out->req_resp);
+ req_type = get_unaligned_be16(&frm_out->req_resp);
switch (req_type) {
case RPMB_PROGRAM_KEY:
@@ -111,7 +111,7 @@ static int ufs_rpmb_route_frames(struct device *dev, u8 *req, unsigned int req_l
struct rpmb_frame *frm_resp = (struct rpmb_frame *)resp;
memset(frm_resp, 0, sizeof(*frm_resp));
- frm_resp->req_resp = cpu_to_be16(RPMB_RESULT_READ);
+ put_unaligned_be16(RPMB_RESULT_READ, &frm_resp->req_resp);
ret = ufs_sec_submit(hba, protocol_id, resp, resp_len, true);
if (ret) {
dev_err(dev, "Result read request failed with ret=%d\n", ret);
--
2.43.0
^ permalink raw reply [flat|nested] 20+ messages in thread* [PATCH v2 6/6] scsi: ufs: debugfs: Reserve space for a string terminator
2026-07-17 15:39 ` [PATCH v2 0/6] " Li Qiang
` (4 preceding siblings ...)
2026-07-17 15:39 ` [PATCH v2 5/6] scsi: ufs: rpmb: Use unaligned accessors for RPMB frames Li Qiang
@ 2026-07-17 15:39 ` Li Qiang
5 siblings, 0 replies; 20+ messages in thread
From: Li Qiang @ 2026-07-17 15:39 UTC (permalink / raw)
To: linux-scsi
Cc: bvanassche, linux-kernel, alim.akhtar, avri.altman,
James.Bottomley, martin.petersen, peter.wang, beanhuo, can.guo,
adrian.hunter, tomas.winkler
ufs_saved_err_write() copies user input into a zero-initialized stack
buffer and passes it to kstrtoint(). A write that fills the entire
buffer overwrites its only terminator.
Reject an input whose length leaves no room for the trailing NUL.
Fixes: 7340faae9474 ("scsi: ufs: core: Add debugfs attributes for triggering the UFS EH")
Signed-off-by: Li Qiang <liqiang01@kylinos.cn>
---
drivers/ufs/core/ufs-debugfs.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/ufs/core/ufs-debugfs.c b/drivers/ufs/core/ufs-debugfs.c
index e3dd81d6fe82..be527209540d 100644
--- a/drivers/ufs/core/ufs-debugfs.c
+++ b/drivers/ufs/core/ufs-debugfs.c
@@ -165,7 +165,7 @@ static ssize_t ufs_saved_err_write(struct file *file, const char __user *buf,
char val_str[16] = { };
int val, ret;
- if (count > sizeof(val_str))
+ if (count >= sizeof(val_str))
return -EINVAL;
if (copy_from_user(val_str, buf, count))
return -EFAULT;
--
2.43.0
^ permalink raw reply [flat|nested] 20+ messages in thread