Commit 68365458a4252fa993b91a00f7a0b18fed399f0d
Committed by
David S. Miller
1 parent
d4782c323d
Exists in
master
and in
7 other branches
[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>
Showing 2 changed files with 5 additions and 14 deletions Side-by-side Diff
drivers/net/veth.c
... | ... | @@ -459,19 +459,7 @@ |
459 | 459 | |
460 | 460 | static __exit void veth_exit(void) |
461 | 461 | { |
462 | - struct veth_priv *priv, *next; | |
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(); | |
462 | + rtnl_link_unregister(&veth_link_ops); | |
475 | 463 | } |
476 | 464 | |
477 | 465 | module_init(veth_init); |
net/core/rtnetlink.c
... | ... | @@ -308,9 +308,12 @@ |
308 | 308 | struct net *net; |
309 | 309 | |
310 | 310 | for_each_net(net) { |
311 | +restart: | |
311 | 312 | for_each_netdev_safe(net, dev, n) { |
312 | - if (dev->rtnl_link_ops == ops) | |
313 | + if (dev->rtnl_link_ops == ops) { | |
313 | 314 | ops->dellink(dev); |
315 | + goto restart; | |
316 | + } | |
314 | 317 | } |
315 | 318 | } |
316 | 319 | list_del(&ops->list); |