scsi: ufs: core: Validate string descriptors

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: 4b828fe156 ("scsi: ufs: revamp string descriptor reading")
Fixes: d794b499f9 ("scsi: ufs: core: fix incorrect buffer duplication in ufshcd_read_string_desc()")
Signed-off-by: Li Qiang <liqiang01@kylinos.cn>
Reviewed-by: Peter Wang <peter.wang@mediatek.com>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Link: https://patch.msgid.link/20260717153914.26321-2-liqiang01@kylinos.cn
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Li Qiang 2026-07-17 23:39:08 +08:00 committed by Martin K. Petersen
parent dc59e4fea9
commit d96e83d028

View File

@ -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: