]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: fix reference tracker mismanagement in netdev_put_lock()
authorJakub Kicinski <kuba@kernel.org>
Fri, 10 Apr 2026 15:36:00 +0000 (08:36 -0700)
committerJakub Kicinski <kuba@kernel.org>
Sun, 12 Apr 2026 16:08:43 +0000 (09:08 -0700)
dev_put() releases a reference which didn't have a tracker.
References without a tracker are accounted in the tracking
code as "no_tracker". We can't free the tracker and then
call dev_put(). The references themselves will be fine
but the tracking code will think it's a double-release:

  refcount_t: decrement hit 0; leaking memory.

IOW commit under fixes confused dev_put() (release never tracked
reference) with __dev_put() (just release the reference, skipping
the reference tracking infra).

Since __netdev_put_lock() uses dev_put() we can't feed a previously
tracked netdev ref into it. Let's flip things around.
netdev_put(dev, NULL) is the same as dev_put(dev) so make
netdev_put_lock() the real function and have __netdev_put_lock()
feed it a NULL tracker for all the cases that were untracked.

Fixes: d04686d9bc86 ("net: Implement netdev_nl_queue_create_doit")
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://patch.msgid.link/20260410153600.1984522-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/core/dev.c
net/core/dev.h

index ccc4418b31631e94df72a307a8e7a33c8b1d8a3d..e59f6025067c1c1957408da601ea62b031faa8a2 100644 (file)
@@ -1060,16 +1060,18 @@ struct net_device *dev_get_by_napi_id(unsigned int napi_id)
  * This helper is intended for locking net_device after it has been looked up
  * using a lockless lookup helper. Lock prevents the instance from going away.
  */
-struct net_device *__netdev_put_lock(struct net_device *dev, struct net *net)
+struct net_device *
+netdev_put_lock(struct net_device *dev, struct net *net,
+               netdevice_tracker *tracker)
 {
        netdev_lock(dev);
        if (dev->reg_state > NETREG_REGISTERED ||
            dev->moving_ns || !net_eq(dev_net(dev), net)) {
                netdev_unlock(dev);
-               dev_put(dev);
+               netdev_put(dev, tracker);
                return NULL;
        }
-       dev_put(dev);
+       netdev_put(dev, tracker);
        return dev;
 }
 
@@ -1121,14 +1123,6 @@ netdev_get_by_index_lock_ops_compat(struct net *net, int ifindex)
        return __netdev_put_lock_ops_compat(dev, net);
 }
 
-struct net_device *
-netdev_put_lock(struct net_device *dev, struct net *net,
-               netdevice_tracker *tracker)
-{
-       netdev_tracker_free(dev, tracker);
-       return __netdev_put_lock(dev, net);
-}
-
 struct net_device *
 netdev_xa_find_lock(struct net *net, struct net_device *dev,
                    unsigned long *index)
index 376bac4a82daf5e225dbcd679bffcf4654370508..628bdaebf0cab74083879ef383bc134c4c8396f6 100644 (file)
@@ -31,9 +31,15 @@ struct napi_struct *
 netdev_napi_by_id_lock(struct net *net, unsigned int napi_id);
 struct net_device *dev_get_by_napi_id(unsigned int napi_id);
 
-struct net_device *__netdev_put_lock(struct net_device *dev, struct net *net);
 struct net_device *netdev_put_lock(struct net_device *dev, struct net *net,
                                   netdevice_tracker *tracker);
+
+static inline struct net_device *
+__netdev_put_lock(struct net_device *dev, struct net *net)
+{
+       return netdev_put_lock(dev, net, NULL);
+}
+
 struct net_device *
 netdev_xa_find_lock(struct net *net, struct net_device *dev,
                    unsigned long *index);