[PATCH] scsi: sd: validate device-supplied mode sense lengths in cache_type_store
From: Jay Vadayath
Date: Fri Jul 17 2026 - 15:10:50 EST
A malicious/emulated USB mass storage device (or any SCSI target) can
return a MODE SENSE(6/10) response whose header_length plus
block_descriptor_length is >= the size of the on-stack 64-byte buffer.
cache_type_store() computed
buffer_data = buffer + data.header_length + data.block_descriptor_length
and then dereferenced buffer_data[0] and buffer_data[2] without ever
checking that this offset still lies within buffer[]. With a reported
block_descriptor_length of 60 and a 4-byte MODE SENSE(6) header the
offset becomes 64 (== sizeof(buffer)), so buffer_data[0]/buffer_data[2]
read and write past the end of the stack buffer. The subsequent length
computation could also underflow and let buffer_data + len run past the
buffer when handed to scsi_mode_select().
KASAN report from an unprivileged user writing to the sysfs cache_type
attribute of a device backed by a raw-gadget mass storage emulator:
BUG: KASAN: stack-out-of-bounds in cache_type_store+0x8ba/0x8f0
Read of size 1 at addr ffff888003097c72 by task poc/57
Call Trace:
dump_stack_lvl+0x53/0x70
print_report+0xce/0x610
kasan_report+0xce/0x100
cache_type_store+0x8ba/0x8f0
kernfs_fop_write_iter+0x384/0x4f0
vfs_write+0x5c7/0xe60
ksys_write+0xf7/0x1c0
do_syscall_64+0x61/0x480
entry_SYSCALL_64_after_hwframe+0x76/0x7e
Reject responses whose mode page offset does not leave room for the
three caching-mode-page bytes we touch, and bound the length by the
space actually remaining in the buffer, mirroring the careful bounds
checking already done in sd_read_cache_type().
This bug was discovered by Artiphishell's vTriage pipeline, which
generated a userspace raw-gadget reproducer that reliably triggers the
KASAN report on an unpatched kernel. The fix below was drafted with the
Claude coding assistant; a userspace reproducer is available on
request.
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Jay Vadayath <jay@xxxxxxxxxxxxxxxx>
---
drivers/scsi/sd.c | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -228,7 +228,7 @@ cache_type_store(struct device *dev, struct device_attribute *attr,
struct scsi_mode_data data;
struct scsi_sense_hdr sshdr;
static const char temp[] = "temporary ";
- int len, ret;
+ int len, offset, ret;
if (sdp->type != TYPE_DISK && sdp->type != TYPE_ZBC)
/* no cache control on RBC devices; theoretically they
@@ -265,13 +265,22 @@ cache_type_store(struct device *dev, struct device_attribute *attr,
return count;
}
if (scsi_mode_sense(sdp, 0x08, 8, 0, buffer, sizeof(buffer), SD_TIMEOUT,
sdkp->max_retries, &data, NULL))
return -EINVAL;
- len = min_t(size_t, sizeof(buffer), data.length - data.header_length -
- data.block_descriptor_length);
- buffer_data = buffer + data.header_length +
- data.block_descriptor_length;
+
+ /*
+ * The mode parameter header and block descriptor lengths are
+ * supplied by the device and must not be trusted (e.g. a malicious
+ * USB mass storage device). Reject responses that would place the
+ * caching mode page (of which we touch the first three bytes)
+ * outside of the buffer to avoid an out-of-bounds access below.
+ */
+ offset = data.header_length + data.block_descriptor_length;
+ if (offset + 3 > sizeof(buffer))
+ return -EINVAL;
+ len = min_t(size_t, sizeof(buffer) - offset, data.length - offset);
+ buffer_data = buffer + offset;
buffer_data[2] &= ~0x05;
buffer_data[2] |= wce << 2 | rcd;
sp = buffer_data[0] & 0x80 ? 1 : 0;