]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
can: j1939: use netdevice_tracker for j1939_{priv,session,ecu} tracking
authorTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Tue, 28 Jul 2026 05:58:34 +0000 (07:58 +0200)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Wed, 29 Jul 2026 08:15:08 +0000 (10:15 +0200)
syzbot is still reporting

  unregister_netdevice: waiting for vcan0 to become free. Usage count = 2

problem. A debug printk() patch in linux-next-20260508 identified that
there is dev_hold()/dev_put() imbalance in j1939_priv management.

  Call trace for vcan0[26] +4 at
     __dev_hold include/linux/netdevice.h:4470 [inline]
     netdev_hold include/linux/netdevice.h:4513 [inline]
     dev_hold include/linux/netdevice.h:4536 [inline]
     j1939_priv_create net/can/j1939/main.c:140 [inline]
     j1939_netdev_start+0x36b/0xc10 net/can/j1939/main.c:268
     j1939_sk_bind+0x853/0xb30 net/can/j1939/socket.c:506
     __sys_bind_socket net/socket.c:1948 [inline]
     __sys_bind+0x2e9/0x410 net/socket.c:1979

  Call trace for vcan0[28] -3 at
     __dev_put include/linux/netdevice.h:4456 [inline]
     netdev_put include/linux/netdevice.h:4523 [inline]
     dev_put include/linux/netdevice.h:4548 [inline]
     __j1939_priv_release net/can/j1939/main.c:166 [inline]
     kref_put include/linux/kref.h:65 [inline]
     j1939_priv_put+0x128/0x270 net/can/j1939/main.c:172
     j1939_sk_sock_destruct+0x52/0x90 net/can/j1939/socket.c:388
     __sk_destruct+0x8d/0x9d0 net/core/sock.c:2352
     rcu_do_batch kernel/rcu/tree.c:2617 [inline]
     rcu_core kernel/rcu/tree.c:2869 [inline]
     rcu_cpu_kthread+0x99e/0x1470 kernel/rcu/tree.c:2957
     smpboot_thread_fn+0x541/0xa50 kernel/smpboot.c:160
     kthread+0x388/0x470 kernel/kthread.c:436
     ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
     ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245

This refcount leak in j1939_priv might be caused by a refcount leak in
j1939_{session,ecu} because j1939_{session,ecu} holds a ref on j1939_priv.
For further investigation using upstream kernels, enable netdevice_tracker
in j1939_{priv,session,ecu} management.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Link: https://patch.msgid.link/20260728055835.1151785-2-o.rempel@pengutronix.de
Cc: stable@kernel.org
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
net/can/j1939/bus.c
net/can/j1939/j1939-priv.h
net/can/j1939/main.c
net/can/j1939/transport.c

index dc374286eeb6e04b8eaf0aaf9f6fc5af1f59d4e9..cdc3c0a71937935eacfcfbf2f8350a7987ee6790 100644 (file)
@@ -20,6 +20,7 @@ static void __j1939_ecu_release(struct kref *kref)
        struct j1939_priv *priv = ecu->priv;
 
        list_del(&ecu->list);
+       netdev_put(priv->ndev, &ecu->priv_dev_tracker);
        kfree(ecu);
        j1939_priv_put(priv);
 }
@@ -155,6 +156,7 @@ struct j1939_ecu *j1939_ecu_create_locked(struct j1939_priv *priv, name_t name)
        if (!ecu)
                return ERR_PTR(-ENOMEM);
        kref_init(&ecu->kref);
+       netdev_hold(priv->ndev, &ecu->priv_dev_tracker, gfp_any());
        ecu->addr = J1939_IDLE_ADDR;
        ecu->name = name;
 
index 81f58924b4acd7a88debfb9d4c91529ab01ee854..cf26352d1d8ce6cba0ec1231ca9b321e898d71c3 100644 (file)
@@ -38,6 +38,7 @@ struct j1939_ecu {
        struct hrtimer ac_timer;
        struct kref kref;
        struct j1939_priv *priv;
+       netdevice_tracker priv_dev_tracker;
 
        /* count users, to help transport protocol decide for interaction */
        int nusers;
@@ -60,6 +61,7 @@ struct j1939_priv {
        rwlock_t lock;
 
        struct net_device *ndev;
+       netdevice_tracker dev_tracker;
 
        /* list of 256 ecu ptrs, that cache the claimed addresses.
         * also protected by the above lock
@@ -230,6 +232,7 @@ enum j1939_session_state {
 
 struct j1939_session {
        struct j1939_priv *priv;
+       netdevice_tracker priv_dev_tracker;
        struct list_head active_session_list_entry;
        struct list_head sk_session_queue_entry;
        struct kref kref;
index 9937c04241bc34ae0a7d87c5f4952eb2a4eba6ff..5e5e6c228f2252cf66e13bf4d87221f3b66ecc24 100644 (file)
@@ -137,7 +137,7 @@ static struct j1939_priv *j1939_priv_create(struct net_device *ndev)
        priv->ndev = ndev;
        kref_init(&priv->kref);
        kref_init(&priv->rx_kref);
-       dev_hold(ndev);
+       netdev_hold(ndev, &priv->dev_tracker, GFP_KERNEL);
 
        netdev_dbg(priv->ndev, "%s : 0x%p\n", __func__, priv);
 
@@ -163,7 +163,7 @@ static void __j1939_priv_release(struct kref *kref)
        WARN_ON_ONCE(!list_empty(&priv->ecus));
        WARN_ON_ONCE(!list_empty(&priv->j1939_socks));
 
-       dev_put(ndev);
+       netdev_put(ndev, &priv->dev_tracker);
        kfree(priv);
 }
 
@@ -281,7 +281,7 @@ struct j1939_priv *j1939_netdev_start(struct net_device *ndev)
                 */
                kref_get(&priv_new->rx_kref);
                mutex_unlock(&j1939_netdev_lock);
-               dev_put(ndev);
+               netdev_put(ndev, &priv->dev_tracker);
                kfree(priv);
                return priv_new;
        }
@@ -298,7 +298,7 @@ struct j1939_priv *j1939_netdev_start(struct net_device *ndev)
        j1939_priv_set(ndev, NULL);
        mutex_unlock(&j1939_netdev_lock);
 
-       dev_put(ndev);
+       netdev_put(ndev, &priv->dev_tracker);
        kfree(priv);
 
        return ERR_PTR(ret);
index 8a31cb23bc76d0abf6451e3958119d1237626a88..98f96362b20f393bc011368c3738915f8ebc3833 100644 (file)
@@ -283,6 +283,7 @@ static void j1939_session_destroy(struct j1939_session *session)
                kfree_skb(skb);
        }
        __j1939_session_drop(session);
+       netdev_put(session->priv->ndev, &session->priv_dev_tracker);
        j1939_priv_put(session->priv);
        kfree(session);
 }
@@ -1526,6 +1527,7 @@ static struct j1939_session *j1939_session_new(struct j1939_priv *priv,
        INIT_LIST_HEAD(&session->active_session_list_entry);
        INIT_LIST_HEAD(&session->sk_session_queue_entry);
        kref_init(&session->kref);
+       netdev_hold(priv->ndev, &session->priv_dev_tracker, gfp_any());
 
        j1939_priv_get(priv);
        session->priv = priv;