mirror of
https://github.com/adulau/aha.git
synced 2024-12-29 04:06:22 +00:00
[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>
This commit is contained in:
parent
d4782c323d
commit
68365458a4
2 changed files with 5 additions and 14 deletions
|
@ -459,19 +459,7 @@ static __init int veth_init(void)
|
||||||
|
|
||||||
static __exit void veth_exit(void)
|
static __exit void veth_exit(void)
|
||||||
{
|
{
|
||||||
struct veth_priv *priv, *next;
|
rtnl_link_unregister(&veth_link_ops);
|
||||||
|
|
||||||
rtnl_lock();
|
|
||||||
/*
|
|
||||||
* cannot trust __rtnl_link_unregister() to unregister all
|
|
||||||
* devices, as each ->dellink call will remove two devices
|
|
||||||
* from the list at once.
|
|
||||||
*/
|
|
||||||
list_for_each_entry_safe(priv, next, &veth_list, list)
|
|
||||||
veth_dellink(priv->dev);
|
|
||||||
|
|
||||||
__rtnl_link_unregister(&veth_link_ops);
|
|
||||||
rtnl_unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(veth_init);
|
module_init(veth_init);
|
||||||
|
|
|
@ -308,9 +308,12 @@ void __rtnl_link_unregister(struct rtnl_link_ops *ops)
|
||||||
struct net *net;
|
struct net *net;
|
||||||
|
|
||||||
for_each_net(net) {
|
for_each_net(net) {
|
||||||
|
restart:
|
||||||
for_each_netdev_safe(net, dev, n) {
|
for_each_netdev_safe(net, dev, n) {
|
||||||
if (dev->rtnl_link_ops == ops)
|
if (dev->rtnl_link_ops == ops) {
|
||||||
ops->dellink(dev);
|
ops->dellink(dev);
|
||||||
|
goto restart;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list_del(&ops->list);
|
list_del(&ops->list);
|
||||||
|
|
Loading…
Reference in a new issue