Re: [PATCH net] ipv6: anycast: insert aca into global hash under idev->lock

From: Kuniyuki Iwashima

Date: Thu May 28 2026 - 23:41:25 EST


On Thu, May 28, 2026 at 8:20 PM Jiayuan Chen <jiayuan.chen@xxxxxxxxx> wrote:
>
> syzbot reported a splat [1]: a slab-use-after-free in
> ipv6_chk_acast_addr(), which walks the global inet6_acaddr_lst[] hash
> under RCU and dereferences a struct ifacaddr6 that has already been
> freed while still linked in the hash, so a later reader walks into a
> dangling node.
>
> In __ipv6_dev_ac_inc() the aca is allocated with refcount 1, then
> aca_get() bumps it to 2 to keep it alive across the unlocked region.
> It is published to idev->ac_list under idev->lock, but
> ipv6_add_acaddr_hash() runs after write_unlock_bh(). A concurrent
> teardown (ipv6_ac_destroy_dev() from addrconf_ifdown(), under RTNL)
> can slip into that window:
>
> CPU0 __ipv6_dev_ac_inc CPU1 ipv6_ac_destroy_dev (RTNL)
> ------------------------------ ------------------------------------
> aca_alloc() refcnt 1
> aca_get() refcnt 2
> write_lock_bh(idev->lock)
> add aca to ac_list
> write_unlock_bh(idev->lock)
> write_lock_bh(idev->lock)
> pull aca off ac_list
> write_unlock_bh(idev->lock)
> ipv6_del_acaddr_hash(aca)
> hlist_del_init_rcu() is a no-op,
> aca is not in the hash yet
> aca_put() refcnt 2->1
> ipv6_add_acaddr_hash(aca)
> aca now inserted into the hash
> aca_put() refcnt 1->0
> call_rcu(aca_free_rcu) -> kfree(aca)
>
> The hash removal becomes a no-op because the insertion has not
> happened yet, so once CPU0 inserts and drops the last reference, the
> aca is freed while still linked in inet6_acaddr_lst[], and readers
> dereference freed memory after the slab slot is reused.
>
> This window opened once RTNL stopped serializing the join path against
> device teardown. Move ipv6_add_acaddr_hash() inside the idev->lock
> section so the ac_list and hash insertions are atomic with respect to
> teardown: a racing remover now either misses the aca entirely or finds
> it in both lists.
>
> [1] https://syzkaller.appspot.com/bug?extid=a01df04303c131efbf3a

Closes: and Reported-by ?

>
> Fixes: eb1ac9ff6c4a ("ipv6: anycast: Don't hold RTNL for IPV6_JOIN_ANYCAST.")
> Signed-off-by: Jiayuan Chen <jiayuan.chen@xxxxxxxxx>

Change itself looks good, thanks

Reviewed-by: Kuniyuki Iwashima <kuniyu@xxxxxxxxxx>


> ---
> net/ipv6/anycast.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c
> index 67a42e01dfc3..cd8c02a1ad4c 100644
> --- a/net/ipv6/anycast.c
> +++ b/net/ipv6/anycast.c
> @@ -371,10 +371,10 @@ int __ipv6_dev_ac_inc(struct inet6_dev *idev, const struct in6_addr *addr)
> aca->aca_next = idev->ac_list;
> rcu_assign_pointer(idev->ac_list, aca);
>
> - write_unlock_bh(&idev->lock);
> -
> ipv6_add_acaddr_hash(net, aca);
>
> + write_unlock_bh(&idev->lock);
> +
> ip6_ins_rt(net, f6i);
>
> addrconf_join_solict(idev->dev, &aca->aca_addr);
> --
> 2.43.0
>