From: Daehyeon Ko <4ncienth@gmail.com>
To: David Ahern <dsahern@kernel.org>, Ido Schimmel <idosch@nvidia.com>
Cc: "David S . Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@kernel.org>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Simon Horman <horms@kernel.org>,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
Daehyeon Ko <4ncienth@gmail.com>
Subject: [PATCH net] ipv6: serialize address publication with addrconf_ifdown
Date: Thu, 1 Oct 2026 14:21:50 +0900 [thread overview]
Message-ID: <20261001052150.136559-1-4ncienth@gmail.com> (raw)
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
next reply other threads:[~2026-10-01 5:22 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-01 5:21 Daehyeon Ko [this message]
2026-10-04 13:51 ` [PATCH net] ipv6: serialize address publication with addrconf_ifdown Ido Schimmel
2026-10-05 5:43 ` netdev-bot+sashiko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261001052150.136559-1-4ncienth@gmail.com \
--to=4ncienth@gmail.com \
--cc=davem@davemloft.net \
--cc=dsahern@kernel.org \
--cc=edumazet@kernel.org \
--cc=horms@kernel.org \
--cc=idosch@nvidia.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.