Skip to content

Commit c86872a

Browse files
vwaxgregkh
authored andcommitted
ipv6: Fix idev->addr_list corruption
[ Upstream commit a2d6cbb ] addrconf_ifdown() removes elements from the idev->addr_list without holding the idev->lock. If this happens while the loop in __ipv6_dev_get_saddr() is handling the same element, that function ends up in an infinite loop: NMI watchdog: BUG: soft lockup - CPU#1 stuck for 23s! [test:1719] Call Trace: ipv6_get_saddr_eval+0x13c/0x3a0 __ipv6_dev_get_saddr+0xe4/0x1f0 ipv6_dev_get_saddr+0x1b4/0x204 ip6_dst_lookup_tail+0xcc/0x27c ip6_dst_lookup_flow+0x38/0x80 udpv6_sendmsg+0x708/0xba8 sock_sendmsg+0x18/0x30 SyS_sendto+0xb8/0xf8 syscall_common+0x34/0x58 Fixes: 6a92393 (Revert "ipv6: Revert optional address flusing on ifdown.") Signed-off-by: Rabin Vincent <rabinv@axis.com> Acked-by: David Ahern <dsa@cumulusnetworks.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 479beb4 commit c86872a

1 file changed

Lines changed: 7 additions & 4 deletions

File tree

net/ipv6/addrconf.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3602,14 +3602,19 @@ static int addrconf_ifdown(struct net_device *dev, int how)
36023602
INIT_LIST_HEAD(&del_list);
36033603
list_for_each_entry_safe(ifa, tmp, &idev->addr_list, if_list) {
36043604
struct rt6_info *rt = NULL;
3605+
bool keep;
36053606

36063607
addrconf_del_dad_work(ifa);
36073608

3609+
keep = keep_addr && (ifa->flags & IFA_F_PERMANENT) &&
3610+
!addr_is_local(&ifa->addr);
3611+
if (!keep)
3612+
list_move(&ifa->if_list, &del_list);
3613+
36083614
write_unlock_bh(&idev->lock);
36093615
spin_lock_bh(&ifa->lock);
36103616

3611-
if (keep_addr && (ifa->flags & IFA_F_PERMANENT) &&
3612-
!addr_is_local(&ifa->addr)) {
3617+
if (keep) {
36133618
/* set state to skip the notifier below */
36143619
state = INET6_IFADDR_STATE_DEAD;
36153620
ifa->state = 0;
@@ -3621,8 +3626,6 @@ static int addrconf_ifdown(struct net_device *dev, int how)
36213626
} else {
36223627
state = ifa->state;
36233628
ifa->state = INET6_IFADDR_STATE_DEAD;
3624-
3625-
list_move(&ifa->if_list, &del_list);
36263629
}
36273630

36283631
spin_unlock_bh(&ifa->lock);

0 commit comments

Comments
 (0)