Skip to content

Commit 65161fb

Browse files
q2venkuba-moo
authored andcommitted
net: Fix dev_net(dev) race in unregister_netdevice_notifier_dev_net().
After the cited commit, dev_net(dev) is fetched before holding RTNL and passed to __unregister_netdevice_notifier_net(). However, dev_net(dev) might be different after holding RTNL. In the reported case [0], while removing a VF device, its netns was being dismantled and the VF was moved to init_net. So the following sequence is basically illegal when dev was fetched without lookup: net = dev_net(dev); rtnl_net_lock(net); Let's use a new helper rtnl_net_dev_lock() to fix the race. It fetches dev_net_rcu(dev), bumps its net->passive, and checks if dev_net_rcu(dev) is changed after rtnl_net_lock(). [0]: BUG: KASAN: slab-use-after-free in notifier_call_chain (kernel/notifier.c:75 (discriminator 2)) Read of size 8 at addr ffff88810cefb4c8 by task test-bridge-lag/21127 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.13.0-0-gf21b5a4aeb02-prebuilt.qemu.org 04/01/2014 Call Trace: <TASK> dump_stack_lvl (lib/dump_stack.c:123) print_report (mm/kasan/report.c:379 mm/kasan/report.c:489) kasan_report (mm/kasan/report.c:604) notifier_call_chain (kernel/notifier.c:75 (discriminator 2)) call_netdevice_notifiers_info (net/core/dev.c:2011) unregister_netdevice_many_notify (net/core/dev.c:11551) unregister_netdevice_queue (net/core/dev.c:11487) unregister_netdev (net/core/dev.c:11635) mlx5e_remove (drivers/net/ethernet/mellanox/mlx5/core/en_main.c:6552 drivers/net/ethernet/mellanox/mlx5/core/en_main.c:6579) mlx5_core auxiliary_bus_remove (drivers/base/auxiliary.c:230) device_release_driver_internal (drivers/base/dd.c:1275 drivers/base/dd.c:1296) bus_remove_device (./include/linux/kobject.h:193 drivers/base/base.h:73 drivers/base/bus.c:583) device_del (drivers/base/power/power.h:142 drivers/base/core.c:3855) mlx5_rescan_drivers_locked (./include/linux/auxiliary_bus.h:241 drivers/net/ethernet/mellanox/mlx5/core/dev.c:333 drivers/net/ethernet/mellanox/mlx5/core/dev.c:535 drivers/net/ethernet/mellanox/mlx5/core/dev.c:549) mlx5_core mlx5_unregister_device (drivers/net/ethernet/mellanox/mlx5/core/dev.c:468) mlx5_core mlx5_uninit_one (./include/linux/instrumented.h:68 ./include/asm-generic/bitops/instrumented-non-atomic.h:141 drivers/net/ethernet/mellanox/mlx5/core/main.c:1563) mlx5_core remove_one (drivers/net/ethernet/mellanox/mlx5/core/main.c:965 drivers/net/ethernet/mellanox/mlx5/core/main.c:2019) mlx5_core pci_device_remove (./include/linux/pm_runtime.h:129 drivers/pci/pci-driver.c:475) device_release_driver_internal (drivers/base/dd.c:1275 drivers/base/dd.c:1296) unbind_store (drivers/base/bus.c:245) kernfs_fop_write_iter (fs/kernfs/file.c:338) vfs_write (fs/read_write.c:587 (discriminator 1) fs/read_write.c:679 (discriminator 1)) ksys_write (fs/read_write.c:732) do_syscall_64 (arch/x86/entry/common.c:52 (discriminator 1) arch/x86/entry/common.c:83 (discriminator 1)) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:130) RIP: 0033:0x7f6a4d5018b7 Fixes: 7fb1073 ("net: Hold rtnl_net_lock() in (un)?register_netdevice_notifier_dev_net().") Reported-by: Yael Chemla <[email protected]> Closes: https://lore.kernel.org/netdev/[email protected]/ Signed-off-by: Kuniyuki Iwashima <[email protected]> Reviewed-by: Eric Dumazet <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent e57a632 commit 65161fb

File tree

1 file changed

+44
-4
lines changed

1 file changed

+44
-4
lines changed

net/core/dev.c

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2070,13 +2070,54 @@ static void __move_netdevice_notifier_net(struct net *src_net,
20702070
__register_netdevice_notifier_net(dst_net, nb, true);
20712071
}
20722072

2073+
static void rtnl_net_dev_lock(struct net_device *dev)
2074+
{
2075+
bool again;
2076+
2077+
do {
2078+
struct net *net;
2079+
2080+
again = false;
2081+
2082+
/* netns might be being dismantled. */
2083+
rcu_read_lock();
2084+
net = dev_net_rcu(dev);
2085+
net_passive_inc(net);
2086+
rcu_read_unlock();
2087+
2088+
rtnl_net_lock(net);
2089+
2090+
#ifdef CONFIG_NET_NS
2091+
/* dev might have been moved to another netns. */
2092+
if (!net_eq(net, rcu_access_pointer(dev->nd_net.net))) {
2093+
rtnl_net_unlock(net);
2094+
net_passive_dec(net);
2095+
again = true;
2096+
}
2097+
#endif
2098+
} while (again);
2099+
}
2100+
2101+
static void rtnl_net_dev_unlock(struct net_device *dev)
2102+
{
2103+
struct net *net = dev_net(dev);
2104+
2105+
rtnl_net_unlock(net);
2106+
net_passive_dec(net);
2107+
}
2108+
20732109
int register_netdevice_notifier_dev_net(struct net_device *dev,
20742110
struct notifier_block *nb,
20752111
struct netdev_net_notifier *nn)
20762112
{
20772113
struct net *net = dev_net(dev);
20782114
int err;
20792115

2116+
/* rtnl_net_lock() assumes dev is not yet published by
2117+
* register_netdevice().
2118+
*/
2119+
DEBUG_NET_WARN_ON_ONCE(!list_empty(&dev->dev_list));
2120+
20802121
rtnl_net_lock(net);
20812122
err = __register_netdevice_notifier_net(net, nb, false);
20822123
if (!err) {
@@ -2093,13 +2134,12 @@ int unregister_netdevice_notifier_dev_net(struct net_device *dev,
20932134
struct notifier_block *nb,
20942135
struct netdev_net_notifier *nn)
20952136
{
2096-
struct net *net = dev_net(dev);
20972137
int err;
20982138

2099-
rtnl_net_lock(net);
2139+
rtnl_net_dev_lock(dev);
21002140
list_del(&nn->list);
2101-
err = __unregister_netdevice_notifier_net(net, nb);
2102-
rtnl_net_unlock(net);
2141+
err = __unregister_netdevice_notifier_net(dev_net(dev), nb);
2142+
rtnl_net_dev_unlock(dev);
21032143

21042144
return err;
21052145
}

0 commit comments

Comments
 (0)