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:
Kuniyuki Iwashima
2026-07-03 00:09:23 +00:00
committed by Paolo Abeni
parent acb351b5a8
commit aabbdb8c76

View File

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