drivers/scsi/ses.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-)
From: Bryam Vargas <hexlabsecurity@proton.me>
ses_intf_add() stores the enclosure's page 1 type-descriptor pointer and
count (page1_types, page1_num_types); the page 2 walks,
ses_enclosure_data_process() and the logical-id read in ses_show_id() all
trust them. Three device-supplied values go unbounded: the enclosure-
descriptor skip loop reads a descriptor's length bytes while only checking
its first byte is in range; the stored count is a device sum unrelated to
what page 1 carries; and it is truncated into a short before being range-
checked. A short or inflated page 1 thus drives an out-of-bounds read of
adjacent slab, some of which ses_show_id() returns to user space.
Bound the skip loop to a whole descriptor and validate the geometry once
where it is parsed: reject a page too short for its logical id, a type
array that ran past the buffer, or a count larger than the array holds --
checking it before it is stored also keeps it inside the short. Conforming
enclosures are unaffected.
Fixes: 9927c68864e9 ("[SCSI] ses: add new Enclosure ULD")
Closes: https://sashiko.dev/#/patchset/20260711-b4-disp-26cc3226-v1-1-e04c11c88996@proton.me?part=1
Cc: stable@vger.kernel.org
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
---
Changes since v1 [1], addressing the Sashiko review of that posting:
- Bound the enclosure-descriptor skip loop itself (type_ptr + 4 <= buf +
len). v1 validated only after that loop ran, so a page 1 a few bytes
short of a descriptor was still read out of bounds inside the loop
before the check.
- Validate the int type count before it is stored, not the short it
truncates to. A device count of 65280 wraps a signed short to -256,
which slipped past the v1 range check; the int is bounded to
(buf+len-type_ptr)/4, below the short's range, so it cannot wrap once
stored.
Verified with an in-kernel KASAN model (7.2.0-rc1, kasan.fault=report) that
replays the skip loop over a real kzalloc'd page 1:
- old guard, 10-byte page 1 -> BUG: KASAN: slab-out-of-bounds read, 1 byte,
0 bytes to the right of the region; the tightened guard stops before it.
- count 65280 -> the pre-store int check rejects it, the post-store short
check (v1) did not.
- a conforming page 1 walks unchanged.
Reproducer available on request.
[1] https://lore.kernel.org/all/20260711-b4-disp-26cc3226-v1-1-e04c11c88996@proton.me
---
drivers/scsi/ses.c | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c
index 4c348645b04e..907bb489252a 100644
--- a/drivers/scsi/ses.c
+++ b/drivers/scsi/ses.c
@@ -742,11 +742,28 @@ static int ses_intf_add(struct device *cdev)
/* begin at the enclosure descriptor */
type_ptr = buf + 8;
/* skip all the enclosure descriptors */
- for (i = 0; i < num_enclosures && type_ptr < buf + len; i++) {
+ for (i = 0; i < num_enclosures && type_ptr + 4 <= buf + len; i++) {
types += type_ptr[2];
type_ptr += type_ptr[3] + 4;
}
+ /*
+ * Validate the device-reported page 1 geometry before the accessors
+ * walk it. page1_types and page1_num_types come straight from the
+ * enclosure; the page 2 descriptor walks (ses_get_page2_descriptor(),
+ * ses_set_page2_descriptor()), ses_enclosure_data_process() and the
+ * logical-id read in ses_show_id() all trust them. Reject a page 1
+ * shorter than its logical id, one whose descriptors ran past the end,
+ * or one declaring more type descriptors than it carries. Bounding the
+ * count before it is stored also keeps it within its short.
+ */
+ if (len < 8 + 4 + (int)sizeof(u64) ||
+ type_ptr > buf + len ||
+ types > (buf + len - type_ptr) / 4) {
+ err = -EINVAL;
+ goto err_free;
+ }
+
ses_dev->page1_types = type_ptr;
ses_dev->page1_num_types = types;
---
base-commit: dd3210c47e8d3ac6b4e9141fc68acc03b38c0ba3
change-id: 20260711-b4-disp-414d08d4-70013715f352
Best regards,
--
Bryam Vargas <hexlabsecurity@proton.me>
© 2016 - 2026 Red Hat, Inc.