mana_hwc_destroy_cq() freed hwc_cq->comp_buf and destroyed the CQ before
the EQ. That was unsafe while the EQ was still registered: the EQ
interrupt handler reaches comp_buf via mana_hwc_comp_event() and the CQ
object (hwc->cq->gdma_cq) via mana_hwc_init_event_handler(), so a late
EQE dispatched after the free could touch freed memory.
Destroy the EQ first. mana_gd_destroy_queue() on the EQ deregisters its
IRQ and waits out in-flight handlers, fencing all EQE dispatch; only then
free the CQ and comp_buf.
Fixes: ca9c54d2d6a5 ("net: mana: Add a driver for Microsoft Azure Network
Adapter (MANA)")
Signed-off-by: Long Li <[email protected]>
---
Changes in v4:
- No functional change since v3; the teardown-ordering guarantees this
patch relies on are made explicit in patch 5.
.../net/ethernet/microsoft/mana/hw_channel.c | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c
b/drivers/net/ethernet/microsoft/mana/hw_channel.c
index 3f011ebbe7b3..19896bb5ce1a 100644
--- a/drivers/net/ethernet/microsoft/mana/hw_channel.c
+++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c
@@ -384,14 +384,24 @@ static void mana_hwc_comp_event(void *ctx, struct
gdma_queue *q_self)
static void mana_hwc_destroy_cq(struct gdma_context *gc, struct hwc_cq *hwc_cq)
{
- kfree(hwc_cq->comp_buf);
+ /* Destroy the EQ before the CQ. mana_gd_destroy_queue() on the EQ
+ * deregisters its IRQ and waits out in-flight handlers, fencing all
+ * EQE dispatch — both the completion path and HWC init/reconfig
+ * events. Freeing the CQ first would leave the EQ live and able to
+ * dispatch an event that dereferences hwc->cq->gdma_cq (e.g.
+ * mana_hwc_init_event_handler()) after it has been freed.
+ */
+ if (hwc_cq->gdma_eq)
+ mana_gd_destroy_queue(gc, hwc_cq->gdma_eq);
+ /* comp_buf is reached only by mana_hwc_comp_event(), invoked from
+ * the now-fenced EQ handler, so it is safe to free once the EQ and
+ * CQ are gone.
+ */
if (hwc_cq->gdma_cq)
mana_gd_destroy_queue(gc, hwc_cq->gdma_cq);
- if (hwc_cq->gdma_eq)
- mana_gd_destroy_queue(gc, hwc_cq->gdma_eq);
-
+ kfree(hwc_cq->comp_buf);
kfree(hwc_cq);
}
--
2.43.0