* [PATCH net 1/3] net/rds: don't use unpin_user_pages_dirty_lock() from atomic context
2026-07-11 2:51 [PATCH net 0/3] net/rds: Bug fix ports Allison Henderson
@ 2026-07-11 2:51 ` Allison Henderson
2026-07-11 2:51 ` [PATCH net 2/3] net/rds: hold the socket while an rds_mr references it Allison Henderson
2026-07-11 2:51 ` [PATCH net 3/3] net/rds: fix rds_message leak in the rds_send_xmit() drop path Allison Henderson
2 siblings, 0 replies; 4+ messages in thread
From: Allison Henderson @ 2026-07-11 2:51 UTC (permalink / raw)
To: netdev, linux-rdma, pabeni, edumazet, kuba, horms; +Cc: achender
From: Gerd Rausch <gerd.rausch@oracle.com>
rds_rdma_free_op() and rds_atomic_free_op() are reached from the IB
send completion path via
rds_ib_tasklet_fn_send()
rds_ib_send_cqe_handler()
rds_message_put()
rds_message_purge()
rds_rdma_free_op() / rds_atomic_free_op()
which runs in tasklet (softirq) context. Both functions unpin the
user pages of the op with unpin_user_pages_dirty_lock(), which uses
set_page_dirty_lock() and thus may call lock_page() and sleep.
Sleeping in softirq context is not allowed and can deadlock or crash.
Dirty the pages with set_page_dirty() and release them with
unpin_user_page() instead, the same way this code handled the pages
before the conversion to the pin_user_pages API.
This mirrors Oracle UEK commit "net/rds: Avoid
unpin_user_pages_dirty_lock() in tasklets".
Fixes: 0d4597c8c5ab ("net/rds: Track user mapped pages through special API")
Signed-off-by: Gerd Rausch <gerd.rausch@oracle.com>
[achender: port to net-next; omit UEK's WARN_ON_ONCE(!page->mapping &&
irqs_disabled()) debug check; update commit message]
Assisted-by: Claude-Code:claude-fable-5
Signed-off-by: Allison Henderson <achender@kernel.org>
---
net/rds/rdma.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/net/rds/rdma.c b/net/rds/rdma.c
index 61fb6e45281bf..201cbe38fa893 100644
--- a/net/rds/rdma.c
+++ b/net/rds/rdma.c
@@ -495,9 +495,13 @@ void rds_rdma_free_op(struct rm_rdma_op *ro)
/* Mark page dirty if it was possibly modified, which
* is the case for a RDMA_READ which copies from remote
- * to local memory
+ * to local memory. This can be called from the IB
+ * send completion tasklet, so the sleeping _lock
+ * variant must not be used here.
*/
- unpin_user_pages_dirty_lock(&page, 1, !ro->op_write);
+ if (!ro->op_write)
+ set_page_dirty(page);
+ unpin_user_page(page);
}
}
@@ -513,8 +517,12 @@ void rds_atomic_free_op(struct rm_atomic_op *ao)
/* Mark page dirty if it was possibly modified, which
* is the case for a RDMA_READ which copies from remote
- * to local memory */
- unpin_user_pages_dirty_lock(&page, 1, true);
+ * to local memory. This can be called from the IB send
+ * completion tasklet, so the sleeping _lock variant must
+ * not be used here.
+ */
+ set_page_dirty(page);
+ unpin_user_page(page);
kfree(ao->op_notifier);
ao->op_notifier = NULL;
--
2.25.1
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH net 2/3] net/rds: hold the socket while an rds_mr references it
2026-07-11 2:51 [PATCH net 0/3] net/rds: Bug fix ports Allison Henderson
2026-07-11 2:51 ` [PATCH net 1/3] net/rds: don't use unpin_user_pages_dirty_lock() from atomic context Allison Henderson
@ 2026-07-11 2:51 ` Allison Henderson
2026-07-11 2:51 ` [PATCH net 3/3] net/rds: fix rds_message leak in the rds_send_xmit() drop path Allison Henderson
2 siblings, 0 replies; 4+ messages in thread
From: Allison Henderson @ 2026-07-11 2:51 UTC (permalink / raw)
To: netdev, linux-rdma, pabeni, edumazet, kuba, horms; +Cc: achender
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.
In the on-demand-paging path in rds_cmsg_rdma_args(), we take the reference
after the transport get_mr() call succeeds. This is because its error
path frees the MR with kfree() directly rather than through
__rds_put_mr_final(). So an early hold in this case would leak the socket
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; update commit message]
Assisted-by: Claude-Code:claude-fable-5
Signed-off-by: Allison Henderson <achender@kernel.org>
---
net/rds/rdma.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/net/rds/rdma.c b/net/rds/rdma.c
index 201cbe38fa893..fe221968f3fe7 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;
@@ -755,6 +760,10 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,
}
rdsdebug("Need odp; local_odp_mr %p trans_private %p\n",
local_odp_mr, local_odp_mr->r_trans_private);
+ /* From here on the MR is torn down through
+ * __rds_put_mr_final(), which drops this reference.
+ */
+ sock_hold(rds_rs_to_sk(rs));
op->op_odp_mr = local_odp_mr;
op->op_odp_addr = iov->addr;
}
--
2.25.1
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH net 3/3] net/rds: fix rds_message leak in the rds_send_xmit() drop path
2026-07-11 2:51 [PATCH net 0/3] net/rds: Bug fix ports Allison Henderson
2026-07-11 2:51 ` [PATCH net 1/3] net/rds: don't use unpin_user_pages_dirty_lock() from atomic context Allison Henderson
2026-07-11 2:51 ` [PATCH net 2/3] net/rds: hold the socket while an rds_mr references it Allison Henderson
@ 2026-07-11 2:51 ` Allison Henderson
2 siblings, 0 replies; 4+ messages in thread
From: Allison Henderson @ 2026-07-11 2:51 UTC (permalink / raw)
To: netdev, linux-rdma, pabeni, edumazet, kuba, horms; +Cc: achender
From: Sharath Srinivasan <sharath.srinivasan@oracle.com>
When rds_send_xmit() picks the next message off cp_send_queue it takes
its own reference with rds_message_addref(). If the message then hits
the never-retransmit check (RDS_MSG_FLUSH, or an RDMA op that was
already retransmitted), it is moved to the local to_be_dropped list and
that reference is dropped after the batch.
However, if RDS_MSG_ON_CONN has already been cleared - e.g. a racing
rds_send_drop_to() or rds_send_path_reset() took the message off the
connection lists - the message is not added to to_be_dropped and the
reference taken above is never dropped: cp_xmit_rm has not been set at
this point, so the loop simply abandons rm and the rds_message (and
everything it pins: pages, MRs, notifiers) leaks after an RDMA error.
Drop the reference directly in that case.
This mirrors Oracle UEK commit "net/rds: fix rds_message memleak in
rds_send_xmit".
Fixes: 2ad8099b58f2 ("RDS: rds_send_xmit() locking/irq fixes")
Signed-off-by: Gerd Rausch <gerd.rausch@oracle.com>
Signed-off-by: Sharath Srinivasan <sharath.srinivasan@oracle.com>
[achender: port to net-next; update commit message]
Assisted-by: Claude-Code:claude-fable-5
Signed-off-by: Allison Henderson <achender@kernel.org>
---
net/rds/send.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/net/rds/send.c b/net/rds/send.c
index 68be1bf0e0adf..ab3a8366c53bd 100644
--- a/net/rds/send.c
+++ b/net/rds/send.c
@@ -339,9 +339,17 @@ int rds_send_xmit(struct rds_conn_path *cp)
(rm->rdma.op_active &&
test_bit(RDS_MSG_RETRANSMITTED, &rm->m_flags))) {
spin_lock_irqsave(&cp->cp_lock, flags);
- if (test_and_clear_bit(RDS_MSG_ON_CONN, &rm->m_flags))
+ if (test_and_clear_bit(RDS_MSG_ON_CONN, &rm->m_flags)) {
+ /* our ref is put after the batch */
list_move(&rm->m_conn_item, &to_be_dropped);
- spin_unlock_irqrestore(&cp->cp_lock, flags);
+ spin_unlock_irqrestore(&cp->cp_lock, flags);
+ } else {
+ /* already off the conn list; drop
+ * the ref taken above ourselves
+ */
+ spin_unlock_irqrestore(&cp->cp_lock, flags);
+ rds_message_put(rm);
+ }
continue;
}
--
2.25.1
^ permalink raw reply related [flat|nested] 4+ messages in thread