* [PATCH 0/4] RDMA/rxe: Fix MW/MR lifetime races
@ 2026-09-28 15:53 Dongliang Qin
2026-09-28 15:53 ` [PATCH 1/4] RDMA/rxe: Take MR reference under MW lock Dongliang Qin
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Dongliang Qin @ 2026-09-28 15:53 UTC (permalink / raw)
To: Zhu Yanjun, Jason Gunthorpe, Leon Romanovsky
Cc: Dongliang Qin, linux-rdma, linux-kernel, Bob Pearson, stable
Soft-RoCE keeps MW-to-MR bindings and type-2 MW-to-QP references across
verbs operations and responder packets. Several paths currently assume
those references remain stable without taking the MW lock or reserving
the MR state. As a result, the responder can acquire a zero reference,
a bind can race with MR invalidation or deregistration, a type-2 MW can
outlive its QP, or the pool can force-free an object with outstanding
references. An unprivileged user with access to an RXe device can use these
races to corrupt kernel memory and escalate privileges.
This series fixes those races with four focused, individually
revertible changes:
1. Move MW lookup, validation, and MR reference acquisition under
mw->lock.
2. Use num_mw as an atomic state reservation while an MR changes state.
3. Invalidate type-2 MWs bound to a QP before destroying that QP.
4. Stop force-freeing sleepable pool objects after a timeout.
Patch 4 is hardening: it prevents pool cleanup from turning an outstanding
reference into a use-after-free, rather than fixing the reported bind and
deregistration race directly.
Before the fix, a concurrent MW bind and MR deregistration reproducer made
KASAN report a slab use-after-free in rxe_mr_copy() from rxe_receiver() on
the RXe responder workqueue. With this series, the same 120-second test no
longer triggers KASAN. MW READ, WRITE, partial READ, invalidate, and rebind
still pass.
Dongliang Qin (4):
RDMA/rxe: Take MR reference under MW lock
RDMA/rxe: Reserve MR state during MW binding
RDMA/rxe: Invalidate MWs on QP destroy
RDMA/rxe: Do not force cleanup on pool timeout
drivers/infiniband/sw/rxe/rxe_loc.h | 8 ++-
drivers/infiniband/sw/rxe/rxe_mr.c | 70 +++++++++++++++++--
drivers/infiniband/sw/rxe/rxe_mw.c | 99 +++++++++++++++++++--------
drivers/infiniband/sw/rxe/rxe_pool.c | 14 +---
drivers/infiniband/sw/rxe/rxe_resp.c | 38 +---------
drivers/infiniband/sw/rxe/rxe_verbs.c | 15 +++-
6 files changed, 159 insertions(+), 85 deletions(-)
base-commit: 93f51579e7df2
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/4] RDMA/rxe: Take MR reference under MW lock
2026-09-28 15:53 [PATCH 0/4] RDMA/rxe: Fix MW/MR lifetime races Dongliang Qin
@ 2026-09-28 15:53 ` Dongliang Qin
2026-09-28 15:53 ` [PATCH 2/4] RDMA/rxe: Reserve MR state during MW binding Dongliang Qin
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Dongliang Qin @ 2026-09-28 15:53 UTC (permalink / raw)
To: Zhu Yanjun, Jason Gunthorpe, Leon Romanovsky
Cc: Dongliang Qin, linux-rdma, linux-kernel, Bob Pearson, stable
The responder validates an MW, drops it, then reads mw->mr and takes
the MR reference. A bind or invalidate can swap the MR in this window,
so the responder may acquire a zero reference or use an MR that has
already been freed.
Move the MW lookup, validation, and MR reference acquisition into
rxe_mw_get_mr(), and perform all of them while holding mw->lock.
Fixes: cdd0b85675ae ("RDMA/rxe: Implement memory access through MWs")
Cc: stable@vger.kernel.org
Signed-off-by: Dongliang Qin <cccccccccccc777777@gmail.com>
---
drivers/infiniband/sw/rxe/rxe_loc.h | 3 ++-
drivers/infiniband/sw/rxe/rxe_mw.c | 34 +++++++++++++++++--------
drivers/infiniband/sw/rxe/rxe_resp.c | 38 +++-------------------------
3 files changed, 28 insertions(+), 47 deletions(-)
diff --git a/drivers/infiniband/sw/rxe/rxe_loc.h b/drivers/infiniband/sw/rxe/rxe_loc.h
index 64d636bf80fd2..2cbec92566f70 100644
--- a/drivers/infiniband/sw/rxe/rxe_loc.h
+++ b/drivers/infiniband/sw/rxe/rxe_loc.h
@@ -86,7 +86,8 @@ int rxe_alloc_mw(struct ib_mw *ibmw, struct ib_udata *udata);
int rxe_dealloc_mw(struct ib_mw *ibmw);
int rxe_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe);
int rxe_invalidate_mw(struct rxe_qp *qp, u32 rkey);
-struct rxe_mw *rxe_lookup_mw(struct rxe_qp *qp, int access, u32 rkey);
+struct rxe_mr *rxe_mw_get_mr(struct rxe_qp *qp, int access, u32 rkey,
+ u64 *offset);
void rxe_mw_cleanup(struct rxe_pool_elem *elem);
/* rxe_net.c */
diff --git a/drivers/infiniband/sw/rxe/rxe_mw.c b/drivers/infiniband/sw/rxe/rxe_mw.c
index bddb7a2578313..04f795adacf53 100644
--- a/drivers/infiniband/sw/rxe/rxe_mw.c
+++ b/drivers/infiniband/sw/rxe/rxe_mw.c
@@ -291,26 +291,38 @@ int rxe_invalidate_mw(struct rxe_qp *qp, u32 rkey)
return ret;
}
-struct rxe_mw *rxe_lookup_mw(struct rxe_qp *qp, int access, u32 rkey)
+struct rxe_mr *rxe_mw_get_mr(struct rxe_qp *qp, int access, u32 rkey,
+ u64 *offset)
{
struct rxe_dev *rxe = to_rdev(qp->ibqp.device);
- struct rxe_pd *pd = to_rpd(qp->ibqp.pd);
+ struct rxe_mr *mr = NULL;
struct rxe_mw *mw;
- int index = rkey >> 8;
- mw = rxe_pool_get_index(&rxe->mw_pool, index);
+ mw = rxe_pool_get_index(&rxe->mw_pool, rkey >> 8);
if (!mw)
return NULL;
- if (unlikely((mw->rkey != rkey) || rxe_mw_pd(mw) != pd ||
- (mw->ibmw.type == IB_MW_TYPE_2 && mw->qp != qp) ||
- (mw->length == 0) || ((access & mw->access) != access) ||
- mw->state != RXE_MW_STATE_VALID)) {
- rxe_put(mw);
- return NULL;
+ spin_lock_bh(&mw->lock);
+
+ if (mw->rkey == rkey && rxe_mw_pd(mw) == to_rpd(qp->ibqp.pd) &&
+ (mw->ibmw.type != IB_MW_TYPE_2 || mw->qp == qp) &&
+ mw->length != 0 && mw->state == RXE_MW_STATE_VALID &&
+ (access & mw->access) == access) {
+ mr = mw->mr;
+ if (mr && mr->state == RXE_MR_STATE_VALID && rxe_get(mr)) {
+ if (offset)
+ *offset = (mw->access & IB_ZERO_BASED) ?
+ mw->addr : 0;
+ } else {
+ mr = NULL;
+ }
}
- return mw;
+ spin_unlock_bh(&mw->lock);
+
+ rxe_put(mw);
+
+ return mr;
}
void rxe_mw_cleanup(struct rxe_pool_elem *elem)
diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c
index 02b16e2b49b8f..f5a957026fe87 100644
--- a/drivers/infiniband/sw/rxe/rxe_resp.c
+++ b/drivers/infiniband/sw/rxe/rxe_resp.c
@@ -468,7 +468,6 @@ static enum resp_states check_rkey(struct rxe_qp *qp,
struct rxe_pkt_info *pkt)
{
struct rxe_mr *mr = NULL;
- struct rxe_mw *mw = NULL;
u64 va;
u32 rkey;
u32 resid;
@@ -520,26 +519,12 @@ static enum resp_states check_rkey(struct rxe_qp *qp,
pktlen = payload_size(pkt);
if (rkey_is_mw(rkey)) {
- mw = rxe_lookup_mw(qp, access, rkey);
- if (!mw) {
- rxe_dbg_qp(qp, "no MW matches rkey %#x\n", rkey);
- state = get_rkey_violation_state(pkt);
- goto err;
- }
-
- mr = mw->mr;
+ mr = rxe_mw_get_mr(qp, access, rkey, &qp->resp.offset);
if (!mr) {
- rxe_dbg_qp(qp, "MW doesn't have an MR\n");
+ rxe_dbg_qp(qp, "no MW/MR matches rkey %#x\n", rkey);
state = get_rkey_violation_state(pkt);
goto err;
}
-
- if (mw->access & IB_ZERO_BASED)
- qp->resp.offset = mw->addr;
-
- rxe_get(mr);
- rxe_put(mw);
- mw = NULL;
} else {
mr = lookup_mr(qp->pd, access, rkey, RXE_LOOKUP_REMOTE);
if (!mr) {
@@ -605,8 +590,6 @@ static enum resp_states check_rkey(struct rxe_qp *qp,
qp->resp.mr = NULL;
if (mr)
rxe_put(mr);
- if (mw)
- rxe_put(mw);
return state;
}
@@ -894,24 +877,9 @@ static struct rxe_mr *rxe_recheck_mr(struct rxe_qp *qp, u32 rkey)
{
struct rxe_dev *rxe = to_rdev(qp->ibqp.device);
struct rxe_mr *mr;
- struct rxe_mw *mw;
if (rkey_is_mw(rkey)) {
- mw = rxe_pool_get_index(&rxe->mw_pool, rkey >> 8);
- if (!mw)
- return NULL;
-
- mr = mw->mr;
- if (mw->rkey != rkey || mw->state != RXE_MW_STATE_VALID ||
- !mr || mr->state != RXE_MR_STATE_VALID) {
- rxe_put(mw);
- return NULL;
- }
-
- rxe_get(mr);
- rxe_put(mw);
-
- return mr;
+ return rxe_mw_get_mr(qp, IB_ACCESS_REMOTE_READ, rkey, NULL);
}
mr = rxe_pool_get_index(&rxe->mr_pool, rkey >> 8);
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 2/4] RDMA/rxe: Reserve MR state during MW binding
2026-09-28 15:53 [PATCH 0/4] RDMA/rxe: Fix MW/MR lifetime races Dongliang Qin
2026-09-28 15:53 ` [PATCH 1/4] RDMA/rxe: Take MR reference under MW lock Dongliang Qin
@ 2026-09-28 15:53 ` Dongliang Qin
2026-09-28 15:53 ` [PATCH 3/4] RDMA/rxe: Invalidate MWs on QP destroy Dongliang Qin
2026-09-28 15:53 ` [PATCH 4/4] RDMA/rxe: Do not force cleanup on pool timeout Dongliang Qin
3 siblings, 0 replies; 5+ messages in thread
From: Dongliang Qin @ 2026-09-28 15:53 UTC (permalink / raw)
To: Zhu Yanjun, Jason Gunthorpe, Leon Romanovsky
Cc: Dongliang Qin, linux-rdma, linux-kernel, Bob Pearson, stable
MR invalidation, fast registration, reregistration, and deregistration
only test num_mw before changing MR state. A concurrent type-2 MW bind
can increment num_mw after that test and leave an MW bound to an MR
that is being destroyed.
Reserve num_mw == -1 while the MR state is changing, and allow binds
only while num_mw is nonnegative. This keeps the MW binding count and
MR lifetime synchronized without adding a new object.
An unprivileged user with access to the device can use this race to corrupt
kernel memory and escalate privileges.
A concurrent bind and deregistration reproducer made KASAN report:
BUG: KASAN: slab-use-after-free in rxe_mr_copy+0xd5/0x3d0
Read of size 1 at addr ff11000105d217b0 by task kworker/u16:3/44
Workqueue: rxe_wq do_work
Call Trace:
rxe_mr_copy+0xd5/0x3d0
rxe_receiver+0x1b50/0x3ed0
do_work+0xbb/0x250
process_one_work+0x412/0x780
worker_thread+0x341/0x5b0
kthread+0x1b8/0x210
Freed by task 1151:
kasan_save_stack+0x33/0x60
kfree+0x17c/0x450
rxe_mr_cleanup+0x3c/0x90
__rxe_cleanup+0x145/0x1e0
rxe_dereg_mr+0x4e/0x110
ib_dereg_mr_user+0xa4/0x1a0
ib_uverbs_ioctl+0x12f/0x1c0
Fixes: 32a577b4c3a9 ("RDMA/rxe: Add support for bind MW work requests")
Cc: stable@vger.kernel.org
Signed-off-by: Dongliang Qin <cccccccccccc777777@gmail.com>
---
drivers/infiniband/sw/rxe/rxe_loc.h | 4 ++
drivers/infiniband/sw/rxe/rxe_mr.c | 70 +++++++++++++++++++++++++--
drivers/infiniband/sw/rxe/rxe_mw.c | 44 +++++++++--------
drivers/infiniband/sw/rxe/rxe_verbs.c | 13 ++++-
4 files changed, 105 insertions(+), 26 deletions(-)
diff --git a/drivers/infiniband/sw/rxe/rxe_loc.h b/drivers/infiniband/sw/rxe/rxe_loc.h
index 2cbec92566f70..5e95e5c5e32d6 100644
--- a/drivers/infiniband/sw/rxe/rxe_loc.h
+++ b/drivers/infiniband/sw/rxe/rxe_loc.h
@@ -72,6 +72,10 @@ enum resp_states rxe_mr_do_atomic_op(struct rxe_mr *mr, u64 iova, int opcode,
enum resp_states rxe_mr_do_atomic_write(struct rxe_mr *mr, u64 iova, u64 value);
struct rxe_mr *lookup_mr(struct rxe_pd *pd, int access, u32 key,
enum rxe_mr_lookup_type type);
+int rxe_mr_get_mw(struct rxe_mr *mr);
+void rxe_mr_put_mw(struct rxe_mr *mr);
+bool rxe_mr_reserve_mw_state(struct rxe_mr *mr);
+void rxe_mr_release_mw_state(struct rxe_mr *mr);
int mr_check_range(struct rxe_mr *mr, u64 iova, size_t length);
int advance_dma_data(struct rxe_dma_info *dma, unsigned int length);
int rxe_invalidate_mr(struct rxe_qp *qp, u32 key);
diff --git a/drivers/infiniband/sw/rxe/rxe_mr.c b/drivers/infiniband/sw/rxe/rxe_mr.c
index 71d9ea4772890..4dc5b7405832f 100644
--- a/drivers/infiniband/sw/rxe/rxe_mr.c
+++ b/drivers/infiniband/sw/rxe/rxe_mr.c
@@ -721,6 +721,53 @@ struct rxe_mr *lookup_mr(struct rxe_pd *pd, int access, u32 key,
return mr;
}
+/*
+ * num_mw counts the MWs bound to an MR. The value -1 is reserved while
+ * the MR is changing state so that a new MW binding cannot race with
+ * invalidation, fast registration, or deregistration.
+ */
+int rxe_mr_get_mw(struct rxe_mr *mr)
+{
+ int old;
+
+ if (!rxe_get(mr))
+ return 0;
+
+ old = atomic_read(&mr->num_mw);
+ do {
+ if (old < 0)
+ goto err_put;
+ } while (!atomic_try_cmpxchg(&mr->num_mw, &old, old + 1));
+
+ if (mr->state != RXE_MR_STATE_VALID) {
+ atomic_dec_return(&mr->num_mw);
+ rxe_put(mr);
+ return 0;
+ }
+
+ return 1;
+
+err_put:
+ rxe_put(mr);
+ return 0;
+}
+
+void rxe_mr_put_mw(struct rxe_mr *mr)
+{
+ atomic_dec_return(&mr->num_mw);
+ rxe_put(mr);
+}
+
+bool rxe_mr_reserve_mw_state(struct rxe_mr *mr)
+{
+ return atomic_cmpxchg(&mr->num_mw, 0, -1) == 0;
+}
+
+void rxe_mr_release_mw_state(struct rxe_mr *mr)
+{
+ atomic_xchg(&mr->num_mw, 0);
+}
+
int rxe_invalidate_mr(struct rxe_qp *qp, u32 key)
{
struct rxe_dev *rxe = to_rdev(qp->ibqp.device);
@@ -743,7 +790,7 @@ int rxe_invalidate_mr(struct rxe_qp *qp, u32 key)
goto err_drop_ref;
}
- if (atomic_read(&mr->num_mw) > 0) {
+ if (!rxe_mr_reserve_mw_state(mr)) {
rxe_dbg_mr(mr, "Attempt to invalidate an MR while bound to MWs\n");
ret = -EINVAL;
goto err_drop_ref;
@@ -752,11 +799,16 @@ int rxe_invalidate_mr(struct rxe_qp *qp, u32 key)
if (unlikely(mr->ibmr.type != IB_MR_TYPE_MEM_REG)) {
rxe_dbg_mr(mr, "Type (%d) is wrong\n", mr->ibmr.type);
ret = -EINVAL;
- goto err_drop_ref;
+ goto err_release;
}
mr->state = RXE_MR_STATE_FREE;
+ rxe_mr_release_mw_state(mr);
ret = 0;
+ goto err_drop_ref;
+
+err_release:
+ rxe_mr_release_mw_state(mr);
err_drop_ref:
rxe_put(mr);
@@ -777,23 +829,26 @@ int rxe_reg_fast_mr(struct rxe_qp *qp, struct rxe_send_wqe *wqe)
u32 key = wqe->wr.wr.reg.key;
u32 access = wqe->wr.wr.reg.access;
+ if (!rxe_mr_reserve_mw_state(mr))
+ return -EINVAL;
+
/* user can only register MR in free state */
if (unlikely(mr->state != RXE_MR_STATE_FREE)) {
rxe_dbg_mr(mr, "mr->lkey = 0x%x not free\n", mr->lkey);
- return -EINVAL;
+ goto err_release;
}
/* user can only register mr with qp in same protection domain */
if (unlikely(qp->ibqp.pd != mr->ibmr.pd)) {
rxe_dbg_mr(mr, "qp->pd and mr->pd don't match\n");
- return -EINVAL;
+ goto err_release;
}
/* user is only allowed to change key portion of l/rkey */
if (unlikely((mr->lkey & ~0xff) != (key & ~0xff))) {
rxe_dbg_mr(mr, "key = 0x%x has wrong index mr->lkey = 0x%x\n",
key, mr->lkey);
- return -EINVAL;
+ goto err_release;
}
mr->access = access;
@@ -801,8 +856,13 @@ int rxe_reg_fast_mr(struct rxe_qp *qp, struct rxe_send_wqe *wqe)
mr->rkey = key;
mr->ibmr.iova = wqe->wr.wr.reg.mr->iova;
mr->state = RXE_MR_STATE_VALID;
+ rxe_mr_release_mw_state(mr);
return 0;
+
+err_release:
+ rxe_mr_release_mw_state(mr);
+ return -EINVAL;
}
void rxe_mr_cleanup(struct rxe_pool_elem *elem)
diff --git a/drivers/infiniband/sw/rxe/rxe_mw.c b/drivers/infiniband/sw/rxe/rxe_mw.c
index 04f795adacf53..82e9fef89b6c3 100644
--- a/drivers/infiniband/sw/rxe/rxe_mw.c
+++ b/drivers/infiniband/sw/rxe/rxe_mw.c
@@ -136,33 +136,39 @@ static int rxe_check_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe,
return 0;
}
-static void rxe_do_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe,
- struct rxe_mw *mw, struct rxe_mr *mr, int access)
+static int rxe_do_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe,
+ struct rxe_mw *mw, struct rxe_mr *mr, int access)
{
u32 key = wqe->wr.wr.mw.rkey & 0xff;
- mw->rkey = (mw->rkey & ~0xff) | key;
- mw->access = access;
- mw->state = RXE_MW_STATE_VALID;
- mw->addr = wqe->wr.wr.mw.addr;
- mw->length = wqe->wr.wr.mw.length;
+ if (mw->ibmw.type == IB_MW_TYPE_2 && !rxe_get(qp))
+ return -EINVAL;
+
+ if (mr && !rxe_mr_get_mw(mr)) {
+ if (mw->ibmw.type == IB_MW_TYPE_2)
+ rxe_put(qp);
+ return -EINVAL;
+ }
if (mw->mr) {
- rxe_put(mw->mr);
- atomic_dec(&mw->mr->num_mw);
+ rxe_mr_put_mw(mw->mr);
mw->mr = NULL;
}
- if (mw->length) {
+ if (mr)
mw->mr = mr;
- atomic_inc(&mr->num_mw);
- rxe_get(mr);
- }
+
+ mw->rkey = (mw->rkey & ~0xff) | key;
+ mw->access = access;
+ mw->state = RXE_MW_STATE_VALID;
+ mw->addr = wqe->wr.wr.mw.addr;
+ mw->length = wqe->wr.wr.mw.length;
if (mw->ibmw.type == IB_MW_TYPE_2) {
- rxe_get(qp);
mw->qp = qp;
}
+
+ return 0;
}
int rxe_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe)
@@ -213,7 +219,9 @@ int rxe_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe)
if (ret)
goto err_unlock;
- rxe_do_bind_mw(qp, wqe, mw, mr, access);
+ ret = rxe_do_bind_mw(qp, wqe, mw, mr, access);
+ if (ret)
+ goto err_unlock;
err_unlock:
spin_unlock_bh(&mw->lock);
err_drop_mr:
@@ -250,8 +258,7 @@ static void rxe_do_invalidate_mw(struct rxe_mw *mw)
/* valid type 2 MW will always have an MR pointer */
mr = mw->mr;
mw->mr = NULL;
- atomic_dec(&mr->num_mw);
- rxe_put(mr);
+ rxe_mr_put_mw(mr);
mw->access = 0;
mw->addr = 0;
@@ -336,8 +343,7 @@ void rxe_mw_cleanup(struct rxe_pool_elem *elem)
struct rxe_mr *mr = mw->mr;
mw->mr = NULL;
- atomic_dec(&mr->num_mw);
- rxe_put(mr);
+ rxe_mr_put_mw(mr);
}
if (mw->qp) {
diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c
index 3864284522ebc..8553c8402c619 100644
--- a/drivers/infiniband/sw/rxe/rxe_verbs.c
+++ b/drivers/infiniband/sw/rxe/rxe_verbs.c
@@ -1337,6 +1337,11 @@ static struct ib_mr *rxe_rereg_user_mr(struct ib_mr *ibmr, int flags,
return ERR_PTR(-EOPNOTSUPP);
}
+ if (!rxe_mr_reserve_mw_state(mr)) {
+ rxe_err_mr(mr, "mr has mws bound\n");
+ return ERR_PTR(-EINVAL);
+ }
+
if (flags & IB_MR_REREG_PD) {
rxe_put(old_pd);
rxe_get(pd);
@@ -1346,6 +1351,8 @@ static struct ib_mr *rxe_rereg_user_mr(struct ib_mr *ibmr, int flags,
if (flags & IB_MR_REREG_ACCESS)
mr->access = access;
+ rxe_mr_release_mw_state(mr);
+
return NULL;
}
@@ -1401,13 +1408,15 @@ static int rxe_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata)
struct rxe_mr *mr = to_rmr(ibmr);
int err, cleanup_err;
- /* See IBA 10.6.7.2.6 */
- if (atomic_read(&mr->num_mw) > 0) {
+ /* See IBA 10.6.7.2.6. Leave num_mw set to -1 for destruction. */
+ if (!rxe_mr_reserve_mw_state(mr)) {
err = -EINVAL;
rxe_dbg_mr(mr, "mr has mw's bound\n");
goto err_out;
}
+ mr->state = RXE_MR_STATE_INVALID;
+
cleanup_err = rxe_cleanup(mr);
if (cleanup_err)
rxe_err_mr(mr, "cleanup failed, err = %d\n", cleanup_err);
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 3/4] RDMA/rxe: Invalidate MWs on QP destroy
2026-09-28 15:53 [PATCH 0/4] RDMA/rxe: Fix MW/MR lifetime races Dongliang Qin
2026-09-28 15:53 ` [PATCH 1/4] RDMA/rxe: Take MR reference under MW lock Dongliang Qin
2026-09-28 15:53 ` [PATCH 2/4] RDMA/rxe: Reserve MR state during MW binding Dongliang Qin
@ 2026-09-28 15:53 ` Dongliang Qin
2026-09-28 15:53 ` [PATCH 4/4] RDMA/rxe: Do not force cleanup on pool timeout Dongliang Qin
3 siblings, 0 replies; 5+ messages in thread
From: Dongliang Qin @ 2026-09-28 15:53 UTC (permalink / raw)
To: Zhu Yanjun, Jason Gunthorpe, Leon Romanovsky
Cc: Dongliang Qin, linux-rdma, linux-kernel, Bob Pearson, stable
A type-2 MW holds a reference to the QP that bound it. If the QP is
destroyed while an MW is still bound, the MW keeps the QP alive and the
responder can retain a stale QP association.
Invalidate MWs bound to the QP before removing the QP from its pool.
Fixes: 32a577b4c3a9 ("RDMA/rxe: Add support for bind MW work requests")
Cc: stable@vger.kernel.org
Signed-off-by: Dongliang Qin <cccccccccccc777777@gmail.com>
---
drivers/infiniband/sw/rxe/rxe_loc.h | 1 +
drivers/infiniband/sw/rxe/rxe_mw.c | 21 +++++++++++++++++++++
drivers/infiniband/sw/rxe/rxe_verbs.c | 2 ++
3 files changed, 24 insertions(+)
diff --git a/drivers/infiniband/sw/rxe/rxe_loc.h b/drivers/infiniband/sw/rxe/rxe_loc.h
index 5e95e5c5e32d6..5c648a5ef34b1 100644
--- a/drivers/infiniband/sw/rxe/rxe_loc.h
+++ b/drivers/infiniband/sw/rxe/rxe_loc.h
@@ -90,6 +90,7 @@ int rxe_alloc_mw(struct ib_mw *ibmw, struct ib_udata *udata);
int rxe_dealloc_mw(struct ib_mw *ibmw);
int rxe_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe);
int rxe_invalidate_mw(struct rxe_qp *qp, u32 rkey);
+void rxe_invalidate_mws(struct rxe_qp *qp);
struct rxe_mr *rxe_mw_get_mr(struct rxe_qp *qp, int access, u32 rkey,
u64 *offset);
void rxe_mw_cleanup(struct rxe_pool_elem *elem);
diff --git a/drivers/infiniband/sw/rxe/rxe_mw.c b/drivers/infiniband/sw/rxe/rxe_mw.c
index 82e9fef89b6c3..02d20ede1a789 100644
--- a/drivers/infiniband/sw/rxe/rxe_mw.c
+++ b/drivers/infiniband/sw/rxe/rxe_mw.c
@@ -298,6 +298,27 @@ int rxe_invalidate_mw(struct rxe_qp *qp, u32 rkey)
return ret;
}
+void rxe_invalidate_mws(struct rxe_qp *qp)
+{
+ struct rxe_dev *rxe = to_rdev(qp->ibqp.device);
+ struct rxe_mw *mw;
+ unsigned long index;
+
+ rcu_read_lock();
+ xa_for_each(&rxe->mw_pool.xa, index, mw) {
+ if (!rxe_get(mw))
+ continue;
+
+ spin_lock_bh(&mw->lock);
+ if (mw->qp == qp)
+ rxe_do_invalidate_mw(mw);
+ spin_unlock_bh(&mw->lock);
+
+ rxe_put(mw);
+ }
+ rcu_read_unlock();
+}
+
struct rxe_mr *rxe_mw_get_mr(struct rxe_qp *qp, int access, u32 rkey,
u64 *offset)
{
diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c
index 8553c8402c619..c0d77cad4906b 100644
--- a/drivers/infiniband/sw/rxe/rxe_verbs.c
+++ b/drivers/infiniband/sw/rxe/rxe_verbs.c
@@ -658,6 +658,8 @@ static int rxe_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata)
goto err_out;
}
+ rxe_invalidate_mws(qp);
+
err = rxe_cleanup(qp);
if (err)
rxe_err_qp(qp, "cleanup failed, err = %d\n", err);
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 4/4] RDMA/rxe: Do not force cleanup on pool timeout
2026-09-28 15:53 [PATCH 0/4] RDMA/rxe: Fix MW/MR lifetime races Dongliang Qin
` (2 preceding siblings ...)
2026-09-28 15:53 ` [PATCH 3/4] RDMA/rxe: Invalidate MWs on QP destroy Dongliang Qin
@ 2026-09-28 15:53 ` Dongliang Qin
3 siblings, 0 replies; 5+ messages in thread
From: Dongliang Qin @ 2026-09-28 15:53 UTC (permalink / raw)
To: Zhu Yanjun, Jason Gunthorpe, Leon Romanovsky
Cc: Dongliang Qin, linux-rdma, linux-kernel, Bob Pearson, stable
The sleepable pool cleanup path waits up to 50 seconds for the last
reference and then continues cleanup anyway. If a reference is still
held, that turns a lifetime bug into a use-after-free.
Wait for completion instead. A stuck object is easier to diagnose than
a stale pointer; the existing non-sleepable path is unchanged.
Fixes: 215d0a755e1b ("RDMA/rxe: Stop lookup of partially built objects")
Cc: stable@vger.kernel.org
Signed-off-by: Dongliang Qin <cccccccccccc777777@gmail.com>
---
drivers/infiniband/sw/rxe/rxe_pool.c | 14 ++------------
1 file changed, 2 insertions(+), 12 deletions(-)
diff --git a/drivers/infiniband/sw/rxe/rxe_pool.c b/drivers/infiniband/sw/rxe/rxe_pool.c
index d9cb682fd71f8..d5ff5d453f5f8 100644
--- a/drivers/infiniband/sw/rxe/rxe_pool.c
+++ b/drivers/infiniband/sw/rxe/rxe_pool.c
@@ -178,7 +178,7 @@ int __rxe_cleanup(struct rxe_pool_elem *elem, bool sleepable)
{
struct rxe_pool *pool = elem->pool;
struct xarray *xa = &pool->xa;
- int ret, err = 0;
+ int err = 0;
void *xa_ret;
if (sleepable)
@@ -201,17 +201,7 @@ int __rxe_cleanup(struct rxe_pool_elem *elem, bool sleepable)
* return to rdma-core
*/
if (sleepable) {
- if (!completion_done(&elem->complete)) {
- ret = wait_for_completion_timeout(&elem->complete,
- msecs_to_jiffies(50000));
-
- /* Shouldn't happen. There are still references to
- * the object but, rather than deadlock, free the
- * object or pass back to rdma-core.
- */
- if (WARN_ON(!ret))
- err = -ETIMEDOUT;
- }
+ wait_for_completion(&elem->complete);
} else {
unsigned long until = jiffies + RXE_POOL_TIMEOUT;
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-09-28 15:54 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-28 15:53 [PATCH 0/4] RDMA/rxe: Fix MW/MR lifetime races Dongliang Qin
2026-09-28 15:53 ` [PATCH 1/4] RDMA/rxe: Take MR reference under MW lock Dongliang Qin
2026-09-28 15:53 ` [PATCH 2/4] RDMA/rxe: Reserve MR state during MW binding Dongliang Qin
2026-09-28 15:53 ` [PATCH 3/4] RDMA/rxe: Invalidate MWs on QP destroy Dongliang Qin
2026-09-28 15:53 ` [PATCH 4/4] RDMA/rxe: Do not force cleanup on pool timeout Dongliang Qin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®