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 Assisted-by: Codex:gpt-5.4 Signed-off-by: Zihan Xi Link: https://lore.kernel.org/all/cover.1784742007.git.zihanx@nebusec.ai/ --- 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/ 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