* [PATCH net] ipv6: addrconf: skip ERRDAD transition when address already DEAD
@ 2026-04-20 3:28 Linmao Li
2026-04-21 7:50 ` [PATCH net v2] " Linmao Li
0 siblings, 1 reply; 3+ messages in thread
From: Linmao Li @ 2026-04-20 3:28 UTC (permalink / raw)
To: davem, dsahern, edumazet, kuba, pabeni
Cc: horms, netdev, linux-kernel, Linmao Li
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).
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.
Signed-off-by: Linmao Li <lilinmao@kylinos.cn>
---
net/ipv6/addrconf.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 5476b6536eb7..14b1ab43da87 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2227,6 +2227,12 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp)
errdad:
/* transition from _POSTDAD to _ERRDAD */
+ if (ifp->state == INET6_IFADDR_STATE_DEAD) {
+ /* ipv6_del_addr() already removed ifp while lock was dropped */
+ spin_unlock_bh(&ifp->lock);
+ in6_ifa_put(ifp);
+ return;
+ }
ifp->state = INET6_IFADDR_STATE_ERRDAD;
spin_unlock_bh(&ifp->lock);
--
2.25.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH net v2] ipv6: addrconf: skip ERRDAD transition when address already DEAD
2026-04-20 3:28 [PATCH net] ipv6: addrconf: skip ERRDAD transition when address already DEAD Linmao Li
@ 2026-04-21 7:50 ` Linmao Li
2026-04-22 10:43 ` Sabrina Dubroca
0 siblings, 1 reply; 3+ messages in thread
From: Linmao Li @ 2026-04-21 7:50 UTC (permalink / raw)
To: davem, dsahern, edumazet, kuba, pabeni
Cc: horms, netdev, linux-kernel, Linmao Li
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).
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.
Fixes: c15b1ccadb32 ("ipv6: move DAD and addrconf_verify processing to workqueue")
Signed-off-by: Linmao Li <lilinmao@kylinos.cn>
---
net/ipv6/addrconf.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 5476b6536eb7..14b1ab43da87 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2227,6 +2227,12 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp)
errdad:
/* transition from _POSTDAD to _ERRDAD */
+ if (ifp->state == INET6_IFADDR_STATE_DEAD) {
+ /* ipv6_del_addr() already removed ifp while lock was dropped */
+ spin_unlock_bh(&ifp->lock);
+ in6_ifa_put(ifp);
+ return;
+ }
ifp->state = INET6_IFADDR_STATE_ERRDAD;
spin_unlock_bh(&ifp->lock);
--
2.25.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH net v2] ipv6: addrconf: skip ERRDAD transition when address already DEAD
2026-04-21 7:50 ` [PATCH net v2] " Linmao Li
@ 2026-04-22 10:43 ` Sabrina Dubroca
0 siblings, 0 replies; 3+ messages in thread
From: Sabrina Dubroca @ 2026-04-22 10:43 UTC (permalink / raw)
To: Linmao Li
Cc: davem, dsahern, edumazet, kuba, pabeni, horms, netdev,
linux-kernel
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
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-04-22 10:43 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-20 3:28 [PATCH net] ipv6: addrconf: skip ERRDAD transition when address already DEAD Linmao Li
2026-04-21 7:50 ` [PATCH net v2] " Linmao Li
2026-04-22 10:43 ` Sabrina Dubroca
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox