diff options
| author | Patrick McHardy <kaber@trash.net> | 2008-01-20 20:25:14 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2008-01-20 23:31:45 -0500 |
| commit | 68365458a4252fa993b91a00f7a0b18fed399f0d (patch) | |
| tree | 824b1f32ba3b955c018626127602c365f986ccfc | |
| parent | d4782c323d10d3698b71b6a6b3c7bdad33824658 (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.c | 14 | ||||
| -rw-r--r-- | net/core/rtnetlink.c | 5 |
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 | ||
| 460 | static __exit void veth_exit(void) | 460 | static __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 | ||
| 477 | module_init(veth_init); | 465 | module_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) { |
| 311 | restart: | ||
| 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); |
