[PATCH net v2 1/1] rxrpc: fix encap_rcv skb accounting exhaustion
From: Zihan Xi <hidden>
Date: 2026-07-29 19:52:12
Subsystem:
networking [general], rxrpc sockets (af_rxrpc), the rest · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, David Howells, Marc Dionne, Linus Torvalds
rxrpc_encap_rcv() moves encapsulated UDP packets onto the local
RxRPC queue without preserving UDP receive-buffer accounting. A local
AF_RXRPC service such as the AFS callback listener can therefore be
flooded with RxRPC-shaped UDP packets until the local queue grows
without bound and consumes large amounts of memory.
Reaccount encapsulated packets against the UDP socket before queueing
them on the RxRPC local queue and drop packets once the socket rcvbuf
limit is reached. Clear sk_user_data under RCU protection and release
the socket only after queued skbs are purged so the new skb ownership
does not outlive the UDP socket.
Fixes: 446b3e14525b ("rxrpc: Move packet reception processing into I/O thread")
Cc: stable@vger.kernel.org
Reported-by: Vega <redacted>
Assisted-by: Codex:gpt-5.4
Signed-off-by: Zihan Xi <redacted>
Link: https://lore.kernel.org/all/cover.1784742007.git.zihanx@nebusec.ai/ (local)
---
changes in v2:
- switch the drop path from atomic_inc(&udp_sk->sk_drops) to
sk_drops_inc(udp_sk)
- retarget Fixes to 446b3e14525b, the queue-to-I/O-thread boundary
identified in the latest public review discussion
- add a canonical Link: trailer for the public v1 posting
- state explicitly that the cover crash log is decode_stacktrace.sh output
- explain in the cover why packetdrill was not used
- reroll as v2 of the public v1 thread
- v1 Link: https://lore.kernel.org/all/cover.1784742007.git.zihanx@nebusec.ai/ (local)
net/rxrpc/io_thread.c | 15 +++++++++++++--
net/rxrpc/local_object.c | 8 ++++++--
2 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
index dc5184a2f..a41cf094e 100644
--- a/net/rxrpc/io_thread.c
+++ b/net/rxrpc/io_thread.c@@ -41,8 +41,6 @@ int rxrpc_encap_rcv(struct sock *udp_sk, struct sk_buff *skb) if (skb->tstamp == 0) skb->tstamp = ktime_get_real(); - skb->mark = RXRPC_SKB_MARK_PACKET; - rxrpc_new_skb(skb, rxrpc_skb_new_encap_rcv); rx_queue = &local->rx_queue; #ifdef CONFIG_AF_RXRPC_INJECT_RX_DELAY if (rxrpc_inject_rx_delay ||
@@ -52,6 +50,19 @@ int rxrpc_encap_rcv(struct sock *udp_sk, struct sk_buff *skb) } #endif + if (atomic_read(&udp_sk->sk_rmem_alloc) >= READ_ONCE(udp_sk->sk_rcvbuf) || + !sk_rmem_schedule(udp_sk, skb, skb->truesize)) { + sk_drops_inc(udp_sk); + kfree_skb(skb); + return 0; + } + + skb->dev = NULL; + skb_set_owner_r(skb, udp_sk); + skb_dst_force(skb); + + skb->mark = RXRPC_SKB_MARK_PACKET; + rxrpc_new_skb(skb, rxrpc_skb_new_encap_rcv); skb_queue_tail(rx_queue, skb); wake_up_process(io_thread); return 0;
diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c
index 169f9dfda..6604f9f95 100644
--- a/net/rxrpc/local_object.c
+++ b/net/rxrpc/local_object.c@@ -437,8 +437,8 @@ void rxrpc_destroy_local(struct rxrpc_local *local) if (socket) { local->socket = NULL; kernel_sock_shutdown(socket, SHUT_RDWR); - socket->sk->sk_user_data = NULL; - sock_release(socket); + rcu_assign_sk_user_data(socket->sk, NULL); + synchronize_rcu(); } /* At this point, there should be no more packets coming in to the
@@ -448,6 +448,10 @@ void rxrpc_destroy_local(struct rxrpc_local *local) rxrpc_purge_queue(&local->rx_delay_queue); #endif rxrpc_purge_queue(&local->rx_queue); + + if (socket) + sock_release(socket); + rxrpc_purge_client_connections(local); page_frag_cache_drain(&local->tx_alloc); }
--
2.43.0