Re: [PATCH net v1] IB/core: Fix use-after-free of ipvlan phy_dev in ib_get_eth_speed
From: Jiayuan Chen
Date: Tue Mar 17 2026 - 05:58:52 EST
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@xxxxxxxxxx>If ib_device_get_netdev() worked as it was supposed to work, it can't.
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.
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