Subject: Re: [PATCH net v2] ipv6: addrconf: skip ERRDAD transition when address already DEAD

From: Sabrina Dubroca <[email-masked]>
Date: Wed, 22 Apr 2026 12:43:32 +0200

2026-04-21, 15:50:33 +0800, Linmao Li wrote:
> addrconf_dad_end() transitions ifp->state from DAD to POSTDAD under
> ifp->lock and releases the lock.  addrconf_dad_failure() takes
> ifp->lock again with the spin_lock_bh() following the
> net_info_ratelimited() duplicate-address log.  A concurrent
> ipv6_del_addr() can acquire the lock in that window, set ifp->state
> to DEAD and run list_del_rcu(&ifp->if_list).

You're pretty much saying that the ifp->state check we did in
addrconf_dad_end before dropping the lock is not valid, so it seems we
should just skip that separate check since it's not doing anything
useful, and move it under the "main" lock we acquire after the
net_info_ratelimited(). There would still be a problem with "we
dropped the lock in the STABLE_PRIVACY block", which your patch
handles.

> addrconf_dad_failure() then overwrites DEAD with ERRDAD at errdad:
> and schedules a new dad_work.  The work calls ipv6_del_addr() again,
> hitting the already-poisoned list entry:
> 
>   general protection fault: 0000 [#1] SMP NOPTI
>   CPU: 4 PID: 217 Comm: kworker/4:1
>   Workqueue: ipv6_addrconf addrconf_dad_work
>   RIP: 0010:ipv6_del_addr+0xe9/0x280
>   RAX: dead000000000122
>   Call Trace:
>    addrconf_dad_stop+0x113/0x140
>    addrconf_dad_work+0x28c/0x430
>    process_one_work+0x1eb/0x3b0
>    worker_thread+0x4d/0x400
>    kthread+0x104/0x140
>    ret_from_fork+0x35/0x40
> 
> Bail out at errdad: when ifp->state is already DEAD. The existing
> in6_ifa_put() releases the reference taken for this invocation.

Mentioning "the existing in6_ifa_put()" is a bit confusing since
you're adding a separate unlock/put/return path.

-- 
Sabrina