Netdev List
 help / color / mirror / Atom feed
* [PATCH net v4 0/4] ipv6: fix address publication races with addrconf_ifdown
@ 2026-10-15  5:06 Daehyeon Ko
  2026-10-15  5:06 ` [PATCH net v4 1/4] ipv6: remove ifaddr from hash during ifdown list cleanup Daehyeon Ko
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Daehyeon Ko @ 2026-10-15  5:06 UTC (permalink / raw)
  To: David Ahern, Ido Schimmel
  Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Hannes Frederic Sowa, netdev, linux-kernel,
	Daehyeon Ko

The first patch removes each non-kept ifaddr from the address hash during
the per-device list cleanup. This prevents addrconf_ifdown() from freeing
a captured object that a late publisher left linked in the hash.

The second patch serializes hash and device-list publication with dead
device teardown. The cleanup patch is now first, so the newly atomic
publisher cannot create a captured-but-still-hashed intermediate state.

The third patch initializes a temporary address's public-ifaddr reference
and both timestamps before publication. No lifetime update can observe a
partially initialized relationship.

The fourth patch prevents stable-privacy DAD recovery from changing a
DEAD replacement address back to PREDAD and queueing work after teardown
has cleared its route.

The original deterministic test used a direct internal caller, kprobes and
atomic rendezvous at existing instruction boundaries. A real RA separately
reached ipv6_add_addr() with can_block=false. No new kernel build or
runtime test was run for v4. Validation used source review, strict
checkpatch and multi-tree apply tests.

Changes in v4:
- Reorder the first two patches so the hash cleanup precedes publication
  serialization; retain all existing patch subjects.
- Describe the freed hash entry as a use-after-free and document the mutual
  backport dependency in patches 1 and 2.
- Keep patch 2 scoped to idev->dead; drop the preliminary READ_ONCE()
  conversions and the unrelated disable_ipv6 recheck.
- State which finding was triggered and which follow-ons came from source
  review, including the observed device-teardown symptom.
- Initialize ifpub, cstamp and tstamp together in patch 3.
- Add patch 4 for the stable-privacy DAD recovery race.

Link: https://lore.kernel.org/r/20261009054042.272944-1-4ncienth@gmail.com
Link: https://lore.kernel.org/r/179161099408.434549.14449036924552933828@kernel.org
Link: https://lore.kernel.org/r/179161099539.434549.2238120015372268173@kernel.org
Link: https://lore.kernel.org/r/179161099665.434549.5858601179492551889@kernel.org
Link: https://lore.kernel.org/r/20261012145306.GB779501@shredder
Link: https://lore.kernel.org/r/20261012151316.GA798355@shredder
Link: https://lore.kernel.org/r/20261012145240.GA779501@shredder
Link: https://lore.kernel.org/r/179152464915.2527009.3889155025831898857@kernel.org

Daehyeon Ko (4):
  ipv6: remove ifaddr from hash during ifdown list cleanup
  ipv6: serialize address publication with device teardown
  ipv6: initialize temporary ifaddr before publication
  ipv6: do not restart DAD for a dead ifaddr

 include/net/addrconf.h |  3 +++
 net/ipv6/addrconf.c    | 46 +++++++++++++++++++++++++++++++++-------------
 2 files changed, 36 insertions(+), 13 deletions(-)


base-commit: 513444cc62e231a11c9b15ea88de0890da9b2b4f
-- 
2.55.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH net v4 1/4] ipv6: remove ifaddr from hash during ifdown list cleanup
  2026-10-15  5:06 [PATCH net v4 0/4] ipv6: fix address publication races with addrconf_ifdown Daehyeon Ko
@ 2026-10-15  5:06 ` Daehyeon Ko
  2026-10-15  5:06 ` [PATCH net v4 2/4] ipv6: serialize address publication with device teardown Daehyeon Ko
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Daehyeon Ko @ 2026-10-15  5:06 UTC (permalink / raw)
  To: David Ahern, Ido Schimmel
  Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Hannes Frederic Sowa, netdev, linux-kernel,
	Daehyeon Ko

addrconf_ifdown() clears the address hash before snapshotting the
per-device address list. A concurrent ipv6_add_addr() can publish an
address after the hash scan and before the list snapshot.

The ifdown path then removes the address from the device list and drops
the list-owned reference while it is still linked in the hash. Once the
remaining references are released, inet6_ifa_finish_destroy() warns, but
a DEAD address continues to kfree_rcu(). The hash therefore points to
freed memory that later RCU walkers can dereference.

This follow-on was found by source review and was not separately
triggered at runtime.

Remove each non-kept address from the hash immediately before removing
it from the per-device list. Keeping it hashed through the delete
notification blocks same-address publication until NETDEV_DOWN has been
delivered. hlist_del_init_rcu() is safe when the earlier hash scan
already removed the address.

This handles a late publisher captured by the list snapshot. The next
change serializes publication with teardown so an add cannot miss both
observations. Both changes are required when backporting the complete fix.

Fixes: 73a8bd74e261 ("ipv6: Revert 'administrative down' address handling changes.")
Cc: stable@vger.kernel.org
Reported-by: Ido Schimmel <idosch@nvidia.com>
Link: https://lore.kernel.org/r/20261004135117.GA206930@shredder
Link: https://lore.kernel.org/r/179161099539.434549.2238120015372268173@kernel.org
Suggested-by: Ido Schimmel <idosch@nvidia.com>
Assisted-by: LLM
Signed-off-by: Daehyeon Ko <4ncienth@gmail.com>
---
 net/ipv6/addrconf.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index c90ee6dd7446cd..72d7df23f79674 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -4019,6 +4019,10 @@ static int addrconf_ifdown(struct net_device *dev, bool unregister)
 		}
 
 		if (!keep) {
+			spin_lock_bh(&net->ipv6.addrconf_hash_lock);
+			hlist_del_init_rcu(&ifa->addr_lst);
+			spin_unlock_bh(&net->ipv6.addrconf_hash_lock);
+
 			write_lock_bh(&idev->lock);
 			list_del_rcu(&ifa->if_list);
 			write_unlock_bh(&idev->lock);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH net v4 2/4] ipv6: serialize address publication with device teardown
  2026-10-15  5:06 [PATCH net v4 0/4] ipv6: fix address publication races with addrconf_ifdown Daehyeon Ko
  2026-10-15  5:06 ` [PATCH net v4 1/4] ipv6: remove ifaddr from hash during ifdown list cleanup Daehyeon Ko
@ 2026-10-15  5:06 ` Daehyeon Ko
  2026-10-15  5:06 ` [PATCH net v4 3/4] ipv6: initialize temporary ifaddr before publication Daehyeon Ko
  2026-10-15  5:06 ` [PATCH net v4 4/4] ipv6: do not restart DAD for a dead ifaddr Daehyeon Ko
  3 siblings, 0 replies; 5+ messages in thread
From: Daehyeon Ko @ 2026-10-15  5:06 UTC (permalink / raw)
  To: David Ahern, Ido Schimmel
  Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Hannes Frederic Sowa, netdev, linux-kernel,
	Daehyeon Ko

ipv6_add_addr() checks idev state before allocating an ifaddr, but
publishes the object later. addrconf_ifdown() can mark and detach the
idev between the check and publication.

This happens when a non-loopback device MTU falls below IPV6_MIN_MTU. A
forced interleaving published an address on a dead idev, and
ipv6_get_ifaddr() still returned it after teardown. Later device deletion
remained blocked with:

  unregister_netdevice: waiting for ac0 to become free. Usage count = 3

Protect the dead indication with idev->lock and keep that lock across
both hash and device-list publication. Recheck dead before publishing;
if teardown wins the lock, reject the unpublished object.

Non-unregister teardown does not set dead. The preceding change covers an
add that misses the initial hash scan but reaches the per-device list
snapshot. Both changes are required when backporting this fix.

Fixes: 8814c4b53381 ("[IPV6] ADDRCONF: Convert addrconf_lock to RCU.")
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/179161099408.434549.14449036924552933828@kernel.org
Link: https://lore.kernel.org/r/20261012145306.GB779501@shredder
Suggested-by: Ido Schimmel <idosch@nvidia.com>
Assisted-by: LLM
Signed-off-by: Daehyeon Ko <4ncienth@gmail.com>
---
 net/ipv6/addrconf.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 72d7df23f79674..3bede30d4e989b 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1168,14 +1168,18 @@ ipv6_add_addr(struct inet6_dev *idev, struct ifa6_config *cfg,
 
 	rcu_read_lock();
 
-	err = ipv6_add_addr_hash(idev->dev, ifa);
+	write_lock_bh(&idev->lock);
+
+	if (idev->dead)
+		err = -ENODEV;
+	else
+		err = ipv6_add_addr_hash(idev->dev, ifa);
 	if (err < 0) {
+		write_unlock_bh(&idev->lock);
 		rcu_read_unlock();
 		goto out;
 	}
 
-	write_lock_bh(&idev->lock);
-
 	/* Add to inet6_dev unicast addr list. */
 	ipv6_link_dev_addr(idev, ifa);
 
@@ -3897,7 +3901,9 @@ static int addrconf_ifdown(struct net_device *dev, bool unregister)
 	 *	   Do not dev_put!
 	 */
 	if (unregister) {
+		write_lock_bh(&idev->lock);
 		WRITE_ONCE(idev->dead, 1);
+		write_unlock_bh(&idev->lock);
 
 		/* protected by rtnl_lock */
 		RCU_INIT_POINTER(dev->ip6_ptr, NULL);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH net v4 3/4] ipv6: initialize temporary ifaddr before publication
  2026-10-15  5:06 [PATCH net v4 0/4] ipv6: fix address publication races with addrconf_ifdown Daehyeon Ko
  2026-10-15  5:06 ` [PATCH net v4 1/4] ipv6: remove ifaddr from hash during ifdown list cleanup Daehyeon Ko
  2026-10-15  5:06 ` [PATCH net v4 2/4] ipv6: serialize address publication with device teardown Daehyeon Ko
@ 2026-10-15  5:06 ` Daehyeon Ko
  2026-10-15  5:06 ` [PATCH net v4 4/4] ipv6: do not restart DAD for a dead ifaddr Daehyeon Ko
  3 siblings, 0 replies; 5+ messages in thread
From: Daehyeon Ko @ 2026-10-15  5:06 UTC (permalink / raw)
  To: David Ahern, Ido Schimmel
  Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Hannes Frederic Sowa, netdev, linux-kernel,
	Daehyeon Ko

ipv6_create_tempaddr() holds a reference to the public ifaddr, publishes
the new temporary address through ipv6_add_addr(), and only then stores
the reference and final timestamps.

addrconf_ifdown() can remove the temporary address before ifpub is
stored, leaking the public ifaddr reference. Publishing ifpub alone is
insufficient: manage_tempaddrs() can then find the object before cstamp
and tstamp are final, update its lifetimes, and have the constructor
overwrite its timestamp with an older value.

This follow-on was found by source review and was not separately
triggered at runtime.

Pass the public ifaddr and both timestamps in ifa6_config. Initialize all
three fields before adding the temporary address to either the hash or
per-device lists. On success the existing public-ifaddr reference
transfers to the temporary object. On error it remains owned and released
by ipv6_create_tempaddr().

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <netdev-bot+sashiko@kernel.org>
Closes: https://lore.kernel.org/r/179122559913.434549.12720841717630168470@kernel.org
Link: https://lore.kernel.org/r/179161099665.434549.5858601179492551889@kernel.org
Link: https://lore.kernel.org/r/20261007164548.GA1153540@shredder
Link: https://lore.kernel.org/r/20261012145240.GA779501@shredder
Suggested-by: Ido Schimmel <idosch@nvidia.com>
Assisted-by: LLM
Signed-off-by: Daehyeon Ko <4ncienth@gmail.com>
---
 include/net/addrconf.h |  3 +++
 net/ipv6/addrconf.c    | 20 +++++++++++++-------
 2 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index e6764245995f25..6818057cf72750 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -81,6 +81,9 @@ struct ifa6_config {
 	u8			ifa_proto;
 
 	const struct in6_addr	*peer_pfx;
+	struct inet6_ifaddr	*ifpub;
+	unsigned long		cstamp;
+	unsigned long		tstamp;
 
 	u32			rt_priority;
 	u32			ifa_flags;
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 3bede30d4e989b..df566681226195 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1155,10 +1155,19 @@ ipv6_add_addr(struct inet6_dev *idev, struct ifa6_config *cfg,
 		ifa->flags |= IFA_F_TENTATIVE;
 	ifa->valid_lft = cfg->valid_lft;
 	ifa->prefered_lft = cfg->preferred_lft;
-	ifa->cstamp = ifa->tstamp = jiffies;
 	ifa->tokenized = false;
 
 	ifa->rt = f6i;
+	ifa->ifpub = cfg->ifpub;
+	if (cfg->ifpub) {
+		ifa->cstamp = cfg->cstamp;
+		ifa->tstamp = cfg->tstamp;
+	} else {
+		unsigned long now = jiffies;
+
+		ifa->cstamp = now;
+		ifa->tstamp = now;
+	}
 
 	ifa->idev = idev;
 	in6_dev_hold(idev);
@@ -1491,6 +1500,9 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block)
 
 	cfg.pfx = &addr;
 	cfg.scope = ipv6_addr_scope(cfg.pfx);
+	cfg.ifpub = ifp;
+	cfg.cstamp = now;
+	cfg.tstamp = tmp_tstamp;
 
 	ift = ipv6_add_addr(idev, &cfg, block, NULL);
 	if (IS_ERR(ift)) {
@@ -1501,12 +1513,6 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, bool block)
 		goto retry;
 	}
 
-	spin_lock_bh(&ift->lock);
-	ift->ifpub = ifp;
-	ift->cstamp = now;
-	ift->tstamp = tmp_tstamp;
-	spin_unlock_bh(&ift->lock);
-
 	addrconf_dad_start(ift);
 	in6_ifa_put(ift);
 	in6_dev_put(idev);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH net v4 4/4] ipv6: do not restart DAD for a dead ifaddr
  2026-10-15  5:06 [PATCH net v4 0/4] ipv6: fix address publication races with addrconf_ifdown Daehyeon Ko
                   ` (2 preceding siblings ...)
  2026-10-15  5:06 ` [PATCH net v4 3/4] ipv6: initialize temporary ifaddr before publication Daehyeon Ko
@ 2026-10-15  5:06 ` Daehyeon Ko
  3 siblings, 0 replies; 5+ messages in thread
From: Daehyeon Ko @ 2026-10-15  5:06 UTC (permalink / raw)
  To: David Ahern, Ido Schimmel
  Cc: David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Hannes Frederic Sowa, netdev, linux-kernel,
	Daehyeon Ko

addrconf_dad_failure() can create a stable-privacy replacement without
RTNL. After ipv6_add_addr() publishes it, addrconf_ifdown() can mark the
new ifaddr DEAD, clear its route, and unlink it before the recovery path
takes ifp2->lock.

The recovery path then unconditionally changes the state back to PREDAD
and queues delayed DAD work. The MTU teardown path can leave IF_READY
set. For an optimistic address, addrconf_dad_begin() can consequently
pass the cleared NULL route to ip6_ins_rt().

This follow-on was found by source review and was not separately
triggered at runtime.

The new ifaddr is already initialized in PREDAD. Under ifp2->lock, record
the retry and queue DAD only if teardown has not changed the state to
DEAD. If teardown wins after this check, the work observes DEAD and exits
without reviving the address.

This complements the existing lock_errdad check that preserves the original
ifaddr's DEAD state after the same function reacquires its lock.

Fixes: 5f40ef77adb2 ("ipv6: do retries on stable privacy addresses")
Cc: stable@vger.kernel.org
Reported-by: Sashiko <netdev-bot+sashiko@kernel.org>
Closes: https://lore.kernel.org/r/179161099408.434549.14449036924552933828@kernel.org
Link: https://lore.kernel.org/r/20260513025509.3776405-1-lilinmao@kylinos.cn
Assisted-by: LLM
Signed-off-by: Daehyeon Ko <4ncienth@gmail.com>
---
 net/ipv6/addrconf.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index df566681226195..a2d3fb187f7684 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2198,6 +2198,7 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp)
 			     ifp->idev->dev->name, &ifp->addr, eth_hdr(skb)->h_source);
 
 	if (ifp->flags & IFA_F_STABLE_PRIVACY) {
+		bool start_dad = false;
 		struct in6_addr new_addr;
 		struct inet6_ifaddr *ifp2;
 		int retries = ifp->stable_privacy_retry + 1;
@@ -2236,11 +2237,14 @@ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp)
 			goto lock_errdad;
 
 		spin_lock_bh(&ifp2->lock);
-		ifp2->stable_privacy_retry = retries;
-		ifp2->state = INET6_IFADDR_STATE_PREDAD;
+		if (ifp2->state != INET6_IFADDR_STATE_DEAD) {
+			ifp2->stable_privacy_retry = retries;
+			start_dad = true;
+		}
 		spin_unlock_bh(&ifp2->lock);
 
-		addrconf_mod_dad_work(ifp2, net->ipv6.sysctl.idgen_delay);
+		if (start_dad)
+			addrconf_mod_dad_work(ifp2, net->ipv6.sysctl.idgen_delay);
 		in6_ifa_put(ifp2);
 lock_errdad:
 		spin_lock_bh(&ifp->lock);
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-10-15  5:07 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-15  5:06 [PATCH net v4 0/4] ipv6: fix address publication races with addrconf_ifdown Daehyeon Ko
2026-10-15  5:06 ` [PATCH net v4 1/4] ipv6: remove ifaddr from hash during ifdown list cleanup Daehyeon Ko
2026-10-15  5:06 ` [PATCH net v4 2/4] ipv6: serialize address publication with device teardown Daehyeon Ko
2026-10-15  5:06 ` [PATCH net v4 3/4] ipv6: initialize temporary ifaddr before publication Daehyeon Ko
2026-10-15  5:06 ` [PATCH net v4 4/4] ipv6: do not restart DAD for a dead ifaddr Daehyeon Ko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox