mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
ipv6: serialize address publication with device teardown
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: 8814c4b533 ("[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>
Signed-off-by: Daehyeon Ko <4ncienth@gmail.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Link: https://patch.msgid.link/20261015050651.1841557-3-4ncienth@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
6ace56ed7b
commit
06227034e2
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user