* [PATCH net] ipv6: serialize address publication with addrconf_ifdown
@ 2026-10-01 5:21 Daehyeon Ko
0 siblings, 0 replies; only message in thread
From: Daehyeon Ko @ 2026-10-01 5:21 UTC (permalink / raw)
To: David Ahern, Ido Schimmel
Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, netdev, linux-kernel, Daehyeon Ko
ipv6_add_addr() inserts an inet6_ifaddr into the per-net hash before
linking it into idev->addr_list. addrconf_ifdown() clears the hash first
and snapshots the device list later. A nonblocking add can therefore
enter between the two teardown observations.
When a non-loopback device's MTU falls below IPV6_MIN_MTU, teardown
marks and detaches the idev. A late add can then link the ifaddr into the
dead idev. On v7.2, a deterministic interleaving left the object
hash-visible with idev->dead=1. ipv6_get_ifaddr() returned it, and later
device deletion waited indefinitely with usage count 3. During network
namespace exit that wait can stall the single-thread netns cleanup
workqueue.
MTU changes require CAP_NET_ADMIN in the affected network namespace.
Where unprivileged user namespaces are permitted, a local user can
obtain that capability in a new user and network namespace.
Publish the hash and device-list memberships while holding
addrconf_hash_lock followed by idev->lock. Recheck idev->dead and
disable_ipv6 before either publication. If teardown wins, the add fails
before publishing the object or taking a list reference.
The same forced interleaving now returns -ENODEV and device deletion
completes without a KASAN or LOCKDEP diagnostic. A real Router
Advertisement separately reached ipv6_add_addr() with can_block=false;
bounded natural stress did not reproduce the full race. A reproducer is
available on request.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Daehyeon Ko <4ncienth@gmail.com>
---
net/ipv6/addrconf.c | 37 ++++++++++++++++++++-----------------
1 file changed, 20 insertions(+), 17 deletions(-)
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index c90ee6dd7446c..899e9a47f7585 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1047,8 +1047,9 @@ static bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr,
return false;
}
-static int ipv6_add_addr_hash(struct net_device *dev, struct inet6_ifaddr *ifa)
+static int ipv6_add_addr_hash(struct inet6_dev *idev, struct inet6_ifaddr *ifa)
{
+ struct net_device *dev = idev->dev;
struct net *net = dev_net(dev);
unsigned int hash = inet6_addr_hash(net, &ifa->addr);
int err = 0;
@@ -1060,7 +1061,23 @@ static int ipv6_add_addr_hash(struct net_device *dev, struct inet6_ifaddr *ifa)
netdev_dbg(dev, "ipv6_add_addr: already assigned\n");
err = -EEXIST;
} else {
- hlist_add_head_rcu(&ifa->addr_lst, &net->ipv6.inet6_addr_lst[hash]);
+ write_lock(&idev->lock);
+ if (idev->dead || idev->cnf.disable_ipv6) {
+ err = idev->dead ? -ENODEV : -EACCES;
+ } else {
+ hlist_add_head_rcu(&ifa->addr_lst,
+ &net->ipv6.inet6_addr_lst[hash]);
+ ipv6_link_dev_addr(idev, ifa);
+
+ if (ifa->flags & IFA_F_TEMPORARY) {
+ /* manage_tempaddrs() relies on addresses being added to the head */
+ list_add(&ifa->tmp_list, &idev->tempaddr_list);
+ in6_ifa_hold(ifa);
+ }
+
+ in6_ifa_hold(ifa);
+ }
+ write_unlock(&idev->lock);
}
spin_unlock_bh(&net->ipv6.addrconf_hash_lock);
@@ -1168,26 +1185,12 @@ ipv6_add_addr(struct inet6_dev *idev, struct ifa6_config *cfg,
rcu_read_lock();
- err = ipv6_add_addr_hash(idev->dev, ifa);
+ err = ipv6_add_addr_hash(idev, ifa);
if (err < 0) {
rcu_read_unlock();
goto out;
}
- write_lock_bh(&idev->lock);
-
- /* Add to inet6_dev unicast addr list. */
- ipv6_link_dev_addr(idev, ifa);
-
- if (ifa->flags&IFA_F_TEMPORARY) {
- /* manage_tempaddrs() relies on addresses being added to the head */
- list_add(&ifa->tmp_list, &idev->tempaddr_list);
- in6_ifa_hold(ifa);
- }
-
- in6_ifa_hold(ifa);
- write_unlock_bh(&idev->lock);
-
rcu_read_unlock();
inet6addr_notifier_call_chain(NETDEV_UP, ifa);
base-commit: 7375d38364a9aa66fb31716bcefef38aecad75d8
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-10-01 5:22 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-01 5:21 [PATCH net] ipv6: serialize address publication with addrconf_ifdown Daehyeon Ko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®