Re: [PATCH RFC] rxrpc: Fix use-after-free in rxrpc_destroy_all_peers()
Marco Elver <[email protected]>
| Newsgroups | dev.linux.lists.syzbot |
|---|---|
| Message-ID | <CANpmjNMOFq+vKs9XKdXjYuowJY0-R9XBrktjy2gm4nPWTFmW9g@mail.gmail.com> |
On Mon, 10 Aug 2026 at 17:04, 'syzbot' via syzkaller-upstream-moderation <[email protected]> wrote: > > During network namespace teardown, rxrpc_destroy_all_peers() iterates over > the rxnet->peer_hash table to print leaked peers. However, it does so > without holding the rxnet->peer_hash_lock. This allows a race condition > with asynchronous peer destruction triggered by the AFS subsystem via RCU, > which can concurrently remove a peer from the hash table and free it. > > When the RCU grace period expires, afs_free_addrlist() calls > rxrpc_kernel_put_peer(), which eventually invokes __rxrpc_put_peer(). This > function acquires rxnet->peer_hash_lock, removes the peer from the hash > table, and frees it via kfree_rcu(). If rxrpc_destroy_all_peers() is > concurrently iterating over the hash bucket, it may attempt to read the > hash_link.next pointer of a physically freed peer, resulting in a KASAN > slab-use-after-free. The crash log confirms this behavior, as one of the > leaked peers printed right before the crash has a refcount of 0, indicating > it was already put and waiting to be removed and freed. > > The crash report illustrates this issue: > > BUG: KASAN: slab-use-after-free in rxrpc_destroy_all_peers+0xcc/0x150 > net/rxrpc/peer_object.c:461 > Read of size 8 at addr ffff88811089e420 by task kworker/u8:1/13 > Call Trace: > <TASK> > rxrpc_destroy_all_peers+0xcc/0x150 net/rxrpc/peer_object.c:461 > rxrpc_exit_net+0x7f/0xc0 net/rxrpc/net_ns.c:114 > ops_exit_list net/core/net_namespace.c:199 [inline] > ops_undo_list+0x43d/0x8d0 net/core/net_namespace.c:252 > cleanup_net+0x572/0x810 net/core/net_namespace.c:702 > process_one_work kernel/workqueue.c:3322 [inline] > process_scheduled_works+0xa8e/0x14e0 kernel/workqueue.c:3405 > worker_thread+0x92d/0xe10 kernel/workqueue.c:3486 > kthread+0x388/0x470 kernel/kthread.c:436 > ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 > ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 > </TASK> > Freed by task 15: > kasan_save_stack mm/kasan/common.c:57 [inline] > kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 > kasan_save_free_info+0x40/0x50 mm/kasan/generic.c:584 > poison_slab_object mm/kasan/common.c:253 [inline] > __kasan_slab_free+0x5c/0x80 mm/kasan/common.c:285 > kasan_slab_free include/linux/kasan.h:235 [inline] > slab_free_hook mm/slub.c:2677 [inline] > __rcu_free_sheaf_prepare+0x12d/0x2a0 mm/slub.c:2928 > rcu_free_sheaf+0x31/0x200 mm/slub.c:5978 > rcu_do_batch kernel/rcu/tree.c:2645 [inline] > rcu_core+0x78b/0x10a0 kernel/rcu/tree.c:2897 > handle_softirqs+0x225/0x840 kernel/softirq.c:622 > run_ksoftirqd+0x36/0x60 kernel/softirq.c:1076 > smpboot_thread_fn+0x57c/0xa80 kernel/smpboot.c:160 > kthread+0x388/0x470 kernel/kthread.c:436 > ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 > ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 > > To fix this, acquire rxnet->peer_hash_lock using spin_lock_bh() before > iterating over the rxnet->peer_hash table in rxrpc_destroy_all_peers(), and > release it after the iteration is complete. The _bh variant is required > because __rxrpc_put_peer() can be invoked from an RCU callback in softirq > context, and using a plain spin_lock() could lead to a deadlock. This > change ensures that the hash table structure remains stable during > iteration and aligns with the locking patterns used in other teardown > functions within the rxrpc subsystem. > > Fixes: 17226f124038 ("rxrpc: Fix leak of rxrpc_peer objects") > Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot > Reported-by: [email protected] > Closes: https://syzkaller.appspot.com/bug?extid=c876adfab6362679008c > Link: https://syzkaller.appspot.com/ai_job?id=148b52c4-bf79-4dde-932f-8b4de7e83f41 > To: "David S. Miller" <[email protected]> > To: "David Howells" <[email protected]> > To: "Eric Dumazet" <[email protected]> > To: "Jakub Kicinski" <[email protected]> > To: <[email protected]> > To: "Marc Dionne" <[email protected]> > To: <[email protected]> > To: "Paolo Abeni" <[email protected]> > Cc: "Simon Horman" <[email protected]> > Cc: <[email protected]> > > --- > diff --git a/net/rxrpc/peer_object.c b/net/rxrpc/peer_object.c > index fa9a406e1..d81ed180e 100644 > --- a/net/rxrpc/peer_object.c > +++ b/net/rxrpc/peer_object.c > @@ -454,6 +454,8 @@ void rxrpc_destroy_all_peers(struct rxrpc_net *rxnet) > struct rxrpc_peer *peer; > int i; > > + spin_lock_bh(&rxnet->peer_hash_lock); > + > for (i = 0; i < HASH_SIZE(rxnet->peer_hash); i++) { > if (hlist_empty(&rxnet->peer_hash[i])) > continue; > @@ -465,6 +467,8 @@ void rxrpc_destroy_all_peers(struct rxrpc_net *rxnet) > &peer->srx.transport); > } > } > + > + spin_unlock_bh(&rxnet->peer_hash_lock); > } > > /** Holding peer_hash_lock prevents the immediate KASAN slab-use-after-free during hash bucket traversal, but this fix is incomplete and masks a larger lifecycle/quiescence bug in netns exit: 1. False Positive Leak Warnings: rxrpc_destroy_all_peers() does not destroy peers; it audits for leaks. External kernel consumers (such as fs/afs) release peer references asynchronously via RCU callbacks (afs_free_addrlist() -> rxrpc_kernel_put_peer()). Because netns teardown (rxrpc_exit_net()) does not synchronize or wait for in-flight RCU grace periods after destroying calls and connections, rxrpc_destroy_all_peers() races with pending RCU callbacks and logs spurious pr_err("Leaked peer...") messages for peers that are already undergoing normal destruction (refcount == 0). 2. Downstream Panic Hazard (rxrpc_destroy_all_locals()): Each struct rxrpc_peer holds a reference to peer->local. The local reference is only dropped when rxrpc_free_peer() runs. When netns exit proceeds directly from rxrpc_destroy_all_peers() to rxrpc_destroy_all_locals(), any in-flight peer release leaves rxnet->local_endpoints populated. rxrpc_destroy_all_locals() will then hit BUG(): if (!hlist_empty(&rxnet->local_endpoints)) { ... BUG(); } Please revise the patch to: 1. Retain spin_lock_bh(&rxnet->peer_hash_lock) in rxrpc_destroy_all_peers() to ensure atomic traversal of the hash table against concurrent unlinks. 2. Add an explicit rcu_barrier() in rxrpc_exit_net() after tearing down calls and connections, ensuring all pending RCU callbacks that drop peer and local references (e.g. from AFS address lists) complete prior to leak checks and local endpoint destruction (mirroring the pattern in af_rxrpc_exit()). ────── ### Suggested Revision diff --git a/net/rxrpc/net_ns.c b/net/rxrpc/net_ns.c index c7a023fb22d0..6e5458319fbc 100644 --- a/net/rxrpc/net_ns.c +++ b/net/rxrpc/net_ns.c @@ -113,6 +113,12 @@ static __net_exit void rxrpc_exit_net(struct net *net) timer_delete_sync(&rxnet->peer_keepalive_timer); rxrpc_destroy_all_calls(rxnet); rxrpc_destroy_all_connections(rxnet); + + /* Ensure all RCU callbacks releasing peer and local references + * (e.g. afs_free_addrlist) have completed before leak assertions. + */ + rcu_barrier(); + rxrpc_destroy_all_peers(rxnet); rxrpc_destroy_all_locals(rxnet); proc_remove(rxnet->proc_net); diff --git a/net/rxrpc/peer_object.c b/net/rxrpc/peer_object.c index fa9a406e1168..d81ed180e55b 100644 --- a/net/rxrpc/peer_object.c +++ b/net/rxrpc/peer_object.c @@ -454,6 +454,8 @@ void rxrpc_destroy_all_peers(struct rxrpc_net *rxnet) struct rxrpc_peer *peer; int i; + spin_lock_bh(&rxnet->peer_hash_lock); + for (i = 0; i < HASH_SIZE(rxnet->peer_hash); i++) { if (hlist_empty(&rxnet->peer_hash[i])) continue; @@ -465,6 +467,8 @@ void rxrpc_destroy_all_peers(struct rxrpc_net *rxnet) &peer->srx.transport); } } + + spin_unlock_bh(&rxnet->peer_hash_lock); } Please independently validate the above claims and retest as required!