* [PATCH net 0/2] Fix ref_tracker warnings at boot
@ 2026-07-31 3:51 Jiangshan Yi
2026-07-31 3:51 ` [PATCH net 1/2] net: cancel pending netdev_work on device unregistration Jiangshan Yi
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Jiangshan Yi @ 2026-07-31 3:51 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni
Cc: horms, netdev, linux-kernel, 13667453960, Jiangshan Yi
This series fixes two ref_tracker warnings that appear at boot on
both TCG and KVM:
WARNING: lib/ref_tracker.c:322 at ref_tracker_free
WARNING: lib/ref_tracker.c:246 at ref_tracker_dir_exit
Patch 1 fixes the root cause: pending netdev_work is not canceled
during device unregistration, leaving a stale work_tracker. When
netdev_work_proc() later runs during the RTNL release window of
netdev_wait_allrefs_any(), it triggers the double-free and leak
warnings.
Patch 2 is a defensive hardening: netdev_put() unconditionally calls
__dev_put() even when ref_tracker_free() returns -EINVAL (double-free),
over-decrementing the device refcount. While patch 1 prevents the
double-free from occurring in the netdev_work path, patch 2 ensures
that any double-free from any source does not corrupt the refcount.
Tested on KVM (16 vCPU, 8 GB RAM) with KASAN/UBSAN/KFENCE enabled:
Before: 2 ref_tracker WARNINGs, 80 UBSAN reports
After: 0 WARNINGs, 53 UBSAN reports (23 network-related fixed)
0 KASAN, 0 BUG, no regressions
Jiangshan Yi (2):
net: cancel pending netdev_work on device unregistration
net: guard refcount against tracker double-free
include/linux/netdevice.h | 19 ++++++++++++-------
net/core/dev.c | 2 +-
net/core/dev.h | 2 ++
net/core/netdev_work.c | 10 ++++++++++
4 files changed, 25 insertions(+), 8 deletions(-)
--
2.25.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH net 1/2] net: cancel pending netdev_work on device unregistration
2026-07-31 3:51 [PATCH net 0/2] Fix ref_tracker warnings at boot Jiangshan Yi
@ 2026-07-31 3:51 ` Jiangshan Yi
2026-07-31 3:51 ` [PATCH net 2/2] net: guard refcount against tracker double-free Jiangshan Yi
2026-08-06 2:27 ` [PATCH net 0/2] Fix ref_tracker warnings at boot Jakub Kicinski
2 siblings, 0 replies; 4+ messages in thread
From: Jiangshan Yi @ 2026-07-31 3:51 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni
Cc: horms, netdev, linux-kernel, 13667453960, Jiangshan Yi, stable
When a netdev is unregistered, pending netdev_work events are not
explicitly canceled. Only events cleared by ndo_stop() (e.g.
VLAN_WORK_LINK_STATE) are handled; core events like
NETDEV_WORK_RX_MODE and driver events like VLAN_WORK_MTU /
VLAN_WORK_FEATURES remain pending, keeping a tracked reference
(dev->work_tracker) on the device.
During netdev_wait_allrefs_any(), RTNL is released to let the system
workqueue run. netdev_work_proc() may then attempt to process the
stale work_tracker, triggering ref_tracker warnings at boot:
WARNING: lib/ref_tracker.c:322 at ref_tracker_free
WARNING: lib/ref_tracker.c:246 at ref_tracker_dir_exit
Add netdev_work_cancel_all() to cancel all pending core and driver
work, releasing the work_tracker before the device enters
netdev_run_todo(). Call it from unregister_netdevice_many_notify()
after rx_mode cleanup.
This is safe because netdev_work_cancel_all() runs under RTNL in
unregister_netdevice_many_notify(), and netdev_work_proc() also
requires RTNL, so the two cannot execute concurrently. If
netdev_work_proc() has already dequeued the device, the cancel is a
no-op; otherwise it releases the tracker before the device enters
netdev_run_todo().
Fixes: 12c765be84d2 ("net: turn the rx_mode work into a generic netdev_work facility")
Cc: stable@vger.kernel.org
Signed-off-by: Jiangshan Yi <yijiangshan@kylinos.cn>
---
net/core/dev.c | 2 +-
net/core/dev.h | 2 ++
net/core/netdev_work.c | 10 ++++++++++
3 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/net/core/dev.c b/net/core/dev.c
index 5933c5d..b461b01 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -12456,7 +12456,7 @@ void unregister_netdevice_many_notify(struct list_head *head,
dev_uc_flush(dev);
dev_mc_flush(dev);
-
+ netdev_work_cancel_all(dev);
netdev_rss_contexts_free(dev);
call_netdevice_notifiers(NETDEV_PRE_UNINIT, dev);
diff --git a/net/core/dev.h b/net/core/dev.h
index 5d0b030..d3ac3ba 100644
--- a/net/core/dev.h
+++ b/net/core/dev.h
@@ -180,6 +180,8 @@ void __netdev_work_core_sched(struct net_device *dev, unsigned long event);
unsigned long
__netdev_work_core_cancel(struct net_device *dev, unsigned long mask);
+void netdev_work_cancel_all(struct net_device *dev);
+
void __dev_notify_flags(struct net_device *dev, unsigned int old_flags,
unsigned int gchanges, u32 portid,
const struct nlmsghdr *nlh);
diff --git a/net/core/netdev_work.c b/net/core/netdev_work.c
index 3109fae..d69363d 100644
--- a/net/core/netdev_work.c
+++ b/net/core/netdev_work.c
@@ -99,6 +99,16 @@ __netdev_work_core_cancel(struct net_device *dev, unsigned long mask)
return netdev_work_dequeue(dev, &dev->work_core_pending, mask);
}
+/* Cancel all pending core and driver work and release the work_tracker
+ * reference. Must be called under RTNL, which mutually excludes
+ * netdev_work_proc().
+ */
+void netdev_work_cancel_all(struct net_device *dev)
+{
+ __netdev_work_core_cancel(dev, ~0UL);
+ netdev_work_cancel(dev, ~0UL);
+}
+
static void netdev_work_run(struct net_device *dev, unsigned long events,
unsigned long core)
{
--
2.25.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH net 2/2] net: guard refcount against tracker double-free
2026-07-31 3:51 [PATCH net 0/2] Fix ref_tracker warnings at boot Jiangshan Yi
2026-07-31 3:51 ` [PATCH net 1/2] net: cancel pending netdev_work on device unregistration Jiangshan Yi
@ 2026-07-31 3:51 ` Jiangshan Yi
2026-08-06 2:27 ` [PATCH net 0/2] Fix ref_tracker warnings at boot Jakub Kicinski
2 siblings, 0 replies; 4+ messages in thread
From: Jiangshan Yi @ 2026-07-31 3:51 UTC (permalink / raw)
To: andrew+netdev, davem, edumazet, kuba, pabeni
Cc: horms, netdev, linux-kernel, 13667453960, Jiangshan Yi, stable
As a defensive measure, guard netdev_put() and netdev_ref_replace()
against refcount corruption when ref_tracker_free() returns -EINVAL
(tracker already freed). Currently __dev_put() is called
unconditionally, which over-decrements the device refcount on a
double-free and can cause netdev_wait_allrefs_any() to return
prematurely.
Change netdev_tracker_free() to return int so callers can detect
the -EINVAL case. In netdev_put() and netdev_ref_replace(), skip
__dev_put() when the tracker was already freed; ref_tracker_free()
has already warned and the first successful free already decremented
the refcount. The -EEXIST case (NULL tracker, untracked reference)
still calls __dev_put(), preserving existing behavior.
Fixes: f12bf6f3f942 ("net: watchdog: add net device refcount tracker")
Cc: stable@vger.kernel.org
Signed-off-by: Jiangshan Yi <yijiangshan@kylinos.cn>
---
include/linux/netdevice.h | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 9981d63..5e8cf3f 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -4512,11 +4512,13 @@ static inline void netdev_tracker_alloc(struct net_device *dev,
#endif
}
-static inline void netdev_tracker_free(struct net_device *dev,
+static inline int netdev_tracker_free(struct net_device *dev,
netdevice_tracker *tracker)
{
#ifdef CONFIG_NET_DEV_REFCNT_TRACKER
- ref_tracker_free(&dev->refcnt_tracker, tracker);
+ return ref_tracker_free(&dev->refcnt_tracker, tracker);
+#else
+ return 0;
#endif
}
@@ -4533,8 +4535,12 @@ static inline void netdev_put(struct net_device *dev,
netdevice_tracker *tracker)
{
if (dev) {
- netdev_tracker_free(dev, tracker);
- __dev_put(dev);
+ /* Skip __dev_put() on -EINVAL: ref_tracker_free() already
+ * warned and the refcount was decremented by the first
+ * successful free.
+ */
+ if (netdev_tracker_free(dev, tracker) != -EINVAL)
+ __dev_put(dev);
}
}
@@ -4569,11 +4575,10 @@ static inline void netdev_ref_replace(struct net_device *odev,
netdevice_tracker *tracker,
gfp_t gfp)
{
- if (odev)
- netdev_tracker_free(odev, tracker);
+ if (odev && netdev_tracker_free(odev, tracker) != -EINVAL)
+ __dev_put(odev);
__dev_hold(ndev);
- __dev_put(odev);
if (ndev)
__netdev_tracker_alloc(ndev, tracker, gfp);
--
2.25.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH net 0/2] Fix ref_tracker warnings at boot
2026-07-31 3:51 [PATCH net 0/2] Fix ref_tracker warnings at boot Jiangshan Yi
2026-07-31 3:51 ` [PATCH net 1/2] net: cancel pending netdev_work on device unregistration Jiangshan Yi
2026-07-31 3:51 ` [PATCH net 2/2] net: guard refcount against tracker double-free Jiangshan Yi
@ 2026-08-06 2:27 ` Jakub Kicinski
2 siblings, 0 replies; 4+ messages in thread
From: Jakub Kicinski @ 2026-08-06 2:27 UTC (permalink / raw)
To: Jiangshan Yi
Cc: andrew+netdev, davem, edumazet, pabeni, horms, netdev,
linux-kernel, 13667453960
On Fri, 31 Jul 2026 11:51:33 +0800 Jiangshan Yi wrote:
> This series fixes two ref_tracker warnings that appear at boot on
> both TCG and KVM:
>
> WARNING: lib/ref_tracker.c:322 at ref_tracker_free
> WARNING: lib/ref_tracker.c:246 at ref_tracker_dir_exit
Thanks for the report, I've been siting on a fix for this.
Let me send mine out.
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-08-06 2:27 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-31 3:51 [PATCH net 0/2] Fix ref_tracker warnings at boot Jiangshan Yi
2026-07-31 3:51 ` [PATCH net 1/2] net: cancel pending netdev_work on device unregistration Jiangshan Yi
2026-07-31 3:51 ` [PATCH net 2/2] net: guard refcount against tracker double-free Jiangshan Yi
2026-08-06 2:27 ` [PATCH net 0/2] Fix ref_tracker warnings at boot Jakub Kicinski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox