From: Allison Henderson <achender@kernel.org>
To: netdev@vger.kernel.org, linux-rdma@vger.kernel.org,
pabeni@redhat.com, edumazet@google.com, kuba@kernel.org,
horms@kernel.org
Cc: achender@kernel.org, jhubbard@nvidia.com, leon@kernel.org
Subject: [PATCH net v4 2/4] net/rds: hold the socket while an rds_mr references it
Date: Wed, 29 Jul 2026 21:16:27 -0700 [thread overview]
Message-ID: <20260730041629.3512480-3-achender@kernel.org> (raw)
In-Reply-To: <20260730041629.3512480-1-achender@kernel.org>
From: Håkon Bugge <haakon.bugge@oracle.com>
Each rds_mr stores a bare back pointer to the socket that created it
(mr->r_sock) but takes no reference on it. When the mr is destroyed it
references the rs. Hence, provisions must be made to avoid the rs
being destroyed before all mrs referencing it have been destroyed.
The MR itself is refcounted, and in-flight messages legitimately hold
MR krefs that can outlive the socket: rds_release() drops the rb-tree
references via rds_rdma_drop_keys(), but a send completion arriving
afterwards drops the final message reference from the CQ handler and
ends up in
rds_message_purge()
__rds_put_mr_final()
rds_destroy_mr() -> takes rs->rs_rdma_lock
dereferencing a socket that may already have been freed.
Oracle UEK fixed the same use-after-free ("rds: Add proper refcnt when
an RDS MR references an RDS Socket") after seeing crashes of the form:
PF: supervisor write access in kernel mode
_raw_spin_lock_irqsave+0x4a/0x6a
__rds_put_mr_final+0x2c/0xe0 [rds]
rds_message_purge+0x13c/0x150 [rds]
rds_message_put+0x39/0x54 [rds]
rds_ib_send_cqe_handler+0x147/0x3dd [rds_rdma]
To fix this, take a socket reference when an MR is created and drop it
when the final MR kref goes away. The reference cycle is broken by
rds_release(), which always runs rds_rdma_drop_keys() on close. So the
socket reference held by an MR never prevents release, it only delays
sk_free() until the last MR user is done.
The hold sits next to kref_init() at both allocation sites -
__rds_rdma_map() and the on-demand-paging path in
rds_cmsg_rdma_args() - so every MR owns exactly one socket reference
from the moment it becomes kref-managed. For that to work on the ODP
path, its get_mr() error handling is converted from a bare kfree() to
kref_put(..., __rds_put_mr_final), with r_trans_private cleared first
since it holds an ERR_PTR there; both sites then tear down through
the same path and a future error-path change cannot silently leak or
double-drop the reference.
Fixes: eff5f53bef75 ("RDS: RDMA support")
Signed-off-by: Håkon Bugge <haakon.bugge@oracle.com>
[achender: port to net-next (sock_hold/sock_put in place of the UEK
rds_sock_addref/rds_sock_put helpers); also balance the reference on
the rds_cmsg_rdma_args() ODP path and unify its error path with
__rds_put_mr_final(); update commit message]
Assisted-by: Claude-Code:claude-fable-5
Signed-off-by: Allison Henderson <achender@kernel.org>
---
v2, v3: unchanged.
v4:
- Take the socket reference next to kref_init() on the ODP path too,
and tear its get_mr() error path down through __rds_put_mr_final()
so both allocation sites follow the same rule.
- Document the counted reference at the r_sock field.
net/rds/rdma.c | 14 +++++++++++++-
net/rds/rds.h | 5 ++++-
2 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/net/rds/rdma.c b/net/rds/rdma.c
index f360a7b3b5fe7..078090d292fa3 100644
--- a/net/rds/rdma.c
+++ b/net/rds/rdma.c
@@ -117,6 +117,7 @@ void __rds_put_mr_final(struct kref *kref)
struct rds_mr *mr = container_of(kref, struct rds_mr, r_kref);
rds_destroy_mr(mr);
+ sock_put(rds_rs_to_sk(mr->r_sock));
kfree(mr);
}
@@ -243,7 +244,11 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args,
kref_init(&mr->r_kref);
RB_CLEAR_NODE(&mr->r_rb_node);
mr->r_trans = rs->rs_transport;
+ /* The MR can outlive its socket: a socket reference is held
+ * until the final kref is dropped in __rds_put_mr_final().
+ */
mr->r_sock = rs;
+ sock_hold(rds_rs_to_sk(rs));
if (args->flags & RDS_RDMA_USE_ONCE)
mr->r_use_once = 1;
@@ -759,7 +764,12 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,
RB_CLEAR_NODE(&local_odp_mr->r_rb_node);
kref_init(&local_odp_mr->r_kref);
local_odp_mr->r_trans = rs->rs_transport;
+ /* The MR can outlive its socket: a socket
+ * reference is held until the final kref is
+ * dropped in __rds_put_mr_final().
+ */
local_odp_mr->r_sock = rs;
+ sock_hold(rds_rs_to_sk(rs));
local_odp_mr->r_trans_private =
rs->rs_transport->get_mr(
NULL, 0, rs, &local_odp_mr->r_key, NULL,
@@ -768,7 +778,9 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,
ret = PTR_ERR(local_odp_mr->r_trans_private);
rdsdebug("get_mr ret %d %p\"", ret,
local_odp_mr->r_trans_private);
- kfree(local_odp_mr);
+ local_odp_mr->r_trans_private = NULL;
+ kref_put(&local_odp_mr->r_kref,
+ __rds_put_mr_final);
ret = -EOPNOTSUPP;
goto out_pages;
}
diff --git a/net/rds/rds.h b/net/rds/rds.h
index 14bff7440b796..2db49573dacd5 100644
--- a/net/rds/rds.h
+++ b/net/rds/rds.h
@@ -320,7 +320,10 @@ struct rds_mr {
unsigned int r_invalidate:1;
unsigned int r_write:1;
- struct rds_sock *r_sock; /* back pointer to the socket that owns us */
+ struct rds_sock *r_sock; /* socket that owns us; counted
+ * reference, dropped by
+ * __rds_put_mr_final()
+ */
struct rds_transport *r_trans;
void *r_trans_private;
};
--
2.25.1
next prev parent reply other threads:[~2026-07-30 4:16 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-30 4:16 [PATCH net v4 0/4] net/rds: Bug fix ports Allison Henderson
2026-07-30 4:16 ` [PATCH net v4 1/4] net/rds: don't use unpin_user_pages_dirty_lock() from atomic context Allison Henderson
2026-07-30 4:16 ` Allison Henderson [this message]
2026-07-30 4:16 ` [PATCH net v4 3/4] net/rds: fix rds_message leak in the rds_send_xmit() drop path Allison Henderson
2026-07-30 4:16 ` [PATCH net v4 4/4] net/rds: unpin MR pages with unpin_user_pages_dirty_lock() Allison Henderson
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=20260730041629.3512480-3-achender@kernel.org \
--to=achender@kernel.org \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=jhubbard@nvidia.com \
--cc=kuba@kernel.org \
--cc=leon@kernel.org \
--cc=linux-rdma@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox