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
reply other threads:[~2026-10-01 5:22 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
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 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®