diff options
author | Taehee Yoo <ap420073@gmail.com> | 2019-10-21 14:47:59 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-10-24 17:53:49 -0400 |
commit | 1962f86b42ed06ea6af9ff09390243b99d9eb83a (patch) | |
tree | e2889962a59cc29fcec292788ee6936c87723c51 /drivers/net/wireless/virt_wifi.c | |
parent | f3b0a18bb6cb07a9abb75e21b1f08eeaefa78e81 (diff) |
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: c7cdba31ed8b ("mac80211-next: rtnetlink wifi simulation device")
Signed-off-by: Taehee Yoo <ap420073@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/wireless/virt_wifi.c')
-rw-r--r-- | drivers/net/wireless/virt_wifi.c | 54 |
1 files changed, 52 insertions, 2 deletions
diff --git a/drivers/net/wireless/virt_wifi.c b/drivers/net/wireless/virt_wifi.c index be92e1220284..7997cc6de334 100644 --- a/drivers/net/wireless/virt_wifi.c +++ b/drivers/net/wireless/virt_wifi.c | |||
@@ -548,6 +548,7 @@ static int virt_wifi_newlink(struct net *src_net, struct net_device *dev, | |||
548 | priv->is_connected = false; | 548 | priv->is_connected = false; |
549 | priv->is_up = false; | 549 | priv->is_up = false; |
550 | INIT_DELAYED_WORK(&priv->connect, virt_wifi_connect_complete); | 550 | INIT_DELAYED_WORK(&priv->connect, virt_wifi_connect_complete); |
551 | __module_get(THIS_MODULE); | ||
551 | 552 | ||
552 | return 0; | 553 | return 0; |
553 | unregister_netdev: | 554 | unregister_netdev: |
@@ -578,6 +579,7 @@ static void virt_wifi_dellink(struct net_device *dev, | |||
578 | netdev_upper_dev_unlink(priv->lowerdev, dev); | 579 | netdev_upper_dev_unlink(priv->lowerdev, dev); |
579 | 580 | ||
580 | unregister_netdevice_queue(dev, head); | 581 | unregister_netdevice_queue(dev, head); |
582 | module_put(THIS_MODULE); | ||
581 | 583 | ||
582 | /* Deleting the wiphy is handled in the module destructor. */ | 584 | /* Deleting the wiphy is handled in the module destructor. */ |
583 | } | 585 | } |
@@ -590,6 +592,42 @@ static struct rtnl_link_ops virt_wifi_link_ops = { | |||
590 | .priv_size = sizeof(struct virt_wifi_netdev_priv), | 592 | .priv_size = sizeof(struct virt_wifi_netdev_priv), |
591 | }; | 593 | }; |
592 | 594 | ||
595 | static bool netif_is_virt_wifi_dev(const struct net_device *dev) | ||
596 | { | ||
597 | return rcu_access_pointer(dev->rx_handler) == virt_wifi_rx_handler; | ||
598 | } | ||
599 | |||
600 | static int virt_wifi_event(struct notifier_block *this, unsigned long event, | ||
601 | void *ptr) | ||
602 | { | ||
603 | struct net_device *lower_dev = netdev_notifier_info_to_dev(ptr); | ||
604 | struct virt_wifi_netdev_priv *priv; | ||
605 | struct net_device *upper_dev; | ||
606 | LIST_HEAD(list_kill); | ||
607 | |||
608 | if (!netif_is_virt_wifi_dev(lower_dev)) | ||
609 | return NOTIFY_DONE; | ||
610 | |||
611 | switch (event) { | ||
612 | case NETDEV_UNREGISTER: | ||
613 | priv = rtnl_dereference(lower_dev->rx_handler_data); | ||
614 | if (!priv) | ||
615 | return NOTIFY_DONE; | ||
616 | |||
617 | upper_dev = priv->upperdev; | ||
618 | |||
619 | upper_dev->rtnl_link_ops->dellink(upper_dev, &list_kill); | ||
620 | unregister_netdevice_many(&list_kill); | ||
621 | break; | ||
622 | } | ||
623 | |||
624 | return NOTIFY_DONE; | ||
625 | } | ||
626 | |||
627 | static struct notifier_block virt_wifi_notifier = { | ||
628 | .notifier_call = virt_wifi_event, | ||
629 | }; | ||
630 | |||
593 | /* Acquires and releases the rtnl lock. */ | 631 | /* Acquires and releases the rtnl lock. */ |
594 | static int __init virt_wifi_init_module(void) | 632 | static int __init virt_wifi_init_module(void) |
595 | { | 633 | { |
@@ -598,14 +636,25 @@ static int __init virt_wifi_init_module(void) | |||
598 | /* Guaranteed to be locallly-administered and not multicast. */ | 636 | /* Guaranteed to be locallly-administered and not multicast. */ |
599 | eth_random_addr(fake_router_bssid); | 637 | eth_random_addr(fake_router_bssid); |
600 | 638 | ||
639 | err = register_netdevice_notifier(&virt_wifi_notifier); | ||
640 | if (err) | ||
641 | return err; | ||
642 | |||
643 | err = -ENOMEM; | ||
601 | common_wiphy = virt_wifi_make_wiphy(); | 644 | common_wiphy = virt_wifi_make_wiphy(); |
602 | if (!common_wiphy) | 645 | if (!common_wiphy) |
603 | return -ENOMEM; | 646 | goto notifier; |
604 | 647 | ||
605 | err = rtnl_link_register(&virt_wifi_link_ops); | 648 | err = rtnl_link_register(&virt_wifi_link_ops); |
606 | if (err) | 649 | if (err) |
607 | virt_wifi_destroy_wiphy(common_wiphy); | 650 | goto destroy_wiphy; |
608 | 651 | ||
652 | return 0; | ||
653 | |||
654 | destroy_wiphy: | ||
655 | virt_wifi_destroy_wiphy(common_wiphy); | ||
656 | notifier: | ||
657 | unregister_netdevice_notifier(&virt_wifi_notifier); | ||
609 | return err; | 658 | return err; |
610 | } | 659 | } |
611 | 660 | ||
@@ -615,6 +664,7 @@ static void __exit virt_wifi_cleanup_module(void) | |||
615 | /* Will delete any devices that depend on the wiphy. */ | 664 | /* Will delete any devices that depend on the wiphy. */ |
616 | rtnl_link_unregister(&virt_wifi_link_ops); | 665 | rtnl_link_unregister(&virt_wifi_link_ops); |
617 | virt_wifi_destroy_wiphy(common_wiphy); | 666 | virt_wifi_destroy_wiphy(common_wiphy); |
667 | unregister_netdevice_notifier(&virt_wifi_notifier); | ||
618 | } | 668 | } |
619 | 669 | ||
620 | module_init(virt_wifi_init_module); | 670 | module_init(virt_wifi_init_module); |