Re: [PATCH can v2] can: j1939: avoid address-claim timer deadlock

[email protected] Mon, 03 Aug 2026 10:43:24 +0000
Newsgroups org.kernel.vger.linux-can
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 2 potential issue(=
s) to consider:

Pre-existing issues:
- [High] Lockless calls to `j1939_ecu_put()` can drop the last reference to=
 an ECU, triggering `__j1939_ecu_release()` and modifying `priv->ecus` lock=
lessly.
- [High] Permanent memory leak of `j1939_ecu` and `j1939_priv` when the net=
work interface is brought down while an address claim timer is pending.
--

--- Patch [1]: [PATCH can v2] can: j1939: avoid address-claim timer deadloc=
k ---
commit 08d11140bccb342c8267ab26aee9e24877e324a3
Author: Felix Hoffmann <[email protected]>

can: j1939: avoid address-claim timer deadlock

j1939_ac_process() holds priv->lock while synchronously canceling an
ECU's address-claim hrtimer. The timer callback takes the same lock. If the
callback starts on another CPU after the receive path takes the lock, the
callback waits for priv->lock while hrtimer_cancel() waits for the callback
to finish. This deadlocks both CPUs and makes the system unresponsive.

Cc: [email protected]
Signed-off-by: Felix Hoffmann <[email protected]>

> diff --git a/net/can/j1939/bus.c b/net/can/j1939/bus.c
> index cdc3c0a719379..eb9a3fd847f42 100644
> --- a/net/can/j1939/bus.c
> +++ b/net/can/j1939/bus.c
> @@ -10,10 +10,19 @@
>   * Since rtnetlink, no real bus is used.
>   */
> =20
> +#include <linux/net.h>
> +
>  #include <net/sock.h>
> =20
>  #include "j1939-priv.h"
> =20
> +#define J1939_ECU_TIMER_RETRY_DELAY_MS 1
> +/*
> + * J1939 does not specify a lock-contention retry count. Five retries ar=
e a
> + * pragmatic threshold for warning about an unusual address-claim delay.
> + */
> +#define J1939_ECU_TIMER_RETRY_WARN 5
> +
>  static void __j1939_ecu_release(struct kref *kref)
>  {
>  	struct j1939_ecu *ecu =3D container_of(kref, struct j1939_ecu, kref);
>  	struct j1939_priv *priv =3D ecu->priv;
> =20
>  	list_del(&ecu->list);

[Severity: High]
This isn't a bug introduced by this patch, but could this lead to list
corruption?

If an ECU is concurrently unmapped (dropping its primary reference) while
another thread holds a temporary reference (e.g., in j1939_ac_recv() or
j1939_ac_fixup()), dropping the temporary reference locklessly via
j1939_ecu_put() will reduce the refcount to 0.

This triggers __j1939_ecu_release(), which performs list_del(&ecu->list) on
priv->ecus without holding priv->lock.

Can this be triggered remotely by sending precisely timed address claim and
release packets to cause a use-after-free or panic?

[ ... ]

> @@ -109,6 +118,8 @@ void j1939_ecu_unmap_all(struct j1939_priv *priv)
> =20
>  void j1939_ecu_timer_start(struct j1939_ecu *ecu)
>  {

[Severity: High]
This is a pre-existing issue, but does this leak j1939_ecu and j1939_priv w=
hen
the network interface is brought down?

When a J1939 interface is unregistered, j1939_ecu_unmap_all() clears mapped
ECUs from priv->ents, but it only iterates over mapped ECUs and ignores ECUs
in priv->ecus that have a pending timer:

net/can/j1939/bus.c:j1939_ecu_unmap_all() {
        for (i =3D 0; i < ARRAY_SIZE(priv->ents); i++)
                if (priv->ents[i].ecu)
                        j1939_ecu_unmap_locked(priv->ents[i].ecu);
}

It seems this fails to cancel pending ac_timer hrtimers for ECUs in the
middle of address claiming.

When the uncancelled timer subsequently fires, j1939_ecu_timer_handler()
executes and maps the ECU back into the now-abandoned j1939_priv.

Since interface teardown (j1939_netdev_notify()) won't run
j1939_ecu_unmap_all() again, does this cause a permanent leak blocking
unregister_netdevice()?

> +	ecu->ac_timer_retries =3D 0;
> +

--=20
Sashiko AI review =C2=B7 https://sashiko.dev/#/patchset/20260803101503.2102=
[email protected]?part=3D1