[PATCH] misc: bcm-vk: fix DMA allocation memory leak
From: Guangshuo Li
Date: Tue Oct 27 2026 - 11:33:16 EST
bcm_vk_dma_alloc() leaks dma->pages and pinned user-page references
when page pinning or coherent SG allocation fails. DMA mapping
failures can also leave the SG list and earlier mappings unreleased,
as bcm_vk_sg_alloc() skips the partially initialized slot.
This issue was found by our static analysis tool under development.
Fixes: 111d746bb476 ("misc: bcm-vk: add VK messaging support")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Guangshuo Li <lgs201920130244@xxxxxxxxx>
---
drivers/misc/bcm-vk/bcm_vk_sg.c | 43 +++++++++++++++++++++++++++------
1 file changed, 36 insertions(+), 7 deletions(-)
diff --git a/drivers/misc/bcm-vk/bcm_vk_sg.c b/drivers/misc/bcm-vk/bcm_vk_sg.c
index 8662bb865129..b237a5332b7f 100644
--- a/drivers/misc/bcm-vk/bcm_vk_sg.c
+++ b/drivers/misc/bcm-vk/bcm_vk_sg.c
@@ -78,7 +78,8 @@ static int bcm_vk_dma_alloc(struct device *dev,
dma->nr_pages = (err >= 0) ? err : 0;
dev_err(dev, "get_user_pages_fast, err=%d [%d]\n",
err, dma->nr_pages);
- return err < 0 ? err : -EINVAL;
+ err = err < 0 ? err : -EINVAL;
+ goto err_pages;
}
/* Max size of sg list is 1 per mapped page + fields at start */
@@ -90,8 +91,10 @@ static int bcm_vk_dma_alloc(struct device *dev,
dma->sglen,
&dma->handle,
GFP_KERNEL);
- if (!dma->sglist)
- return -ENOMEM;
+ if (!dma->sglist) {
+ err = -ENOMEM;
+ goto err_pages;
+ }
dma->sglist[SGLIST_NUM_SG] = 0;
dma->sglist[SGLIST_TOTALSIZE] = vkdata->size;
@@ -108,8 +111,8 @@ static int bcm_vk_dma_alloc(struct device *dev,
dma->direction);
transfer_size = size;
if (unlikely(dma_mapping_error(dev, sg_addr))) {
- __free_page(dma->pages[0]);
- return -EIO;
+ err = -EIO;
+ goto err_sglist;
}
for (i = 1; i < dma->nr_pages; i++) {
@@ -121,8 +124,8 @@ static int bcm_vk_dma_alloc(struct device *dev,
size,
dma->direction);
if (unlikely(dma_mapping_error(dev, addr))) {
- __free_page(dma->pages[i]);
- return -EIO;
+ err = -EIO;
+ goto err_unmap;
}
/*
@@ -167,6 +170,32 @@ static int bcm_vk_dma_alloc(struct device *dev,
#endif
return 0;
+
+err_unmap:
+ /*
+ * Unmap completed SG entries and the in-progress DMA range,
+ * which has not been written to the SG list yet.
+ */
+ sgdata = (struct _vk_data *)&dma->sglist[SGLIST_VKDATA_START];
+ for (i = 0; i < dma->sglist[SGLIST_NUM_SG]; i++) {
+ size = sgdata[i].size;
+ addr = get_unaligned(&sgdata[i].address);
+ dma_unmap_page(dev, addr, size, dma->direction);
+ }
+ dma_unmap_page(dev, sg_addr, transfer_size, dma->direction);
+
+err_sglist:
+ dma_free_coherent(dev, dma->sglen, dma->sglist, dma->handle);
+
+err_pages:
+ for (i = 0; i < dma->nr_pages; i++)
+ put_page(dma->pages[i]);
+ kfree(dma->pages);
+ dma->pages = NULL;
+ dma->sglist = NULL;
+ dma->sglen = 0;
+ dma->nr_pages = 0;
+ return err;
}
int bcm_vk_sg_alloc(struct device *dev,
--
2.43.0