--- /dev/null
+From d3f440424bf18f9b9cc4234b7379b1fc18f77b7e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Apr 2026 22:24:32 +0000
+Subject: rxrpc: Fix recvmsg() unconditional requeue
+
+From: David Howells <dhowells@redhat.com>
+
+[ Upstream commit 2c28769a51deb6022d7fbd499987e237a01dd63a ]
+
+If rxrpc_recvmsg() fails because MSG_DONTWAIT was specified but the call
+at the front of the recvmsg queue already has its mutex locked, it
+requeues the call - whether or not the call is already queued. The call
+may be on the queue because MSG_PEEK was also passed and so the call was
+not dequeued or because the I/O thread requeued it.
+
+The unconditional requeue may then corrupt the recvmsg queue, leading to
+things like UAFs or refcount underruns.
+
+Fix this by only requeuing the call if it isn't already on the queue -
+and moving it to the front if it is already queued. If we don't queue
+it, we have to put the ref we obtained by dequeuing it.
+
+Also, MSG_PEEK doesn't dequeue the call so shouldn't call
+rxrpc_notify_socket() for the call if we didn't use up all the data on
+the queue, so fix that also.
+
+Fixes: 540b1c48c37a ("rxrpc: Fix deadlock between call creation and sendmsg/recvmsg")
+Reported-by: Faith <faith@zellic.io>
+Reported-by: Pumpkin Chang <pumpkin@devco.re>
+Signed-off-by: David Howells <dhowells@redhat.com>
+Acked-by: Marc Dionne <marc.dionne@auristor.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Cc: stable@vger.kernel.org
+[Adapted to 5.10: use write_lock_bh/write_unlock_bh, trace_rxrpc_call
+ directly for see-call tracing, 5.10 trace enum naming convention, and
+ added entries to both plain enum and EM() macro list.]
+Signed-off-by: Jay Wang <wanjay@amazon.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/trace/events/rxrpc.h | 8 ++++++++
+ net/rxrpc/recvmsg.c | 22 ++++++++++++++++++----
+ 2 files changed, 26 insertions(+), 4 deletions(-)
+
+diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
+index 221856f2d295c..6cde10ae4445d 100644
+--- a/include/trace/events/rxrpc.h
++++ b/include/trace/events/rxrpc.h
+@@ -93,9 +93,13 @@ enum rxrpc_call_trace {
+ rxrpc_call_put_notimer,
+ rxrpc_call_put_timer,
+ rxrpc_call_put_userid,
++ rxrpc_call_put_recvmsg_peek_nowait,
+ rxrpc_call_queued,
+ rxrpc_call_queued_ref,
+ rxrpc_call_release,
++ rxrpc_call_see_recvmsg_requeue,
++ rxrpc_call_see_recvmsg_requeue_first,
++ rxrpc_call_see_recvmsg_requeue_move,
+ rxrpc_call_seen,
+ };
+
+@@ -291,9 +295,13 @@ enum rxrpc_tx_point {
+ EM(rxrpc_call_put_notimer, "PnT") \
+ EM(rxrpc_call_put_timer, "PTM") \
+ EM(rxrpc_call_put_userid, "Pus") \
++ EM(rxrpc_call_put_recvmsg_peek_nowait, "PpN") \
+ EM(rxrpc_call_queued, "QUE") \
+ EM(rxrpc_call_queued_ref, "QUR") \
+ EM(rxrpc_call_release, "RLS") \
++ EM(rxrpc_call_see_recvmsg_requeue, "SrQ") \
++ EM(rxrpc_call_see_recvmsg_requeue_first,"SrF") \
++ EM(rxrpc_call_see_recvmsg_requeue_move, "SrM") \
+ E_(rxrpc_call_seen, "SEE")
+
+ #define rxrpc_transmit_traces \
+diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c
+index 7878267739378..301b8acf78f55 100644
+--- a/net/rxrpc/recvmsg.c
++++ b/net/rxrpc/recvmsg.c
+@@ -607,7 +607,8 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
+
+ if (after(call->rx_top, call->rx_hard_ack) &&
+ call->rxtx_buffer[(call->rx_hard_ack + 1) & RXRPC_RXTX_BUFF_MASK])
+- rxrpc_notify_socket(call);
++ if (!(flags & MSG_PEEK))
++ rxrpc_notify_socket(call);
+ break;
+ default:
+ ret = 0;
+@@ -642,11 +643,24 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
+ error_requeue_call:
+ if (!(flags & MSG_PEEK)) {
+ write_lock_bh(&rx->recvmsg_lock);
+- list_add(&call->recvmsg_link, &rx->recvmsg_q);
+- write_unlock_bh(&rx->recvmsg_lock);
++ if (list_empty(&call->recvmsg_link)) {
++ list_add(&call->recvmsg_link, &rx->recvmsg_q);
++ trace_rxrpc_call(call->debug_id,
++ rxrpc_call_see_recvmsg_requeue,
++ refcount_read(&call->ref),
++ __builtin_return_address(0), NULL);
++ write_unlock_bh(&rx->recvmsg_lock);
++ } else if (list_is_first(&call->recvmsg_link, &rx->recvmsg_q)) {
++ write_unlock_bh(&rx->recvmsg_lock);
++ rxrpc_put_call(call, rxrpc_call_see_recvmsg_requeue_first);
++ } else {
++ list_move(&call->recvmsg_link, &rx->recvmsg_q);
++ write_unlock_bh(&rx->recvmsg_lock);
++ rxrpc_put_call(call, rxrpc_call_see_recvmsg_requeue_move);
++ }
+ trace_rxrpc_recvmsg(call, rxrpc_recvmsg_requeue, 0, 0, 0, 0);
+ } else {
+- rxrpc_put_call(call, rxrpc_call_put);
++ rxrpc_put_call(call, rxrpc_call_put_recvmsg_peek_nowait);
+ }
+ error_no_call:
+ release_sock(&rx->sk);
+--
+2.53.0
+