Skip to content

Commit 3e7d18b

Browse files
TaeheeYoodavem330
authored andcommitted
net: mld: fix reference count leak in mld_{query | report}_work()
mld_{query | report}_work() processes queued events. If there are too many events in the queue, it re-queue a work. And then, it returns without in6_dev_put(). But if queuing is failed, it should call in6_dev_put(), but it doesn't. So, a reference count leak would occur. THREAD0 THREAD1 mld_report_work() spin_lock_bh() if (!mod_delayed_work()) in6_dev_hold(); spin_unlock_bh() spin_lock_bh() schedule_delayed_work() spin_unlock_bh() Script to reproduce(by Hangbin Liu): ip netns add ns1 ip netns add ns2 ip netns exec ns1 sysctl -w net.ipv6.conf.all.force_mld_version=1 ip netns exec ns2 sysctl -w net.ipv6.conf.all.force_mld_version=1 ip -n ns1 link add veth0 type veth peer name veth0 netns ns2 ip -n ns1 link set veth0 up ip -n ns2 link set veth0 up for i in `seq 50`; do for j in `seq 100`; do ip -n ns1 addr add 2021:${i}::${j}/64 dev veth0 ip -n ns2 addr add 2022:${i}::${j}/64 dev veth0 done done modprobe -r veth ip -a netns del splat looks like: unregister_netdevice: waiting for veth0 to become free. Usage count = 2 leaked reference. ipv6_add_dev+0x324/0xec0 addrconf_notify+0x481/0xd10 raw_notifier_call_chain+0xe3/0x120 call_netdevice_notifiers+0x106/0x160 register_netdevice+0x114c/0x16b0 veth_newlink+0x48b/0xa50 [veth] rtnl_newlink+0x11a2/0x1a40 rtnetlink_rcv_msg+0x63f/0xc00 netlink_rcv_skb+0x1df/0x3e0 netlink_unicast+0x5de/0x850 netlink_sendmsg+0x6c9/0xa90 ____sys_sendmsg+0x76a/0x780 __sys_sendmsg+0x27c/0x340 do_syscall_64+0x43/0x90 entry_SYSCALL_64_after_hwframe+0x63/0xcd Tested-by: Hangbin Liu <liuhangbin@gmail.com> Fixes: f185de2 ("mld: add new workqueues for process mld events") Signed-off-by: Taehee Yoo <ap420073@gmail.com> Reviewed-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent c7b205f commit 3e7d18b

File tree

1 file changed

+8
-6
lines changed

1 file changed

+8
-6
lines changed

net/ipv6/mcast.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1522,7 +1522,6 @@ static void mld_query_work(struct work_struct *work)
15221522

15231523
if (++cnt >= MLD_MAX_QUEUE) {
15241524
rework = true;
1525-
schedule_delayed_work(&idev->mc_query_work, 0);
15261525
break;
15271526
}
15281527
}
@@ -1533,8 +1532,10 @@ static void mld_query_work(struct work_struct *work)
15331532
__mld_query_work(skb);
15341533
mutex_unlock(&idev->mc_lock);
15351534

1536-
if (!rework)
1537-
in6_dev_put(idev);
1535+
if (rework && queue_delayed_work(mld_wq, &idev->mc_query_work, 0))
1536+
return;
1537+
1538+
in6_dev_put(idev);
15381539
}
15391540

15401541
/* called with rcu_read_lock() */
@@ -1624,7 +1625,6 @@ static void mld_report_work(struct work_struct *work)
16241625

16251626
if (++cnt >= MLD_MAX_QUEUE) {
16261627
rework = true;
1627-
schedule_delayed_work(&idev->mc_report_work, 0);
16281628
break;
16291629
}
16301630
}
@@ -1635,8 +1635,10 @@ static void mld_report_work(struct work_struct *work)
16351635
__mld_report_work(skb);
16361636
mutex_unlock(&idev->mc_lock);
16371637

1638-
if (!rework)
1639-
in6_dev_put(idev);
1638+
if (rework && queue_delayed_work(mld_wq, &idev->mc_report_work, 0))
1639+
return;
1640+
1641+
in6_dev_put(idev);
16401642
}
16411643

16421644
static bool is_in(struct ifmcaddr6 *pmc, struct ip6_sf_list *psf, int type,

0 commit comments

Comments
 (0)