Commit 68365458a4252fa993b91a00f7a0b18fed399f0d

Authored by Patrick McHardy
Committed by David S. Miller
1 parent d4782c323d

[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

... ... @@ -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);