aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorPavel Emelyanov <xemul@openvz.org>2008-05-08 04:24:25 -0400
committerDavid S. Miller <davem@davemloft.net>2008-05-08 04:24:25 -0400
commitaca51397d01474f80cab8fc978559b45f2e453ad (patch)
tree767ec47cfb981775294301d33208c0d2b52a01a3 /net
parentf3261aff35cbc811fee0e23eaea277f1b7286eca (diff)
netns: Fix arbitrary net_device-s corruptions on net_ns stop.
When a net namespace is destroyed, some devices (those, not killed on ns stop explicitly) are moved back to init_net. The problem, is that this net_ns change has one point of failure - the __dev_alloc_name() may be called if a name collision occurs (and this is easy to trigger). This allocator performs a likely-to-fail GFP_ATOMIC allocation to find a suitable number. Other possible conditions that may cause error (for device being ns local or not registered) are always false in this case. So, when this call fails, the device is unregistered. But this is *not* the right thing to do, since after this the device may be released (and kfree-ed) improperly. E. g. bridges require more actions (sysfs update, timer disarming, etc.), some other devices want to remove their private areas from lists, etc. I. e. arbitrary use-after-free cases may occur. The proposed fix is the following: since the only reason for the dev_change_net_namespace to fail is the name generation, we may give it a unique fall-back name w/o %d-s in it - the dev<ifindex> one, since ifindexes are still unique. So make this change, raise the failure-case printk loglevel to EMERG and replace the unregister_netdevice call with BUG(). [ Use snprintf() -DaveM ] Signed-off-by: Pavel Emelyanov <xemul@openvz.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/core/dev.c8
1 files changed, 5 insertions, 3 deletions
diff --git a/net/core/dev.c b/net/core/dev.c
index d334446a8eaf..4addaf0df96e 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4480,17 +4480,19 @@ static void __net_exit default_device_exit(struct net *net)
4480 rtnl_lock(); 4480 rtnl_lock();
4481 for_each_netdev_safe(net, dev, next) { 4481 for_each_netdev_safe(net, dev, next) {
4482 int err; 4482 int err;
4483 char fb_name[IFNAMSIZ];
4483 4484
4484 /* Ignore unmoveable devices (i.e. loopback) */ 4485 /* Ignore unmoveable devices (i.e. loopback) */
4485 if (dev->features & NETIF_F_NETNS_LOCAL) 4486 if (dev->features & NETIF_F_NETNS_LOCAL)
4486 continue; 4487 continue;
4487 4488
4488 /* Push remaing network devices to init_net */ 4489 /* Push remaing network devices to init_net */
4489 err = dev_change_net_namespace(dev, &init_net, "dev%d"); 4490 snprintf(fb_name, IFNAMSIZ, "dev%d", dev->ifindex);
4491 err = dev_change_net_namespace(dev, &init_net, fb_name);
4490 if (err) { 4492 if (err) {
4491 printk(KERN_WARNING "%s: failed to move %s to init_net: %d\n", 4493 printk(KERN_EMERG "%s: failed to move %s to init_net: %d\n",
4492 __func__, dev->name, err); 4494 __func__, dev->name, err);
4493 unregister_netdevice(dev); 4495 BUG();
4494 } 4496 }
4495 } 4497 }
4496 rtnl_unlock(); 4498 rtnl_unlock();