mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 14:04:27 -04:00
ipvlan: Synchronise ipvlan_init() and ipvlan_uninit() for the same lower dev.
ipvlan_uninit() for the last ipvlan device resets the lower device's
rx_handler_data to NULL.
Once RTNL is removed, ipvlan_init() would race with ipvlan_uninit(),
which could leak a newly allocated ipvl_port.
ipvlan_init() ipvlan_uninit()
| |- if (refcount_dec_and_test(old_port))
... |- ipvlan_port_destroy(old_port)
| '
|- refcount_inc_not_zero(old_port) <-- fails
|- ipvlan_port_create(phy_dev) .
|- new_port = kzalloc() |
|- phy_dev->rx_handler_data = new_port
|- phy_dev->rx_handler_data = NULL
...
`- kfree(old_port);
Let's synchronise the two by holding the lower device's netdev_lock().
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Link: https://patch.msgid.link/20260703001009.1572444-13-kuniyu@google.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
committed by
Paolo Abeni
parent
acb351b5a8
commit
aabbdb8c76
@@ -177,9 +177,12 @@ static int ipvlan_init(struct net_device *dev)
|
||||
if (!ipvlan->pcpu_stats)
|
||||
return -ENOMEM;
|
||||
|
||||
netdev_lock(phy_dev);
|
||||
|
||||
if (!netif_is_ipvlan_port(phy_dev)) {
|
||||
err = ipvlan_port_create(phy_dev);
|
||||
if (err < 0) {
|
||||
netdev_unlock(phy_dev);
|
||||
free_percpu(ipvlan->pcpu_stats);
|
||||
return err;
|
||||
}
|
||||
@@ -190,6 +193,8 @@ static int ipvlan_init(struct net_device *dev)
|
||||
refcount_inc(&port->count);
|
||||
}
|
||||
|
||||
netdev_unlock(phy_dev);
|
||||
|
||||
ipvlan->port = port;
|
||||
|
||||
return 0;
|
||||
@@ -198,9 +203,19 @@ static int ipvlan_init(struct net_device *dev)
|
||||
static void ipvlan_uninit(struct net_device *dev)
|
||||
{
|
||||
struct ipvl_dev *ipvlan = netdev_priv(dev);
|
||||
netdevice_tracker dev_tracker;
|
||||
struct net_device *phy_dev;
|
||||
|
||||
free_percpu(ipvlan->pcpu_stats);
|
||||
|
||||
phy_dev = ipvlan->phy_dev;
|
||||
netdev_hold(phy_dev, &dev_tracker, GFP_KERNEL);
|
||||
netdev_lock(phy_dev);
|
||||
|
||||
ipvlan_port_put(ipvlan->port);
|
||||
|
||||
netdev_unlock(phy_dev);
|
||||
netdev_put(phy_dev, &dev_tracker);
|
||||
}
|
||||
|
||||
static int ipvlan_open(struct net_device *dev)
|
||||
|
||||
Reference in New Issue
Block a user