[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);
 		}
 	}