Subject: Re: [PATCH net v1] IB/core: Fix use-after-free of ipvlan phy_dev in ib_get_eth_speed

From: Leon Romanovsky <[email-masked]>
Date: Mon, 16 Mar 2026 18:29:09 +0200

On Wed, Mar 11, 2026 at 06:03:08PM +0800, Jiayuan Chen wrote:
> From: Jiayuan Chen <[email-masked]>
> 
> Jianzhou Zhao reported a NULL pointer dereference in
> __ethtool_get_link_ksettings [1]. The root cause is a use-after-free
> of ipvlan->phy_dev.
> 
> In ib_get_eth_speed(), ib_device_get_netdev() obtains a reference to the
> ipvlan device outside of rtnl_lock(). This creates a race window: between
> ib_device_get_netdev() and rtnl_lock(), the underlying phy_dev (e.g. a
> dummy device) can be unregistered and freed by another thread.

If ib_device_get_netdev() worked as it was supposed to work, it can't.
That function grabs reference on netdev and returns or netdev with elevated
reference counter which can't be freed or returns NULL.

Thanks

> When the ethtool call later recurses through ipvlan_ethtool_get_link_ksettings()
> into the freed phy_dev, it dereferences freed memory whose ethtool_ops
> reads as NULL, causing the crash at offset 0x1f8.
> 
> Fix this by moving ib_device_get_netdev() inside the rtnl_lock() section
> so that netdev lookup and the ethtool call are atomic with respect to
> device unregistration. Under RTNL, if the phy_dev has been deleted, the
> ipvlan device is also unregistered and ib_device_get_netdev() returns NULL
> safely.
> 
> None of the existing callers of ib_get_eth_speed() hold rtnl_lock, so this
> change does not introduce any deadlock.
> 
> [1] https://lore.kernel.org/netdev/[email-masked]/T/#t
> 
> Fixes: d41861942fc5 ("IB/core: Add generic function to extract IB speed from netdev")
> Reported-by: Jianzhou Zhao <[email-masked]>
> Closes: https://lore.kernel.org/netdev/[email-masked]/T/#t
> Cc: Jiayuan Chen <[email-masked]>
> Signed-off-by: Jiayuan Chen <[email-masked]>
> ---
>  drivers/infiniband/core/verbs.c | 6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
> index 575b4a4b200b..f16d11e7c2e3 100644
> --- a/drivers/infiniband/core/verbs.c
> +++ b/drivers/infiniband/core/verbs.c
> @@ -2046,11 +2046,13 @@ int ib_get_eth_speed(struct ib_device *dev, u32 port_num, u16 *speed, u8 *width)
>  	if (rdma_port_get_link_layer(dev, port_num) != IB_LINK_LAYER_ETHERNET)
>  		return -EINVAL;
>  
> +	rtnl_lock();
>  	netdev = ib_device_get_netdev(dev, port_num);
> -	if (!netdev)
> +	if (!netdev) {
> +		rtnl_unlock();
>  		return -ENODEV;
> +	}
>  
> -	rtnl_lock();
>  	rc = __ethtool_get_link_ksettings(netdev, &lksettings);
>  	rtnl_unlock();
>  
> -- 
> 2.43.0
> 
>