[PATCH for-rc v2 5/8] RDMA/bnxt_re: Fix rdev lifetime races in suspend/resume/shutdown
From: Selvin Xavier <selvin.xavier@broadcom.com>
Date: 2026-09-09 08:31:32
Subsystem:
broadcom netxtreme-e roce driver, infiniband subsystem, the rest · Maintainers:
Selvin Xavier, Kalesh AP, Jason Gunthorpe, Leon Romanovsky, Linus Torvalds
en_info->rdev is read by unlocked L2 ULP callbacks
(bnxt_re_stop_irq/start_irq/async_notifier), which rely on rdev
being cleared before it is freed, not after. bnxt_re_add_device()
cleared it too late on a bnxt_re_dev_init() failure and
bnxt_re_resume() didn't check the return value, risking a
use-after-free/NULL deref. bnxt_re_suspend() froze and freed rdev
before clearing en_info->rdev, opening the same UAF window.
bnxt_re_shutdown() took no lock, never checked rdev for NULL, never
cleared en_info->rdev, and never freed rdev at all.
Fix the ordering in bnxt_re_add_device()/bnxt_re_suspend(), add the
missing return-value check in bnxt_re_resume(), and rewrite
bnxt_re_shutdown() to take the lock, check for NULL, and reuse
bnxt_re_remove_device() with the same clear-before-free ordering.
Fixes: dee3da3422d5 ("RDMA/bnxt_re: Change aux driver data to en_info to hold more information")
Fixes: cc5b9b48d447 ("RDMA/bnxt_re: Recover the device when FW error is detected")
Signed-off-by: Selvin Xavier <selvin.xavier@broadcom.com>
---
drivers/infiniband/hw/bnxt_re/main.c | 32 ++++++++++++++++++++++------
1 file changed, 25 insertions(+), 7 deletions(-)
diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c
index 17654a9e23fe..91c2edebdb93 100644
--- a/drivers/infiniband/hw/bnxt_re/main.c
+++ b/drivers/infiniband/hw/bnxt_re/main.c@@ -2433,11 +2433,14 @@ static int bnxt_re_add_device(struct auxiliary_device *adev, u8 op_type) bnxt_re_update_en_info_rdev(rdev, en_info, adev); rc = bnxt_re_dev_init(rdev, op_type); - if (rc) + if (rc) { + bnxt_re_update_en_info_rdev(NULL, en_info, adev); goto re_dev_dealloc; + } rc = bnxt_re_ib_init(rdev); if (rc) { + bnxt_re_update_en_info_rdev(NULL, en_info, adev); pr_err("Failed to register with IB: %s", aux_priv->aux_dev.name); goto re_dev_uninit;
@@ -2448,7 +2451,6 @@ static int bnxt_re_add_device(struct auxiliary_device *adev, u8 op_type) return 0; re_dev_uninit: - bnxt_re_update_en_info_rdev(NULL, en_info, adev); bnxt_re_dev_uninit(rdev, BNXT_RE_COMPLETE_REMOVE); re_dev_dealloc: ib_dealloc_device(&rdev->ibdev);
@@ -2517,9 +2519,13 @@ static int bnxt_re_suspend(struct auxiliary_device *adev, pm_message_t state) struct bnxt_en_dev *en_dev; struct bnxt_re_dev *rdev; + mutex_lock(&bnxt_re_mutex); rdev = en_info->rdev; + if (!rdev) { + mutex_unlock(&bnxt_re_mutex); + return 0; + } en_dev = en_info->en_dev; - mutex_lock(&bnxt_re_mutex); ibdev_info(&rdev->ibdev, "Handle device suspend call"); /* Check the current device state from bnxt_en_dev and move the
@@ -2539,8 +2545,9 @@ static int bnxt_re_suspend(struct auxiliary_device *adev, pm_message_t state) ibdev_info(&rdev->ibdev, "%s: L2 driver notified to stop en_state 0x%lx", __func__, en_dev->en_state); - bnxt_re_remove_device(rdev, BNXT_RE_PRE_RECOVERY_REMOVE, adev); + bnxt_re_update_en_info_rdev(NULL, en_info, adev); + bnxt_re_remove_device(rdev, BNXT_RE_PRE_RECOVERY_REMOVE, adev); mutex_unlock(&bnxt_re_mutex); return 0;
@@ -2550,9 +2557,14 @@ static int bnxt_re_resume(struct auxiliary_device *adev) { struct bnxt_re_en_dev_info *en_info = auxiliary_get_drvdata(adev); struct bnxt_re_dev *rdev; + int rc; mutex_lock(&bnxt_re_mutex); - bnxt_re_add_device(adev, BNXT_RE_POST_RECOVERY_INIT); + rc = bnxt_re_add_device(adev, BNXT_RE_POST_RECOVERY_INIT); + if (rc) { + mutex_unlock(&bnxt_re_mutex); + return rc; + } rdev = en_info->rdev; ibdev_info(&rdev->ibdev, "Device resume completed"); mutex_unlock(&bnxt_re_mutex);
@@ -2565,9 +2577,15 @@ static void bnxt_re_shutdown(struct auxiliary_device *adev) struct bnxt_re_en_dev_info *en_info = auxiliary_get_drvdata(adev); struct bnxt_re_dev *rdev; + mutex_lock(&bnxt_re_mutex); rdev = en_info->rdev; - ib_unregister_device(&rdev->ibdev); - bnxt_re_dev_uninit(rdev, BNXT_RE_COMPLETE_REMOVE); + if (!rdev) + goto out; + + bnxt_re_update_en_info_rdev(NULL, en_info, adev); + bnxt_re_remove_device(rdev, BNXT_RE_COMPLETE_REMOVE, adev); +out: + mutex_unlock(&bnxt_re_mutex); } static const struct auxiliary_device_id bnxt_re_id_table[] = {
--
2.39.3