From: "yanjun.zhu" <yanjun.zhu@linux.dev>
To: Ibrahim Hashimov <security@auditcode.ai>,
Zhu Yanjun <zyjzyj2000@gmail.com>, Jason Gunthorpe <jgg@ziepe.ca>,
Leon Romanovsky <leon@kernel.org>,
Zhu Yanjun <yanjun.zhu@linux.dev>
Cc: linux-rdma@vger.kernel.org, linux-kernel@vger.kernel.org,
stable@vger.kernel.org
Subject: Re: [PATCH] RDMA/rxe: fix responder UAF on IB_QP_MAX_DEST_RD_ATOMIC modify_qp
Date: Wed, 8 Jul 2026 18:22:32 -0700 [thread overview]
Message-ID: <d4af0c54-3bd6-401c-905a-ce546f2da475@linux.dev> (raw)
In-Reply-To: <20260708224550.1281-1-security@auditcode.ai>
On 7/8/26 3:45 PM, Ibrahim Hashimov wrote:
> rxe_qp_from_attr()'s IB_QP_MAX_DEST_RD_ATOMIC branch frees and
> reallocates qp->resp.resources[] (the rd_atomic resource array used by
> the responder to track in-flight RDMA READ/ATOMIC/FLUSH requests)
> completely outside of the IB_QP_STATE handling above it. Unlike every
> other place that tears this array down -- rxe_qp_reset(), reached only
> under IB_QP_STATE, always calls rxe_disable_task(&qp->recv_task) /
> rxe_disable_task(&qp->send_task) to drain the responder and requester
> tasks before touching per-QP state, then re-enables them -- this branch
> runs with the responder task (rxe_receiver(), scheduled as recv_task on
> the rxe_wq workqueue) fully live and unlocked. A userspace modify_qp()
> that sets only IB_QP_MAX_DEST_RD_ATOMIC (no state change, so
> __qp_chk_state()/ib_modify_qp_is_ok() never runs and qp->state_lock is
> never taken here) can therefore race the responder in two ways:
>
> 1. free_rd_atomic_resources() calls kfree(qp->resp.resources) and
> alloc_rd_atomic_resources() kzalloc_objs()'s a new array while
> rxe_prepare_res()/find_resource() in rxe_resp.c are concurrently
> walking &qp->resp.resources[i] with no lock held -- a straight
> free-vs-read race on the array itself.
>
> 2. free_rd_atomic_resources() only NULLs qp->resp.resources; it never
> clears qp->resp.res, the raw pointer *into* that array that
> rxe_resp.c caches across a multi-packet RDMA READ/ATOMIC/FLUSH
> reply (set at rxe_resp.c read/atomic/flush-reply sites, cleared
> only on the normal completion paths). If a modify_qp() races a
> resource still referenced by qp->resp.res, the array is freed out
> from under the cached pointer and the next reply packet dereferences
> it -- independent of the kfree/kzalloc_objs() window in (1).
>
> Reproduced with KASAN: a single process driving one RC QP pair in rxe
> loopback, one thread pumping large multi-packet IBV_WR_RDMA_READs
> against qpB while a second thread hammers
> ibv_modify_qp(qpB, IB_QP_MAX_DEST_RD_ATOMIC), reliably (~11s) produces
>
> BUG: KASAN: slab-use-after-free in rxe_receiver+0x4f78/0x89e0 [rdma_rxe]
> Workqueue: rxe_wq do_work [rdma_rxe]
Can you share all the stack trace with us? Thanks a lot.
>
> with the freed kmalloc-1k object being the rd_atomic resource array
> freed by the modify_qp() thread while the recv_task kworker reads it.
> An identical run modifying only IB_QP_MIN_RNR_TIMER (no resource free)
> is clean.
>
> Fix both races the same way rxe_qp_reset() already handles tearing down
> this exact array: quiesce the responder task around the free/realloc by
> calling rxe_disable_task(&qp->recv_task) before free_rd_atomic_resources()
> and rxe_enable_task(&qp->recv_task) after alloc_rd_atomic_resources(),
> so rxe_receiver() cannot observe the array mid-free/mid-realloc. And
> close the still-open window for (2) at the source: have
> free_rd_atomic_resources() clear qp->resp.res along with
> qp->resp.resources, exactly like the existing completion paths in
> rxe_resp.c (check_rkey()/duplicate_request()/RESPST_CLEANUP) already do
> when a resource's lifetime ends, so a drained-and-resumed responder
> restarts at RESPST_CHK_PSN against the fresh array instead of replaying
> a stale reference into the old one.
>
> Only qp->recv_task is drained: qp->resp.resources / qp->resp.res are
> touched exclusively by the responder (rxe_resp.c); the requester
> (send_task / rxe_sender()) never reads them, so there is no need to
> widen this beyond what rxe_qp_reset() would drain for the equivalent
> state.
>
> Verified on the same v6.19 KASAN stand: with this fix applied, the
> identical differential reproducer drives sustained MAX_DEST_RD_ATOMIC
> storms against qpB well past the ~11s pre-fix time-to-first-splat with
> zero KASAN reports, versus reliably tripping the slab-use-after-free in
> rxe_receiver() described above before the fix.
>
> Fixes: 8700e3e7c485 ("Soft RoCE driver")
> Cc: stable@vger.kernel.org
> Signed-off-by: Ibrahim Hashimov <security@auditcode.ai>
> Assisted-by: AuditCode-AI:2026.07
> ---
> drivers/infiniband/sw/rxe/rxe_qp.c | 9 ++++++++-
> 1 file changed, 8 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/infiniband/sw/rxe/rxe_qp.c b/drivers/infiniband/sw/rxe/rxe_qp.c
> index f3dff1aea96a..646957707765 100644
> --- a/drivers/infiniband/sw/rxe/rxe_qp.c
> +++ b/drivers/infiniband/sw/rxe/rxe_qp.c
> @@ -172,6 +172,7 @@ static void free_rd_atomic_resources(struct rxe_qp *qp)
> }
> kfree(qp->resp.resources);
> qp->resp.resources = NULL;
> + qp->resp.res = NULL;
> }
> }
>
> @@ -709,9 +710,15 @@ int rxe_qp_from_attr(struct rxe_qp *qp, struct ib_qp_attr *attr, int mask,
>
> qp->attr.max_dest_rd_atomic = max_dest_rd_atomic;
>
> + /*
> + * Not gated by IB_QP_STATE above: quiesce the responder task
> + * the same way rxe_qp_reset() does before touching this
> + * array, so rxe_receiver() can't race the free/realloc.
> + */
> + rxe_disable_task(&qp->recv_task);
> free_rd_atomic_resources(qp);
> -
> err = alloc_rd_atomic_resources(qp, max_dest_rd_atomic);
> + rxe_enable_task(&qp->recv_task);
If alloc_rd_atomic_resources fails, that is, qp->resp.resources is NULL.
After rxe_enable_task(&qp->recv_task); qp->resp.resources(NULL) will be
used in resp. This will cause problems.
drivers/infiniband/sw/rxe/rxe_resp.c:656: res =
&qp->resp.resources[qp->resp.res_head];
drivers/infiniband/sw/rxe/rxe_resp.c:1325: struct resp_res *res =
&qp->resp.resources[i];
Zhu Yanjun
> if (err)
> return err;
> }
next prev parent reply other threads:[~2026-07-09 1:22 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-08 22:45 [PATCH] RDMA/rxe: fix responder UAF on IB_QP_MAX_DEST_RD_ATOMIC modify_qp Ibrahim Hashimov
2026-07-09 1:22 ` yanjun.zhu [this message]
2026-07-09 7:22 ` Ibrahim Hashimov
2026-07-09 7:26 ` [PATCH v2] " Ibrahim Hashimov
2026-07-09 18:24 ` yanjun.zhu
2026-07-12 8:58 ` Leon Romanovsky
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=d4af0c54-3bd6-401c-905a-ce546f2da475@linux.dev \
--to=yanjun.zhu@linux.dev \
--cc=jgg@ziepe.ca \
--cc=leon@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rdma@vger.kernel.org \
--cc=security@auditcode.ai \
--cc=stable@vger.kernel.org \
--cc=zyjzyj2000@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.