[PATCH] scsi: mptfusion: reject events with a MsgLength outside the reply frame

From: Yehyeong Lee

Date: Sat Oct 10 2026 - 05:15:08 EST


mptsas_event_process() sizes the event payload from the reply header:

event_data_sz = ((reply->MsgLength * 4) -
offsetof(EventNotificationReply_t, Data));

MsgLength is an 8-bit field chosen by the controller firmware and is not
validated on the way in; ProcessEventNotification() only looks at
EventDataLength. event_data_sz is a signed int but is then used as a
size_t in both the kzalloc() and the memcpy() that follow:

fw_event = kzalloc(sizeof(*fw_event) + event_data_sz, GFP_ATOMIC);
...
memcpy(fw_event->event_data, reply->Data, event_data_sz);

A MsgLength too small to cover the 28-byte reply header (MsgLength <= 6)
makes event_data_sz negative; it sign-extends to a huge size_t, so the
kzalloc() under-allocates and the memcpy() runs for ~2^64 bytes. A
MsgLength larger than the reply frame makes the memcpy() read past the
frame and leak up to ~940 bytes of the adjacent reply.

Reproduced under KASAN with a crafted EventNotificationReply (reply_sz
0x50); MsgLength 255 over-reads the frame, MsgLength 4 wraps the length:

BUG: KASAN: slab-out-of-bounds in mptsas_event_process [mptsas]
Read of size 992
which belongs to the cache kmalloc-96 of size 96

BUG: KASAN: out-of-bounds in mptsas_event_process [mptsas]
Read of size 18446744073709551604

reply points at a fixed MPT_REPLY_FRAME_SIZE frame, so a valid event has
7 <= MsgLength <= ioc->reply_sz / 4. Compute event_data_sz before the
switch and drop the event when MsgLength is outside that range, as
mptsas_mgmt_done() and mptctl_reply() already bound it to ioc->reply_sz.
Doing it before the switch also keeps a dropped BROADCAST_PRIMITIVE from
leaving ioc->broadcast_aen_busy set.

Fixes: 3eb0822c6740 ("[SCSI] mpt fusion: Firmware event implementation using seperate WorkQueue")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Yehyeong Lee <yhlee@xxxxxxxxxxxxxxxxxx>
---
drivers/message/fusion/mptsas.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c
index c362f09a8c555..1812b871c47ef 100644
--- a/drivers/message/fusion/mptsas.c
+++ b/drivers/message/fusion/mptsas.c
@@ -5009,6 +5009,18 @@ mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply)
return 0;

delay = msecs_to_jiffies(1);
+
+ /* Bound the firmware-chosen MsgLength to the reply frame: too small
+ * underflows event_data_sz, too large over-reads the copy.
+ */
+ event_data_sz = ((reply->MsgLength * 4) -
+ offsetof(EventNotificationReply_t, Data));
+ if (event_data_sz < 0 || reply->MsgLength * 4 > ioc->reply_sz) {
+ printk(MYIOC_s_WARN_FMT "%s: bad MsgLength %u\n",
+ ioc->name, __func__, reply->MsgLength);
+ return 0;
+ }
+
switch (event) {
case MPI_EVENT_SAS_BROADCAST_PRIMITIVE:
{
@@ -5105,8 +5117,6 @@ mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply)
return 0;
}

- event_data_sz = ((reply->MsgLength * 4) -
- offsetof(EventNotificationReply_t, Data));
fw_event = kzalloc(sizeof(*fw_event) + event_data_sz, GFP_ATOMIC);
if (!fw_event) {
printk(MYIOC_s_WARN_FMT "%s: failed at (line=%d)\n", ioc->name,
--
2.43.0