aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2008-01-20 20:25:14 -0500
committerDavid S. Miller <davem@davemloft.net>2008-01-20 23:31:45 -0500
commit68365458a4252fa993b91a00f7a0b18fed399f0d (patch)
tree824b1f32ba3b955c018626127602c365f986ccfc
parentd4782c323d10d3698b71b6a6b3c7bdad33824658 (diff)
[NET]: rtnl_link: fix use-after-free
When unregistering the rtnl_link_ops, all existing devices using the ops are destroyed. With nested devices this may lead to a use-after-free despite the use of for_each_netdev_safe() in case the upper device is next in the device list and is destroyed by the NETDEV_UNREGISTER notifier. The easy fix is to restart scanning the device list after removing a device. Alternatively we could add new devices to the front of the list to avoid having dependant devices follow the device they depend on. A third option would be to only restart scanning if dev->iflink of the next device matches dev->ifindex of the current one. For now this seems like the safest solution. With this patch, the veth rtnl_link_ops unregistration can use rtnl_link_unregister() directly since it now also handles destruction of multiple devices at once. Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/veth.c14
-rw-r--r--net/core/rtnetlink.c5
2 files changed, 5 insertions, 14 deletions
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 43af9e9b2652..3f67a29593bc 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -459,19 +459,7 @@ static __init int veth_init(void)
459 459
460static __exit void veth_exit(void) 460static __exit void veth_exit(void)
461{ 461{
462 struct veth_priv *priv, *next; 462 rtnl_link_unregister(&veth_link_ops);
463
464 rtnl_lock();
465 /*
466 * cannot trust __rtnl_link_unregister() to unregister all
467 * devices, as each ->dellink call will remove two devices
468 * from the list at once.
469 */
470 list_for_each_entry_safe(priv, next, &veth_list, list)
471 veth_dellink(priv->dev);
472
473 __rtnl_link_unregister(&veth_link_ops);
474 rtnl_unlock();
475} 463}
476 464
477module_init(veth_init); 465module_init(veth_init);
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index e1ba26fb4bf2..fed95a323b28 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -308,9 +308,12 @@ void __rtnl_link_unregister(struct rtnl_link_ops *ops)
308 struct net *net; 308 struct net *net;
309 309
310 for_each_net(net) { 310 for_each_net(net) {
311restart:
311 for_each_netdev_safe(net, dev, n) { 312 for_each_netdev_safe(net, dev, n) {
312 if (dev->rtnl_link_ops == ops) 313 if (dev->rtnl_link_ops == ops) {
313 ops->dellink(dev); 314 ops->dellink(dev);
315 goto restart;
316 }
314 } 317 }
315 } 318 }
316 list_del(&ops->list); 319 list_del(&ops->list);