[PATCH v2] scsi: mpt3sas: fix OOB SGE write at the request frame boundary

From: Weibin Liu

Date: Fri Oct 16 2026 - 03:09:52 EST


The MPT3COMMAND ioctl validates the user-supplied data_sge_offset
with

if (karg.data_sge_offset * 4 > ioc->request_sz ||
karg.data_sge_offset > (UINT_MAX / 4))

which only keeps the start of the SGE array,

psge = (void *)request + (karg.data_sge_offset * 4),

inside the request frame. The SGE builders, however, write complete
SGEs at psge: _base_build_sg() and _base_build_sg_ieee() emit up to
two SGEs for dual-direction requests, and the MCTP passthrough path
always builds both an H2D and a D2H SGE. When data_sge_offset * 4
is within 2 * sge_size bytes of the end of the request frame, those
writes cross the frame boundary and corrupt the adjacent frame in
the contiguous request DMA pool.

Require room for two SGEs of the largest format in use past
data_sge_offset, mirroring the max_sge_elements computation in
_base_get_ioc_facts(), so that the SGE builders can never write
outside the request frame:

sge_room = 2 * max_t(u16, ioc->sge_size, ioc->sge_size_ieee);
if (sge_room > ioc->request_sz ||
karg.data_sge_offset * 4 > ioc->request_sz - sge_room ||
karg.data_sge_offset > (UINT_MAX / 4))

Fixes: f92363d12359 ("[SCSI] mpt3sas: add new driver supporting 12GB SAS")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Weibin Liu <liuwb@xxxxxxxxxxxx>
Reviewed-by: Guanglei Zhu <zhugl3@xxxxxxxxxxxx>
---
Changes in v2:
- v1 only rejected offsets that place psge at or past the end of the
request frame. As the Sashiko AI review of v1 pointed out, offsets
that merely leave less room than the SGE builders need (up to two
SGEs, 16 bytes each with 64-bit DMA) still allowed the SGE writes
to cross the frame boundary. Require room for two SGEs of the
largest format in use so that no builder can write outside the
request frame.

drivers/scsi/mpt3sas/mpt3sas_ctl.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.c b/drivers/scsi/mpt3sas/mpt3sas_ctl.c
index 8bb947004885..2fef6aa83d34 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_ctl.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.c
@@ -716,6 +716,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg,
size_t data_in_sz = 0;
long ret;
u16 device_handle = MPT3SAS_INVALID_DEVICE_HANDLE;
+ u16 sge_room;
int tm_ret;

issue_reset = 0;
@@ -738,8 +739,16 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg,
goto out;
}

- /* Check for overflow and wraparound */
- if (karg.data_sge_offset * 4 > ioc->request_sz ||
+ /*
+ * Check for overflow and wraparound. The SGE builders write up
+ * to two SGEs at psge (dual-direction requests get a WRITE and a
+ * READ SGE, and the MCTP passthrough path always builds an H2D
+ * and a D2H SGE), so the request frame must have room for two of
+ * the largest SGE format in use past data_sge_offset.
+ */
+ sge_room = 2 * max_t(u16, ioc->sge_size, ioc->sge_size_ieee);
+ if (sge_room > ioc->request_sz ||
+ karg.data_sge_offset * 4 > ioc->request_sz - sge_room ||
karg.data_sge_offset > (UINT_MAX / 4)) {
ret = -EINVAL;
goto out;
--
2.50.1