[PATCH] scsi: aacraid: fix DMA mapping leak in aac_send_raw_srb()
Ivy Lopez <[email protected]> Sat, 8 Aug 2026 21:34:49 -0600
| Newsgroups | gmane.linux.kernel,gmane.linux.scsi |
|---|---|
| Message-ID | <[email protected]> |
aac_send_raw_srb() maps each scatter/gather entry for DMA via dma_map_single() across five separate code paths, depending on the adapter type and SG format (native HBA, 64-bit host SG, 32-bit host SG, and two legacy formats). None of these mappings are ever undone: there is no dma_unmap_single() call anywhere in the file, on the success path or any of the error paths that funnel through the single cleanup label. Every FSACTL_SEND_RAW_SRB ioctl that submits at least one SG entry therefore leaks that many DMA mappings permanently. Under an IOMMU or SWIOTLB this is a genuinely exhaustible resource: sustained use (e.g. periodic smartctl -d aacraid,... polling) eventually drives new DMA mappings to fail, surfacing as intermittent I/O failures (aac_fib_send failing with -ENOMEM) and, left long enough, adapter resets and system instability. Fix this by tracking the DMA address returned from each of the five dma_map_single() calls in a new per-entry array (sg_addr[]), zero initialized alongside the existing sg_list[] array. In cleanup, unmap each entry before freeing its backing memory, using the sg_addr[i] != 0 check as a guard: some cleanup paths are reached after sg_indx has been advanced for entry i but before that entry's dma_map_single() call runs (e.g. a copy_from_user() failure), so not every index in the cleanup loop was actually mapped. Link: https://bugzilla.kernel.org/show_bug.cgi?id=220504 Signed-off-by: Ivy Lopez <[email protected]> --- drivers/scsi/aacraid/commctrl.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/aacraid/commctrl.c b/drivers/scsi/aacraid/commctrl.c index bd82aeb679ae..d4dffd6f8af0 100644 --- a/drivers/scsi/aacraid/commctrl.c +++ b/drivers/scsi/aacraid/commctrl.c @@ -492,6 +492,7 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg) u32 data_dir; void __user *sg_user[HBA_MAX_SG_EMBEDDED]; void *sg_list[HBA_MAX_SG_EMBEDDED]; + dma_addr_t sg_addr[HBA_MAX_SG_EMBEDDED]; u32 sg_count[HBA_MAX_SG_EMBEDDED]; u32 sg_indx = 0; u32 byte_count = 0; @@ -517,6 +518,7 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg) } memset(sg_list, 0, sizeof(sg_list)); /* cleanup may take issue */ + memset(sg_addr, 0, sizeof(sg_addr)); /* mark all entries unmapped */ if(copy_from_user(&fibsize, &user_srb->count,sizeof(u32))){ dprintk((KERN_DEBUG"aacraid: Could not copy data size from user\n")); rcode = -EFAULT; @@ -690,6 +692,7 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg) } addr = dma_map_single(&dev->pdev->dev, p, sg_count[i], data_dir); + sg_addr[i] = addr; hbacmd->sge[i].addr_hi = cpu_to_le32((u32)(addr>>32)); hbacmd->sge[i].addr_lo = cpu_to_le32( (u32)(addr & 0xffffffff)); @@ -752,7 +755,7 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg) } addr = dma_map_single(&dev->pdev->dev, p, sg_count[i], data_dir); - + sg_addr[i] = addr; psg->sg[i].addr[0] = cpu_to_le32(addr & 0xffffffff); psg->sg[i].addr[1] = cpu_to_le32(addr>>32); byte_count += sg_count[i]; @@ -808,7 +811,7 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg) } addr = dma_map_single(&dev->pdev->dev, p, sg_count[i], data_dir); - + sg_addr[i] = addr; psg->sg[i].addr[0] = cpu_to_le32(addr & 0xffffffff); psg->sg[i].addr[1] = cpu_to_le32(addr>>32); byte_count += sg_count[i]; @@ -865,7 +868,7 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg) addr = dma_map_single(&dev->pdev->dev, p, usg->sg[i].count, data_dir); - + sg_addr[i] = addr; psg->sg[i].addr = cpu_to_le32(addr & 0xffffffff); byte_count += usg->sg[i].count; psg->sg[i].count = cpu_to_le32(sg_count[i]); @@ -905,7 +908,7 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg) } addr = dma_map_single(&dev->pdev->dev, p, sg_count[i], data_dir); - + sg_addr[i] = addr; psg->sg[i].addr = cpu_to_le32(addr); byte_count += sg_count[i]; psg->sg[i].count = cpu_to_le32(sg_count[i]); @@ -986,8 +989,12 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg) cleanup: kfree(user_srbcmd); if (rcode != -ERESTARTSYS) { - for (i = 0; i <= sg_indx; i++) + for (i = 0; i <= sg_indx; i++) { + if (sg_addr[i]) + dma_unmap_single(&dev->pdev->dev, sg_addr[i], + sg_count[i], data_dir); kfree(sg_list[i]); + } aac_fib_complete(srbfib); aac_fib_free(srbfib); } -- 2.55.0