[PATCH rdma-next] RDMA/cxgb4: Fix dereg_skb leak and double free in write_tpt_entry()
From: Leon Romanovsky
Date: Sun Jul 26 2026 - 04:58:36 EST
From: Leon Romanovsky <leonro@xxxxxxxxxx>
When the device is in the fatal error state, write_tpt_entry() returns -EIO
before handing the caller's preallocated skb to the transmit path; its
allocation-failure returns do the same. c4iw_dereg_mr() ignores the error
and frees mhp, leaking mhp->dereg_skb. c4iw_get_dma_mr() instead frees the
skb a second time after dereg_mem() already consumed it, a double free.
Make write_tpt_entry() the sole owner of a non-NULL skb, freeing it on
every return preceding handoff to c4iw_ofld_send(): fatal error, tpt and
stag allocation failure. c4iw_ofld_send() consumes the skb on success and
error alike, so drop the redundant kfree_skb() in c4iw_get_dma_mr() after
dereg_mem().
Fixes: 0f8ab0b6e91b ("RDMA/iw_cxgb4: Low resource fixes for Memory registration")
Signed-off-by: Leon Romanovsky <leonro@xxxxxxxxxx>
---
drivers/infiniband/hw/cxgb4/mem.c | 46 +++++++++++++++------------------------
1 file changed, 17 insertions(+), 29 deletions(-)
diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c
index 49498c75f38f..12d0c7be8df8 100644
--- a/drivers/infiniband/hw/cxgb4/mem.c
+++ b/drivers/infiniband/hw/cxgb4/mem.c
@@ -199,7 +199,8 @@ static int _c4iw_write_mem_dma(struct c4iw_rdev *rdev, u32 addr, u32 len,
daddr = dma_map_single(&rdev->lldi.pdev->dev, data, len, DMA_TO_DEVICE);
if (dma_mapping_error(&rdev->lldi.pdev->dev, daddr))
- return -1;
+ return _c4iw_write_mem_inline(rdev, addr, len, data, skb,
+ wr_waitp);
save = daddr;
while (remain > inline_threshold) {
@@ -235,30 +236,12 @@ static int write_adapter_mem(struct c4iw_rdev *rdev, u32 addr, u32 len,
void *data, struct sk_buff *skb,
struct c4iw_wr_wait *wr_waitp)
{
- int ret;
-
- if (!rdev->lldi.ulptx_memwrite_dsgl || !use_dsgl) {
- ret = _c4iw_write_mem_inline(rdev, addr, len, data, skb,
- wr_waitp);
- goto out;
- }
-
- if (len <= inline_threshold) {
- ret = _c4iw_write_mem_inline(rdev, addr, len, data, skb,
+ if (!rdev->lldi.ulptx_memwrite_dsgl || !use_dsgl ||
+ len <= inline_threshold)
+ return _c4iw_write_mem_inline(rdev, addr, len, data, skb,
wr_waitp);
- goto out;
- }
-
- ret = _c4iw_write_mem_dma(rdev, addr, len, data, skb, wr_waitp);
- if (ret) {
- pr_warn_ratelimited("%s: dma map failure (non fatal)\n",
- pci_name(rdev->lldi.pdev));
- ret = _c4iw_write_mem_inline(rdev, addr, len, data, skb,
- wr_waitp);
- }
-out:
- return ret;
+ return _c4iw_write_mem_dma(rdev, addr, len, data, skb, wr_waitp);
}
/*
@@ -279,12 +262,16 @@ static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry,
u32 stag_idx;
static atomic_t key;
- if (c4iw_fatal_error(rdev))
+ if (c4iw_fatal_error(rdev)) {
+ kfree_skb(skb);
return -EIO;
+ }
tpt = kmalloc_obj(*tpt);
- if (!tpt)
+ if (!tpt) {
+ kfree_skb(skb);
return -ENOMEM;
+ }
stag_state = stag_state > 0;
stag_idx = (*stag) >> 8;
@@ -296,6 +283,7 @@ static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry,
rdev->stats.stag.fail++;
mutex_unlock(&rdev->stats.lock);
kfree(tpt);
+ kfree_skb(skb);
return -ENOMEM;
}
mutex_lock(&rdev->stats.lock);
@@ -469,8 +457,10 @@ struct ib_mr *c4iw_get_dma_mr(struct ib_pd *pd, int acc)
FW_RI_STAG_NSMR, mhp->attr.perms,
mhp->attr.mw_bind_enable, 0, 0, ~0ULL, 0, 0, 0,
NULL, mhp->wr_waitp);
- if (ret)
- goto err_free_skb;
+ if (ret) {
+ kfree_skb(mhp->dereg_skb);
+ goto err_free_wr_wait;
+ }
ret = finish_mem_reg(mhp, stag);
if (ret)
@@ -479,8 +469,6 @@ struct ib_mr *c4iw_get_dma_mr(struct ib_pd *pd, int acc)
err_dereg_mem:
dereg_mem(&rhp->rdev, mhp->attr.stag, mhp->attr.pbl_size,
mhp->attr.pbl_addr, mhp->dereg_skb, mhp->wr_waitp);
-err_free_skb:
- kfree_skb(mhp->dereg_skb);
err_free_wr_wait:
c4iw_put_wr_wait(mhp->wr_waitp);
err_free_mhp:
---
base-commit: aac287f4f1ebebc85f36c0680bcf955ef9145c66
change-id: 20260726-leaked-mhp-dereg-skb-in-c4iw-dereg-m-e76503c672a0
Best regards,
--
Leon Romanovsky <leonro@xxxxxxxxxx>