[PATCH 5/9] RDMA/hfi2: Fix use-after-free in PIO and SDMA map teardown
Dennis Dalessandro <[email protected]> Mon, 03 Aug 2026 12:11:17 -0400
| Newsgroups | org.kernel.vger.linux-rdma |
|---|---|
| Message-ID | <178577347732.1793053.13152251769397445815.stgit@awdrv-04> |
hfi2_free_pio_map() and hfi2_sdma_clean() free the RCU-protected
pio_map/sdma_map pointer while still holding the associated spinlock
and before calling synchronize_rcu(). This means the memory can be
freed while a concurrent RCU reader is still dereferencing it, since
the grace period has not yet elapsed.
Fix this by saving the pointer to a local variable, clearing the
RCU pointer, dropping the lock, waiting for the RCU grace period via
synchronize_rcu(), and only then freeing the saved pointer. Unlike
hfi1, hfi2 maintains these maps per port, so the fix is applied
inside the existing per-port loop in both functions.
Matches hfi1 fix 76b48a70b16b ("IB/hfi1: Fix potential use-after-free
in PIO and SDMA map teardown").
Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: Dennis Dalessandro <[email protected]>
---
drivers/infiniband/hw/hfi2/pio.c | 4 +++-
drivers/infiniband/hw/hfi2/sdma.c | 4 +++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/infiniband/hw/hfi2/pio.c b/drivers/infiniband/hw/hfi2/pio.c
index 90fe6bb14b09..4d457dd69c59 100644
--- a/drivers/infiniband/hw/hfi2/pio.c
+++ b/drivers/infiniband/hw/hfi2/pio.c
@@ -2052,6 +2052,7 @@ int hfi2_pio_map_init(struct hfi2_pportdata *ppd, u8 hfi2_num_vls)
void hfi2_free_pio_map(struct hfi2_devdata *dd)
{
struct hfi2_pportdata *ppd;
+ struct pio_vl_map *map;
int i;
for (i = 0; i < dd->num_pports; i++) {
@@ -2059,10 +2060,11 @@ void hfi2_free_pio_map(struct hfi2_devdata *dd)
/* Free PIO map if allocated */
if (rcu_access_pointer(ppd->pio_map)) {
spin_lock_irq(&dd->pio_map_lock);
- pio_map_free(rcu_access_pointer(ppd->pio_map));
+ map = rcu_access_pointer(ppd->pio_map);
RCU_INIT_POINTER(ppd->pio_map, NULL);
spin_unlock_irq(&dd->pio_map_lock);
synchronize_rcu();
+ pio_map_free(map);
}
kfree(ppd->kernel_send_context);
ppd->kernel_send_context = NULL;
diff --git a/drivers/infiniband/hw/hfi2/sdma.c b/drivers/infiniband/hw/hfi2/sdma.c
index a48c548fadc2..8dbeb8ac2f72 100644
--- a/drivers/infiniband/hw/hfi2/sdma.c
+++ b/drivers/infiniband/hw/hfi2/sdma.c
@@ -1376,13 +1376,15 @@ void hfi2_sdma_clean(struct hfi2_devdata *dd)
for (pidx = 0; pidx < dd->num_pports; pidx++) {
struct hfi2_pportdata *ppd = dd->pport + pidx;
+ struct sdma_vl_map *map;
if (rcu_access_pointer(ppd->sdma_map)) {
spin_lock_irq(&dd->sde_map_lock);
- sdma_map_free(rcu_access_pointer(ppd->sdma_map));
+ map = rcu_access_pointer(ppd->sdma_map);
RCU_INIT_POINTER(ppd->sdma_map, NULL);
spin_unlock_irq(&dd->sde_map_lock);
synchronize_rcu();
+ sdma_map_free(map);
}
}