virt_wifi: fix refcnt leak in module exit routine

virt_wifi_newlink() calls netdev_upper_dev_link() and it internally
holds reference count of lower interface.

Current code does not release a reference count of the lower interface
when the lower interface is being deleted.
So, reference count leaks occur.

Test commands:
    ip link add dummy0 type dummy
    ip link add vw1 link dummy0 type virt_wifi
    ip link del dummy0

Splat looks like:
[  133.787526][  T788] WARNING: CPU: 1 PID: 788 at net/core/dev.c:8274 rollback_registered_many+0x835/0xc80
[  133.788355][  T788] Modules linked in: virt_wifi cfg80211 dummy team af_packet sch_fq_codel ip_tables x_tables unix
[  133.789377][  T788] CPU: 1 PID: 788 Comm: ip Not tainted 5.4.0-rc3+ #96
[  133.790069][  T788] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[  133.791167][  T788] RIP: 0010:rollback_registered_many+0x835/0xc80
[  133.791906][  T788] Code: 00 4d 85 ff 0f 84 b5 fd ff ff ba c0 0c 00 00 48 89 de 4c 89 ff e8 9b 58 04 00 48 89 df e8 30
[  133.794317][  T788] RSP: 0018:ffff88805ba3f338 EFLAGS: 00010202
[  133.795080][  T788] RAX: ffff88805e57e801 RBX: ffff88805ba34000 RCX: ffffffffa9294723
[  133.796045][  T788] RDX: 1ffff1100b746816 RSI: 0000000000000008 RDI: ffffffffabcc4240
[  133.797006][  T788] RBP: ffff88805ba3f4c0 R08: fffffbfff5798849 R09: fffffbfff5798849
[  133.797993][  T788] R10: 0000000000000001 R11: fffffbfff5798848 R12: dffffc0000000000
[  133.802514][  T788] R13: ffff88805ba3f440 R14: ffff88805ba3f400 R15: ffff88805ed622c0
[  133.803237][  T788] FS:  00007f2e9608c0c0(0000) GS:ffff88806cc00000(0000) knlGS:0000000000000000
[  133.804002][  T788] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  133.804664][  T788] CR2: 00007f2e95610603 CR3: 000000005f68c004 CR4: 00000000000606e0
[  133.805363][  T788] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[  133.806073][  T788] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[  133.806787][  T788] Call Trace:
[  133.807069][  T788]  ? generic_xdp_install+0x310/0x310
[  133.807612][  T788]  ? lock_acquire+0x164/0x3b0
[  133.808077][  T788]  ? is_bpf_text_address+0x5/0xf0
[  133.808640][  T788]  ? deref_stack_reg+0x9c/0xd0
[  133.809138][  T788]  ? __nla_validate_parse+0x98/0x1ab0
[  133.809944][  T788]  unregister_netdevice_many.part.122+0x13/0x1b0
[  133.810599][  T788]  rtnl_delete_link+0xbc/0x100
[  133.811073][  T788]  ? rtnl_af_register+0xc0/0xc0
[  133.811672][  T788]  rtnl_dellink+0x30e/0x8a0
[  133.812205][  T788]  ? is_bpf_text_address+0x5/0xf0
[ ... ]

[  144.110530][  T788] unregister_netdevice: waiting for dummy0 to become free. Usage count = 1

This patch adds notifier routine to delete upper interface before deleting
lower interface.

Fixes: c7cdba31ed ("mac80211-next: rtnetlink wifi simulation device")
Signed-off-by: Taehee Yoo <ap420073@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Taehee Yoo 2019-10-21 18:47:59 +00:00 committed by David S. Miller
parent f3b0a18bb6
commit 1962f86b42
1 changed files with 52 additions and 2 deletions

View File

@ -548,6 +548,7 @@ static int virt_wifi_newlink(struct net *src_net, struct net_device *dev,
priv->is_connected = false;
priv->is_up = false;
INIT_DELAYED_WORK(&priv->connect, virt_wifi_connect_complete);
__module_get(THIS_MODULE);
return 0;
unregister_netdev:
@ -578,6 +579,7 @@ static void virt_wifi_dellink(struct net_device *dev,
netdev_upper_dev_unlink(priv->lowerdev, dev);
unregister_netdevice_queue(dev, head);
module_put(THIS_MODULE);
/* Deleting the wiphy is handled in the module destructor. */
}
@ -590,6 +592,42 @@ static struct rtnl_link_ops virt_wifi_link_ops = {
.priv_size = sizeof(struct virt_wifi_netdev_priv),
};
static bool netif_is_virt_wifi_dev(const struct net_device *dev)
{
return rcu_access_pointer(dev->rx_handler) == virt_wifi_rx_handler;
}
static int virt_wifi_event(struct notifier_block *this, unsigned long event,
void *ptr)
{
struct net_device *lower_dev = netdev_notifier_info_to_dev(ptr);
struct virt_wifi_netdev_priv *priv;
struct net_device *upper_dev;
LIST_HEAD(list_kill);
if (!netif_is_virt_wifi_dev(lower_dev))
return NOTIFY_DONE;
switch (event) {
case NETDEV_UNREGISTER:
priv = rtnl_dereference(lower_dev->rx_handler_data);
if (!priv)
return NOTIFY_DONE;
upper_dev = priv->upperdev;
upper_dev->rtnl_link_ops->dellink(upper_dev, &list_kill);
unregister_netdevice_many(&list_kill);
break;
}
return NOTIFY_DONE;
}
static struct notifier_block virt_wifi_notifier = {
.notifier_call = virt_wifi_event,
};
/* Acquires and releases the rtnl lock. */
static int __init virt_wifi_init_module(void)
{
@ -598,14 +636,25 @@ static int __init virt_wifi_init_module(void)
/* Guaranteed to be locallly-administered and not multicast. */
eth_random_addr(fake_router_bssid);
err = register_netdevice_notifier(&virt_wifi_notifier);
if (err)
return err;
err = -ENOMEM;
common_wiphy = virt_wifi_make_wiphy();
if (!common_wiphy)
return -ENOMEM;
goto notifier;
err = rtnl_link_register(&virt_wifi_link_ops);
if (err)
virt_wifi_destroy_wiphy(common_wiphy);
goto destroy_wiphy;
return 0;
destroy_wiphy:
virt_wifi_destroy_wiphy(common_wiphy);
notifier:
unregister_netdevice_notifier(&virt_wifi_notifier);
return err;
}
@ -615,6 +664,7 @@ static void __exit virt_wifi_cleanup_module(void)
/* Will delete any devices that depend on the wiphy. */
rtnl_link_unregister(&virt_wifi_link_ops);
virt_wifi_destroy_wiphy(common_wiphy);
unregister_netdevice_notifier(&virt_wifi_notifier);
}
module_init(virt_wifi_init_module);