* [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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.