* Re: [PATCH] scsi: mptfusion: Fix array out of bounds error
2026-06-16 12:45 [PATCH] scsi: mptfusion: Fix array out of bounds error Alexey Gladkov
@ 2026-06-16 13:04 ` sashiko-bot
2026-06-16 14:41 ` Alexey Gladkov
2026-06-16 15:29 ` [PATCH v2] " Alexey Gladkov
1 sibling, 1 reply; 5+ messages in thread
From: sashiko-bot @ 2026-06-16 13:04 UTC (permalink / raw)
To: Alexey Gladkov; +Cc: linux-scsi
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Incomplete fix: Out-of-bounds array access in fusion sub-drivers due to unclamped ioc->facts.NumberOfPorts
--
commit c993313579593bc432c039b88b1e748a26bc690c
Author: Alexey Gladkov <legion@kernel.org>
scsi: mptfusion: Fix array out of bounds error
The driver retrieves the number of ports from the hardware. However, the
driver can handle no more than two such ports. It uses a fixed array for
them.
> diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
> index 3a431ffd3e2eb..05bd556bb9383 100644
> --- a/drivers/message/fusion/mptbase.c
> +++ b/drivers/message/fusion/mptbase.c
> @@ -3257,6 +3257,8 @@ GetPortFacts(MPT_ADAPTER *ioc, int portnum, int sleepFlag)
> return -4;
> }
>
> + BUG_ON(portnum < 0 || portnum >= ARRAY_SIZE(ioc->pfacts));
> +
> pfacts = &ioc->pfacts[portnum];
[ ... ]
> @@ -6701,6 +6703,7 @@ static int mpt_iocinfo_proc_show(struct seq_file *m, void *v)
> char expVer[32];
> int sz;
> int p;
> + int numberOfPorts = MIN(ioc->facts.NumberOfPorts, ARRAY_SIZE(ioc->pfacts));
>
> mpt_get_fw_exp_ver(expVer, ioc);
>
> @@ -6755,7 +6758,7 @@ static int mpt_iocinfo_proc_show(struct seq_file *m, void *v)
> seq_printf(m, " MaxBuses = %d\n", ioc->facts.MaxBuses);
>
> /* per-port info */
> - for (p=0; p < ioc->facts.NumberOfPorts; p++) {
> + for (p = 0; p < numberOfPorts; p++) {
[Severity: Critical]
This is a pre-existing issue, but by only capping numberOfPorts locally here,
does this leave the rest of the fusion sub-drivers vulnerable to out-of-bounds
accesses?
The unclamped ioc->facts.NumberOfPorts is still used in several loops across
the driver, where the bounds of statically sized arrays (like pfacts or
fc_port_page1, both size 2) are exceeded if hardware reports more than 2
ports.
For instance, in drivers/message/fusion/mptfc.c:mptfc_remove():
for (ii=0; ii<ioc->facts.NumberOfPorts; ii++) {
if (ioc->fc_data.fc_port_page1[ii].data) {
dma_free_coherent(&ioc->pcidev->dev,
ioc->fc_data.fc_port_page1[ii].pg_sz,
ioc->fc_data.fc_port_page1[ii].data,
ioc->fc_data.fc_port_page1[ii].dma);
Since fc_port_page1 is size 2, if NumberOfPorts is 8, could this read
adjacent struct members of MPT_ADAPTER and pass them to dma_free_coherent(),
leading to arbitrary invalid frees and a kernel panic?
Similar loops iterating over pfacts exist in mptfc_probe(), mptsas_probe(),
mptspi_probe(), and mptlan_probe().
Would it be safer to clamp ioc->facts.NumberOfPorts directly after retrieving
it from firmware, so all sub-drivers are protected?
> seq_printf(m, " PortNumber = %d (of %d)\n",
> p+1,
> ioc->facts.NumberOfPorts);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260616124528.319527-1-legion@kernel.org?part=1
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH v2] scsi: mptfusion: Fix array out of bounds error
2026-06-16 12:45 [PATCH] scsi: mptfusion: Fix array out of bounds error Alexey Gladkov
2026-06-16 13:04 ` sashiko-bot
@ 2026-06-16 15:29 ` Alexey Gladkov
2026-06-16 15:53 ` sashiko-bot
1 sibling, 1 reply; 5+ messages in thread
From: Alexey Gladkov @ 2026-06-16 15:29 UTC (permalink / raw)
To: Sathya Prakash, Sreekanth Reddy, Suganath Prabu Subramani
Cc: MPT-FusionLinux.pdl, linux-scsi, linux-kernel, legion
The driver retrieves the number of ports from the hardware. However, the
driver can handle no more than two such ports. It uses a fixed array for
them.
We use NumberOfPorts without checking, and maybe on actual hardware
there really are never more than two ports, but QEMU passes 8 [1][2].
[1] https://gitlab.com/qemu-project/qemu/-/blob/master/hw/scsi/mptsas.h?ref_type=heads#L7
[2] https://gitlab.com/qemu-project/qemu/-/blob/master/hw/scsi/mptsas.c?ref_type=heads#L619
Signed-off-by: Alexey Gladkov <legion@kernel.org>
---
v2:
* Replaced the retrieval of MPT_ADAPTER from pci_get_drvdata with a wrapper that
checks the array boundaries.
* Replaced the magic number of array elements with a macro because these arrays
are associated.
---
drivers/message/fusion/mptbase.c | 22 +++++++++++++++++++---
drivers/message/fusion/mptbase.h | 9 ++++++---
drivers/message/fusion/mptctl.c | 2 +-
drivers/message/fusion/mptfc.c | 4 ++--
drivers/message/fusion/mptlan.c | 4 ++--
drivers/message/fusion/mptsas.c | 6 +++---
drivers/message/fusion/mptscsih.c | 6 +++---
drivers/message/fusion/mptspi.c | 6 +++---
8 files changed, 39 insertions(+), 20 deletions(-)
diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
index 3a431ffd3e2e..9e738d0bb8e3 100644
--- a/drivers/message/fusion/mptbase.c
+++ b/drivers/message/fusion/mptbase.c
@@ -1741,6 +1741,21 @@ mpt_mapresources(MPT_ADAPTER *ioc)
return r;
}
+MPT_ADAPTER *
+mpt_get_adapter(struct pci_dev *pdev)
+{
+ MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
+
+ if (ioc && ioc->facts.NumberOfPorts >= ARRAY_SIZE(ioc->pfacts)) {
+ ioc->facts.NumberOfPorts = ARRAY_SIZE(ioc->pfacts);
+ }
+
+ BUILD_BUG_ON(ARRAY_SIZE(ioc->pfacts) != ARRAY_SIZE(ioc->fc_port_page0));
+ BUILD_BUG_ON(ARRAY_SIZE(ioc->pfacts) != ARRAY_SIZE(ioc->fc_data.fc_port_page1));
+
+ return ioc;
+}
+
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
* mpt_attach - Install a PCI intelligent MPT adapter.
@@ -2074,7 +2089,7 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
void
mpt_detach(struct pci_dev *pdev)
{
- MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
+ MPT_ADAPTER *ioc = mpt_get_adapter(pdev);
char pname[64];
u8 cb_idx;
unsigned long flags;
@@ -2140,7 +2155,7 @@ int
mpt_suspend(struct pci_dev *pdev, pm_message_t state)
{
u32 device_state;
- MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
+ MPT_ADAPTER *ioc = mpt_get_adapter(pdev);
device_state = pci_choose_state(pdev, state);
printk(MYIOC_s_INFO_FMT "pci-suspend: pdev=0x%p, slot=%s, Entering "
@@ -2179,7 +2194,7 @@ mpt_suspend(struct pci_dev *pdev, pm_message_t state)
int
mpt_resume(struct pci_dev *pdev)
{
- MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
+ MPT_ADAPTER *ioc = mpt_get_adapter(pdev);
u32 device_state = pdev->current_state;
int recovery_state;
int err;
@@ -8451,6 +8466,7 @@ EXPORT_SYMBOL(mpt_reset_register);
EXPORT_SYMBOL(mpt_reset_deregister);
EXPORT_SYMBOL(mpt_device_driver_register);
EXPORT_SYMBOL(mpt_device_driver_deregister);
+EXPORT_SYMBOL(mpt_get_adapter);
EXPORT_SYMBOL(mpt_get_msg_frame);
EXPORT_SYMBOL(mpt_put_msg_frame);
EXPORT_SYMBOL(mpt_put_msg_frame_hi_pri);
diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h
index b406fd676da0..ca0a873af01e 100644
--- a/drivers/message/fusion/mptbase.h
+++ b/drivers/message/fusion/mptbase.h
@@ -113,6 +113,8 @@
#define MPT_PROCFS_SUMMARY_ALL_PATHNAME "/proc/" MPT_PROCFS_SUMMARY_ALL_NODE
#define MPT_FW_REV_MAGIC_ID_STRING "FwRev="
+#define MPT_MAX_PORT_FACTS 2
+
#define MPT_MAX_REQ_DEPTH 1023
#define MPT_DEFAULT_REQ_DEPTH 256
#define MPT_MIN_REQ_DEPTH 128
@@ -537,7 +539,7 @@ typedef struct _FcCfgData {
FCPortPage1_t *data;
dma_addr_t dma;
int pg_sz;
- } fc_port_page1[2];
+ } fc_port_page1[MPT_MAX_PORT_FACTS];
} FcCfgData;
#define MPT_RPORT_INFO_FLAGS_REGISTERED 0x01 /* rport registered */
@@ -699,8 +701,8 @@ typedef struct _MPT_ADAPTER
u32 hs_req[MPT_MAX_FRAME_SIZE/sizeof(u32)];
u16 hs_reply[MPT_MAX_FRAME_SIZE/sizeof(u16)];
IOCFactsReply_t facts;
- PortFactsReply_t pfacts[2];
- FCPortPage0_t fc_port_page0[2];
+ PortFactsReply_t pfacts[MPT_MAX_PORT_FACTS];
+ FCPortPage0_t fc_port_page0[MPT_MAX_PORT_FACTS];
LANPage0_t lan_cnfg_page0;
LANPage1_t lan_cnfg_page1;
@@ -918,6 +920,7 @@ extern int mpt_reset_register(u8 cb_idx, MPT_RESETHANDLER reset_func);
extern void mpt_reset_deregister(u8 cb_idx);
extern int mpt_device_driver_register(struct mpt_pci_driver * dd_cbfunc, u8 cb_idx);
extern void mpt_device_driver_deregister(u8 cb_idx);
+extern MPT_ADAPTER *mpt_get_adapter(struct pci_dev *pdev);
extern MPT_FRAME_HDR *mpt_get_msg_frame(u8 cb_idx, MPT_ADAPTER *ioc);
extern void mpt_free_msg_frame(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf);
extern void mpt_put_msg_frame(u8 cb_idx, MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf);
diff --git a/drivers/message/fusion/mptctl.c b/drivers/message/fusion/mptctl.c
index 77fa55df70d0..8023062431e1 100644
--- a/drivers/message/fusion/mptctl.c
+++ b/drivers/message/fusion/mptctl.c
@@ -2849,7 +2849,7 @@ static long compat_mpctl_ioctl(struct file *f, unsigned int cmd, unsigned long a
static int
mptctl_probe(struct pci_dev *pdev)
{
- MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
+ MPT_ADAPTER *ioc = mpt_get_adapter(pdev);
mutex_init(&ioc->ioctl_cmds.mutex);
init_completion(&ioc->ioctl_cmds.done);
diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c
index b55deb988ad9..3c00f4c88343 100644
--- a/drivers/message/fusion/mptfc.c
+++ b/drivers/message/fusion/mptfc.c
@@ -1219,7 +1219,7 @@ mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if ((r = mpt_attach(pdev,id)) != 0)
return r;
- ioc = pci_get_drvdata(pdev);
+ ioc = mpt_get_adapter(pdev);
ioc->DoneCtx = mptfcDoneCtx;
ioc->TaskCtx = mptfcTaskCtx;
ioc->InternalCtx = mptfcInternalCtx;
@@ -1525,7 +1525,7 @@ mptfc_init(void)
*/
static void mptfc_remove(struct pci_dev *pdev)
{
- MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
+ MPT_ADAPTER *ioc = mpt_get_adapter(pdev);
struct mptfc_rport_info *p, *n;
struct workqueue_struct *work_q;
unsigned long flags;
diff --git a/drivers/message/fusion/mptlan.c b/drivers/message/fusion/mptlan.c
index f7fc5cc04b92..ccd735aa532a 100644
--- a/drivers/message/fusion/mptlan.c
+++ b/drivers/message/fusion/mptlan.c
@@ -1380,7 +1380,7 @@ mpt_register_lan_device (MPT_ADAPTER *mpt_dev, int pnum)
static int
mptlan_probe(struct pci_dev *pdev)
{
- MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
+ MPT_ADAPTER *ioc = mpt_get_adapter(pdev);
struct net_device *dev;
int i;
@@ -1426,7 +1426,7 @@ mptlan_probe(struct pci_dev *pdev)
static void
mptlan_remove(struct pci_dev *pdev)
{
- MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
+ MPT_ADAPTER *ioc = mpt_get_adapter(pdev);
struct net_device *dev = ioc->netdev;
struct mpt_lan_priv *priv = netdev_priv(dev);
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c
index c362f09a8c55..b328c8685192 100644
--- a/drivers/message/fusion/mptsas.c
+++ b/drivers/message/fusion/mptsas.c
@@ -5163,7 +5163,7 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (r)
return r;
- ioc = pci_get_drvdata(pdev);
+ ioc = mpt_get_adapter(pdev);
mptsas_fw_event_off(ioc);
ioc->DoneCtx = mptsasDoneCtx;
ioc->TaskCtx = mptsasTaskCtx;
@@ -5337,7 +5337,7 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
static void
mptsas_shutdown(struct pci_dev *pdev)
{
- MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
+ MPT_ADAPTER *ioc = mpt_get_adapter(pdev);
mptsas_fw_event_off(ioc);
mptsas_cleanup_fw_event_q(ioc);
@@ -5345,7 +5345,7 @@ mptsas_shutdown(struct pci_dev *pdev)
static void mptsas_remove(struct pci_dev *pdev)
{
- MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
+ MPT_ADAPTER *ioc = mpt_get_adapter(pdev);
struct mptsas_portinfo *p, *n;
int i;
diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c
index ec6edcc4ef56..290939225688 100644
--- a/drivers/message/fusion/mptscsih.c
+++ b/drivers/message/fusion/mptscsih.c
@@ -1171,7 +1171,7 @@ mptscsih_report_queue_full(struct scsi_cmnd *sc, SCSIIOReply_t *pScsiReply, SCSI
void
mptscsih_remove(struct pci_dev *pdev)
{
- MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
+ MPT_ADAPTER *ioc = mpt_get_adapter(pdev);
struct Scsi_Host *host = ioc->sh;
MPT_SCSI_HOST *hd;
int sz1;
@@ -1228,7 +1228,7 @@ mptscsih_shutdown(struct pci_dev *pdev)
int
mptscsih_suspend(struct pci_dev *pdev, pm_message_t state)
{
- MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
+ MPT_ADAPTER *ioc = mpt_get_adapter(pdev);
scsi_block_requests(ioc->sh);
mptscsih_shutdown(pdev);
@@ -1244,7 +1244,7 @@ mptscsih_suspend(struct pci_dev *pdev, pm_message_t state)
int
mptscsih_resume(struct pci_dev *pdev)
{
- MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
+ MPT_ADAPTER *ioc = mpt_get_adapter(pdev);
int rc;
rc = mpt_resume(pdev);
diff --git a/drivers/message/fusion/mptspi.c b/drivers/message/fusion/mptspi.c
index 56892b1f3de2..ad92c23bd632 100644
--- a/drivers/message/fusion/mptspi.c
+++ b/drivers/message/fusion/mptspi.c
@@ -1330,7 +1330,7 @@ mptspi_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
static int
mptspi_resume(struct pci_dev *pdev)
{
- MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
+ MPT_ADAPTER *ioc = mpt_get_adapter(pdev);
struct _MPT_SCSI_HOST *hd = shost_priv(ioc->sh);
int rc;
@@ -1367,7 +1367,7 @@ mptspi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if ((r = mpt_attach(pdev,id)) != 0)
return r;
- ioc = pci_get_drvdata(pdev);
+ ioc = mpt_get_adapter(pdev);
ioc->DoneCtx = mptspiDoneCtx;
ioc->TaskCtx = mptspiTaskCtx;
ioc->InternalCtx = mptspiInternalCtx;
@@ -1546,7 +1546,7 @@ mptspi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
static void mptspi_remove(struct pci_dev *pdev)
{
- MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
+ MPT_ADAPTER *ioc = mpt_get_adapter(pdev);
scsi_remove_host(ioc->sh);
mptscsih_remove(pdev);
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread