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

Jiayuan Chen posted 1 patch 3 weeks, 6 days ago
drivers/infiniband/core/verbs.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
[PATCH net v1] IB/core: Fix use-after-free of ipvlan phy_dev in ib_get_eth_speed
Posted by Jiayuan Chen 3 weeks, 6 days ago
From: Jiayuan Chen <jiayuan.chen@shopee.com>

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. 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/94089b74-def5-4dd0-9143-1cfbc722fe73@linux.dev/T/#t

Fixes: d41861942fc5 ("IB/core: Add generic function to extract IB speed from netdev")
Reported-by: Jianzhou Zhao <luckd0g@163.com>
Closes: https://lore.kernel.org/netdev/94089b74-def5-4dd0-9143-1cfbc722fe73@linux.dev/T/#t
Cc: Jiayuan Chen <jiayuan.chen@linux.dev>
Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
---
 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
Re: [PATCH net v1] IB/core: Fix use-after-free of ipvlan phy_dev in ib_get_eth_speed
Posted by Leon Romanovsky 3 weeks, 1 day ago
On Wed, Mar 11, 2026 at 06:03:08PM +0800, Jiayuan Chen wrote:
> From: Jiayuan Chen <jiayuan.chen@shopee.com>
> 
> 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/94089b74-def5-4dd0-9143-1cfbc722fe73@linux.dev/T/#t
> 
> Fixes: d41861942fc5 ("IB/core: Add generic function to extract IB speed from netdev")
> Reported-by: Jianzhou Zhao <luckd0g@163.com>
> Closes: https://lore.kernel.org/netdev/94089b74-def5-4dd0-9143-1cfbc722fe73@linux.dev/T/#t
> Cc: Jiayuan Chen <jiayuan.chen@linux.dev>
> Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
> ---
>  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
> 
>
Re: [PATCH net v1] IB/core: Fix use-after-free of ipvlan phy_dev in ib_get_eth_speed
Posted by Jiayuan Chen 3 weeks ago
On 3/17/26 12:29 AM, Leon Romanovsky wrote:
> On Wed, Mar 11, 2026 at 06:03:08PM +0800, Jiayuan Chen wrote:
>> From: Jiayuan Chen <jiayuan.chen@shopee.com>
>>
>> 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
>

ipvlan's phy_dev is safe in the data path — TX/RX runs in softirq
context with RCU protection, no lock needed per packet.

The issue here is in the control path. __ethtool_get_link_ksettings()
requires rtnl_lock() — all existing ethtool callers follow this:

- ioctl path: rtnl_lock() is taken first, then __dev_get_by_name()
looks up the dev without even holding a refcnt — relying entirely
on RTNL for safety. (net/ethtool/ioctl.c:3571, 3249)
- netlink path: dev is looked up with refcnt first, but the actual
ethtool ops run under rtnl_lock(). (net/ethtool/netlink.c:527-533)

Under RTNL, phy_dev cannot disappear because phy_dev unregistration
triggers NETDEV_UNREGISTER which deletes ipvlan first — all within
the same RTNL context. That's why no virtual netdev driver (ipvlan,
macvlan, bond, etc.) holds an extra refcnt on the lower dev in its
ethtool callbacks.

ib_get_eth_speed() calls __ethtool_get_link_ksettings() under
rtnl_lock(), but obtains the netdev before it. Moving the lookup
inside rtnl_lock() makes the netdev resolution and ethtool call
atomic w.r.t. device unregistration, consistent with how ethtool's
own paths work.


Thanks

Re: [PATCH net v1] IB/core: Fix use-after-free of ipvlan phy_dev in ib_get_eth_speed
Posted by Leon Romanovsky 3 weeks ago
On Tue, Mar 17, 2026 at 05:48:55PM +0800, Jiayuan Chen wrote:
> 
> On 3/17/26 12:29 AM, Leon Romanovsky wrote:
> > On Wed, Mar 11, 2026 at 06:03:08PM +0800, Jiayuan Chen wrote:
> > > From: Jiayuan Chen <jiayuan.chen@shopee.com>
> > > 
> > > 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
> > 
> 
> ipvlan's phy_dev is safe in the data path — TX/RX runs in softirq
> context with RCU protection, no lock needed per packet.
> 
> The issue here is in the control path. __ethtool_get_link_ksettings()
> requires rtnl_lock() — all existing ethtool callers follow this:
> 
> - ioctl path: rtnl_lock() is taken first, then __dev_get_by_name()
> looks up the dev without even holding a refcnt — relying entirely
> on RTNL for safety. (net/ethtool/ioctl.c:3571, 3249)
> - netlink path: dev is looked up with refcnt first, but the actual
> ethtool ops run under rtnl_lock(). (net/ethtool/netlink.c:527-533)
> 
> Under RTNL, phy_dev cannot disappear because phy_dev unregistration
> triggers NETDEV_UNREGISTER which deletes ipvlan first — all within
> the same RTNL context. That's why no virtual netdev driver (ipvlan,
> macvlan, bond, etc.) holds an extra refcnt on the lower dev in its
> ethtool callbacks.
> 
> ib_get_eth_speed() calls __ethtool_get_link_ksettings() under
> rtnl_lock(), but obtains the netdev before it. Moving the lookup
> inside rtnl_lock() makes the netdev resolution and ethtool call
> atomic w.r.t. device unregistration, consistent with how ethtool's
> own paths work.

Please reread my earlier response and explain how an ipvlan device can
disappear immediately after a successful call to ib_device_get_netdev().

Thanks

> 
> 
> Thanks
> 
> 
Re: [PATCH net v1] IB/core: Fix use-after-free of ipvlan phy_dev in ib_get_eth_speed
Posted by Jakub Kicinski 3 weeks, 5 days ago
On Wed, 11 Mar 2026 18:03:08 +0800 Jiayuan Chen wrote:
> Subject: [PATCH net v1] IB/core: Fix use-after-free of ipvlan phy_dev in ib_get_eth_speed

FWIW this is not a net patch (infiniband is not net)
-- 
pw-bot: nap