The function iop_get_config_itl() reads a 32‑bit offset (req32) from the inbound queue register (hba->u.itl.iop->inbound_queue) and then uses it without validation to compute: req = (base + req32) followed by memcpy_fromio(config, req, sizeof(*config)).
Without verifying that req32 is within the valid I/O region and that req32 + sizeof(*config) does not overflow the mapped I/O region, a malicious or faulty device/firmware could cause the driver to read memory outside the intended request structure — leading to an out‑of‑bounds I/O read.
According to kernel documentation: "The value returned from the inbound queue port is an offset relative to the IOP BAR0." ([docs.kernel.org](https://docs.kernel.org/scsi/hptiop.html)) However, the documentation does *not* specify a maximum offset, nor a bound such as “offset + size ≤ IOP memory region size”.
In the driver code, hptiop_map_pci_bar_itl() does: hba->u.itl.iop = hptiop_map_pci_bar(hba, 0); and uses pci_resource_len(hba->pcidev, 0) to obtain the mapped region size for BAR0. Therefore we can use that size at runtime to bound req32 safely.
To implement the fix in iop_get_config_itl(): - Retrieve the BAR0 region size via: struct pci_dev *pcidev = hba->pcidev; u32 length = pci_resource_len(pcidev, 0); - Then check: if (req32 == IOPMU_QUEUE_EMPTY || req32 + sizeof(*config) > length) return -EINVAL; - This ensures we do not rely on a hard‑coded maximum, but use the actual mapped region size for the bound.
Fixes: ede1e6f8b4324 ("[SCSI] hptiop: HighPoint RocketRAID 3xxx controller driver") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li lgs201920130244@gmail.com --- drivers/scsi/hptiop.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/scsi/hptiop.c b/drivers/scsi/hptiop.c index f18b770626e6..c01370893a81 100644 --- a/drivers/scsi/hptiop.c +++ b/drivers/scsi/hptiop.c @@ -404,7 +404,10 @@ static int iop_get_config_itl(struct hptiop_hba *hba, struct hpt_iop_request_get_config __iomem *req;
req32 = readl(&hba->u.itl.iop->inbound_queue); - if (req32 == IOPMU_QUEUE_EMPTY) + + struct pci_dev *pcidev = hba->pcidev; + u32 length = pci_resource_len(pcidev, 0); + if (req32 == IOPMU_QUEUE_EMPTY || req32 + sizeof(*config) > length) return -1;
req = (struct hpt_iop_request_get_config __iomem *)