From: sashiko-bot@kernel.org
To: "Tetsuo Handa" <penguin-kernel@I-love.SAKURA.ne.jp>,
linux-can@vger.kernel.org
Cc: "Marc Kleine-Budde" <mkl@pengutronix.de>,
"Oliver Hartkopp" <socketcan@hartkopp.net>,
"Vincent Mailhol" <mailhol@kernel.org>,
"Oleksij Rempel" <o.rempel@pengutronix.de>
Subject: Re: [PATCH v13 6/6] can: j1939: fix possibility of hard-lockup/deadlock in j1939_ecu_timer_cancel()
Date: Sun, 15 Nov 2026 05:51:17 +0000 [thread overview]
Message-ID: <sashiko-outbox-215235@kernel.org> (raw)
In-Reply-To: <dc9d8b6a-5e6c-4392-9b6a-f5f546bfe872@I-love.SAKURA.ne.jp>
Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [Medium] can: j1939: j1939_ac_process() drops new claim and leaves
stale addr on cancel
- [Medium] can: j1939: j1939_ecu_timer_start() races with finishing
__run_hrtimer()
--
commit d92351ad8ae3e165027e4084ec974a0caa02eabc
Author: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
can: j1939: fix possibility of hard-lockup/deadlock in j1939_ecu_timer_cancel()
This commit replaces hrtimer_cancel() in j1939_ecu_timer_cancel() with
hrtimer_try_to_cancel() and two boolean flags to avoid waiting for the
timer callback while holding priv->lock.
> diff --git a/net/can/j1939/address-claim.c b/net/can/j1939/address-claim.c
> index ca4ad6cdd5cbf..eb5916ac97bb4 100644
> --- a/net/can/j1939/address-claim.c
> +++ b/net/can/j1939/address-claim.c
> @@ -211,8 +211,11 @@ static void j1939_ac_process(struct j1939_priv *priv, struct sk_buff *skb)
> if (IS_ERR_OR_NULL(ecu))
> goto out_unlock_bh;
>
> - /* cancel pending (previous) address claim */
> - j1939_ecu_timer_cancel(ecu);
> + /* Cancel pending (previous) address claim. If we can't cancel,
> + * do nothing until pending (previous) address claim commits or aborts.
> + */
> + if (!j1939_ecu_timer_cancel(ecu))
> + goto out_ecu_put;
[Severity: Medium]
Does returning early here while setting ecu->abort_ac_handler = true drop
both the expiring address claim and the newly received replacement claim?
CPU0 CPU1
j1939_ecu_timer_handler()
/* waiting on priv->lock */
j1939_ac_process() /* new claim for SA2 */
write_lock_bh(&priv->lock)
j1939_ecu_timer_cancel(ecu)
hrtimer_try_to_cancel() -> -1
ecu->abort_ac_handler = true
return false
goto out_ecu_put <-- drops SA2 claim
write_unlock_bh(&priv->lock)
write_lock_bh(&priv->lock)
if (ecu->abort_ac_handler)
goto out <-- aborts SA1 claim without mapping
j1939_ecu_put(ecu)
Because SAE J1939 address claim messages are one-shot announcements,
neither SA1 nor SA2 is claimed. For a remote ECU (ecu->nusers == 0),
j1939_ecu_put() in the timer handler drops the last reference and frees
the struct j1939_ecu. For a surviving ECU (such as a local ECU with
ecu->nusers > 0), ecu remains unmapped with no running timer while
ecu->addr still holds SA1, so any later claim for SA1 hits the early check
in j1939_ac_process() and is ignored:
net/can/j1939/address-claim.c:j1939_ac_process() {
...
if (ecu && ecu->addr == skcb->addr.sa) {
...
goto out_ecu_put;
}
...
}
> diff --git a/net/can/j1939/bus.c b/net/can/j1939/bus.c
> index 9b40f37546233..6520c91a6e1ec 100644
> --- a/net/can/j1939/bus.c
> +++ b/net/can/j1939/bus.c
> @@ -156,15 +156,35 @@ void j1939_ecu_timer_start(struct j1939_ecu *ecu)
> return;
>
> /* Schedule timer in 250 msec to commit address change. */
> + ecu->abort_ac_handler = false;
> + ecu->done_ac_handler = false;
> hrtimer_start(&ecu->ac_timer, ms_to_ktime(250),
> HRTIMER_MODE_REL_SOFT);
> }
>
> -void j1939_ecu_timer_cancel(struct j1939_ecu *ecu)
> +bool j1939_ecu_timer_cancel(struct j1939_ecu *ecu)
> {
> - if (hrtimer_cancel(&ecu->ac_timer))
> + switch (hrtimer_try_to_cancel(&ecu->ac_timer)) {
> + case 0: /* Not active or already finished. */
> + break;
> + case 1: /* Successfully canceled. */
> /* Drop the ref from j1939_ecu_timer_start(). */
> j1939_ecu_put(ecu);
> + break;
> + default: /* Currently executing. */
> + /* OK to call j1939_ecu_timer_start() again if
> + * j1939_ecu_timer_handler() already committed or aborted.
> + */
> + if (ecu->done_ac_handler)
> + break;
> + /* Tell j1939_ecu_timer_handler() to abort. */
> + ecu->abort_ac_handler = true;
> + /* Don't call j1939_ecu_timer_start() until
> + * j1939_ecu_timer_handler() commits or aborts.
> + */
> + return false;
> + }
> + return true;
> }
>
> static enum hrtimer_restart j1939_ecu_timer_handler(struct hrtimer *hrtimer)
> @@ -174,14 +194,28 @@ static enum hrtimer_restart j1939_ecu_timer_handler(struct hrtimer *hrtimer)
> struct j1939_priv *priv = ecu->priv;
>
> write_lock_bh(&priv->lock);
> +
> + /* Abort if j1939_ecu_timer_cancel() told me to abort. */
> + if (ecu->abort_ac_handler) {
> + ecu->abort_ac_handler = false;
> + goto out;
> + }
> +
> /* TODO: can we test if ecu->addr is unicast before starting
> * the timer?
> */
> j1939_ecu_map_locked(ecu);
>
> +out:
> + /* Tell j1939_ecu_timer_cancel() that this handler has finished
> + * its critical section (either committed or aborted), so no need
> + * to set abort_ac_handler again while __run_hrtimer() is finishing.
> + */
> + ecu->done_ac_handler = true;
> + write_unlock_bh(&priv->lock);
> +
> /* Drop the ref from j1939_ecu_timer_start(). */
> j1939_ecu_put(ecu);
> - write_unlock_bh(&priv->lock);
>
> return HRTIMER_NORESTART;
> }
[Severity: Medium]
Can allowing j1939_ecu_timer_start() while __run_hrtimer() is still
finishing cause a subsequent j1939_ecu_timer_cancel() to fail to cancel
the newly started timer and Poison it with ecu->abort_ac_handler = true?
__run_hrtimer() keeps base->running set to &ecu->ac_timer until after
j1939_ecu_timer_handler() returns and cpu_base->lock is re-acquired. If
two address claim frames arrive after write_unlock_bh(&priv->lock) but
before __run_hrtimer() clears base->running:
CPU0 CPU1
__run_hrtimer()
j1939_ecu_timer_handler()
ecu->done_ac_handler = true
write_unlock_bh(&priv->lock)
j1939_ac_process() /* frame 1 */
j1939_ecu_timer_cancel(ecu)
hrtimer_try_to_cancel() -> -1
/* done_ac_handler is true */
return true
j1939_ecu_timer_start(ecu)
ecu->done_ac_handler = false
hrtimer_start(&ecu->ac_timer, ...)
j1939_ac_process() /* frame 2 */
j1939_ecu_timer_cancel(ecu)
hrtimer_try_to_cancel() -> -1
/* done_ac_handler is now false */
ecu->abort_ac_handler = true
return false <-- drops frame 2
j1939_ecu_put(ecu)
base->running = NULL
Because hrtimer_callback_running(&ecu->ac_timer) is still true on CPU0,
hrtimer_try_to_cancel() on frame 2 returns -1 without dequeuing frame 1's
newly started timer. With ecu->done_ac_handler cleared by frame 1,
j1939_ecu_timer_cancel() sets ecu->abort_ac_handler = true and drops frame
2, while frame 1's timer remains queued for 250 ms only to abort when it
fires.
--
Sashiko AI review · https://sashiko.dev/#/patchset/7cc08b0f-290e-464b-ab13-0a5ce3c57f4f@I-love.SAKURA.ne.jp?part=6
prev parent reply other threads:[~2026-11-15 5:51 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-15 5:34 [PATCH v13 0/6] can: j1939: make module more robust Tetsuo Handa
2026-11-15 5:34 ` [PATCH v13 1/6] can: j1939: fix possibility of UAF on module unload Tetsuo Handa
2026-11-15 5:34 ` [PATCH v13 2/6] can: j1939: add a safeguard to __j1939_priv_release_work() Tetsuo Handa
2026-11-15 5:35 ` [PATCH v13 3/6] can: j1939: fix possibility of list corruption in __j1939_ecu_release() Tetsuo Handa
2026-11-15 5:35 ` [PATCH v13 4/6] can: j1939: add a safeguard to __j1939_ecu_release_work() Tetsuo Handa
2026-11-15 5:35 ` [PATCH v13 5/6] can: j1939: fix "struct j1939_ecu" refcount leak Tetsuo Handa
2026-11-15 5:36 ` [PATCH v13 6/6] can: j1939: fix possibility of hard-lockup/deadlock in j1939_ecu_timer_cancel() Tetsuo Handa
2026-11-15 5:51 ` sashiko-bot [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-215235@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=linux-can@vger.kernel.org \
--cc=mailhol@kernel.org \
--cc=mkl@pengutronix.de \
--cc=o.rempel@pengutronix.de \
--cc=penguin-kernel@I-love.SAKURA.ne.jp \
--cc=sashiko-reviews@lists.linux.dev \
--cc=socketcan@hartkopp.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.