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!
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.