From: Leon Romanovsky <leon@kernel.org>
To: "yanjun.zhu" <yanjun.zhu@linux.dev>
Cc: Ibrahim Hashimov <security@auditcode.ai>,
zyjzyj2000@gmail.com, jgg@ziepe.ca, linux-rdma@vger.kernel.org,
linux-kernel@vger.kernel.org, stable@vger.kernel.org
Subject: Re: [PATCH v2] RDMA/rxe: fix responder UAF on IB_QP_MAX_DEST_RD_ATOMIC modify_qp
Date: Sun, 12 Jul 2026 11:58:55 +0300 [thread overview]
Message-ID: <20260712085855.GD33197@unreal> (raw)
In-Reply-To: <821c7993-b99d-42eb-be3f-0c2b9ac33340@linux.dev>
On Thu, Jul 09, 2026 at 11:24:32AM -0700, yanjun.zhu wrote:
> On 7/9/26 12:26 AM, 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]
> >
> > 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) only after alloc_rd_atomic_resources()
> > has succeeded, so rxe_receiver() cannot observe the array mid-free/
> > mid-realloc. On the alloc-failure path the responder is deliberately
> > left quiesced: qp->resp.resources is NULL at that point and
> > rxe_prepare_res()/find_resource() would dereference it, so recv_task
> > must not be re-enabled until a fresh array has been installed. 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
> > ---
> > v2: address Zhu Yanjun's review of v1
> > (https://lore.kernel.org/linux-rdma/20260708224550.1281-1-security@auditcode.ai/):
> > only re-enable recv_task after alloc_rd_atomic_resources() succeeds, so
> > the responder is not resumed against a NULL qp->resp.resources on the
> > ENOMEM path (rxe_prepare_res()/find_resource() would dereference it).
> > No change to the successful path; fix description updated accordingly.
> >
> > drivers/infiniband/sw/rxe/rxe_qp.c | 16 +++++++++++++++-
> > 1 file changed, 15 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/infiniband/sw/rxe/rxe_qp.c b/drivers/infiniband/sw/rxe/rxe_qp.c
> > index f3dff1aea96a..e39fb144cbbb 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,11 +710,24 @@ 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;
> > + /*
> > + * This branch is not gated by IB_QP_STATE, so the responder
> > + * task is live here. Quiesce it the way rxe_qp_reset() does
> > + * before swapping the rd_atomic resource array, so
> > + * rxe_receiver() cannot 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);
> > + /*
> > + * On failure the responder stays quiesced: qp->resp.resources
> > + * is NULL now, and rxe_prepare_res()/find_resource() would
> > + * dereference it, so do not re-enable recv_task until a fresh
> > + * array has been installed.
> > + */
> > if (err)
> > return err;
> > + rxe_enable_task(&qp->recv_task);
>
> Thanks a lot. I am fine with this commit. Please Leon and Jason comment on
> this commit.
1. Please send patches as a standalone series, not as a reply to
the previous one.
2. Write commit messages in a clear and sensible way so they are
easy for humans to understand. Keep them short and direct, and
describe the function call flow instead of producing AI prose.
Thanks
>
> Reviewed-by: Zhu Yanjun <yanjun.zhu@linux.dev>
>
> Zhu Yanjun
>
> > }
> > if (mask & IB_QP_EN_SQD_ASYNC_NOTIFY)
>
prev parent reply other threads:[~2026-07-12 8:58 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
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 [this message]
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=20260712085855.GD33197@unreal \
--to=leon@kernel.org \
--cc=jgg@ziepe.ca \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rdma@vger.kernel.org \
--cc=security@auditcode.ai \
--cc=stable@vger.kernel.org \
--cc=yanjun.zhu@linux.dev \
--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.