From: sashiko-bot@kernel.org
To: "Zihan Xi" <zihanx@nebusec.ai>, 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 net v5 1/3] can: j1939: fix TX timer reference accounting
Date: Tue, 03 Nov 2026 05:48:20 +0000 [thread overview]
Message-ID: <sashiko-outbox-194598@kernel.org> (raw)
In-Reply-To: <fe402cff5706b3162d4e8aec02cc62dd7fa0b303.1793681851.git.zihanx@nebusec.ai>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] can: j1939: race in j1939_session_txtimer_cancel_async_locked() drops ref
--
commit f9122b0d36d586b125a26171c0a02491b3e5aab6
Author: Zihan Xi <zihanx@nebusec.ai>
can: j1939: fix TX timer reference accounting
This commit reworks J1939 session TX timer reference accounting so a single
kref reference covers the queued and running lifetime of session->txtimer,
adding synchronization around timer arming, cancellation, and callback
completion.
Link: https://bugtracker.nebusec.ai/f/11143
> diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c
> index 8fcfd13e5e6f8..4958f7c47ca44 100644
> --- a/net/can/j1939/transport.c
> +++ b/net/can/j1939/transport.c
> @@ -301,9 +301,78 @@ void j1939_session_put(struct j1939_session *session)
> kref_put(&session->kref, __j1939_session_release);
> }
>
> -static void j1939_session_txtimer_cancel(struct j1939_session *session)
> +static void j1939_session_txtimer_cancel(struct j1939_session *session,
> + bool shutdown)
> {
> - if (hrtimer_cancel(&session->txtimer))
> + bool put_ref = false;
> + int ret;
> +
> + spin_lock_bh(&session->txtimer_lock);
> + session->txtimer_cancelers++;
> + if (shutdown)
> + session->txtimer_shutdown = true;
> + ret = hrtimer_try_to_cancel(&session->txtimer);
> + if (ret == 1) {
> + if (!WARN_ON_ONCE(!session->txtimer_ref)) {
> + session->txtimer_ref = false;
> + put_ref = true;
> + }
> + }
> + spin_unlock_bh(&session->txtimer_lock);
> +
> + /* Never wait for a softirq callback while holding txtimer_lock. */
> + if (ret < 0) {
> + ret = hrtimer_cancel(&session->txtimer);
> + if (ret == 1) {
> + spin_lock_bh(&session->txtimer_lock);
> + if (!WARN_ON_ONCE(!session->txtimer_ref)) {
> + session->txtimer_ref = false;
> + put_ref = true;
> + }
> + spin_unlock_bh(&session->txtimer_lock);
> + }
> + }
> + spin_lock_bh(&session->txtimer_lock);
> + if (WARN_ON_ONCE(!session->txtimer_cancelers))
> + session->txtimer_cancelers = 0;
> + else
> + session->txtimer_cancelers--;
> + spin_unlock_bh(&session->txtimer_lock);
> +
> + if (put_ref)
> + j1939_session_put(session);
> +}
> +
> +static bool
> +j1939_session_txtimer_cancel_async_locked(struct j1939_session *session)
> +{
> + int ret;
> +
> + lockdep_assert_held(&session->txtimer_lock);
> + session->txtimer_shutdown = true;
> + ret = hrtimer_try_to_cancel(&session->txtimer);
> + /* Let a queued successor finish promptly after callback exit. */
> + if (ret < 0 && session->txtimer_ref &&
> + hrtimer_is_queued(&session->txtimer))
> + hrtimer_start(&session->txtimer, 0, HRTIMER_MODE_REL_SOFT);
[Severity: Critical]
Can j1939_session_txtimer_cancel_async_locked() race with a concurrent
hrtimer_cancel() in j1939_session_txtimer_cancel() and leave session->txtimer
queued without a session reference?
Unlike j1939_tp_schedule_txtimer(), j1939_session_txtimer_cancel_async_locked()
does not check session->txtimer_cancelers before calling hrtimer_start(), and
hrtimer_cancel() in j1939_session_txtimer_cancel() runs without holding
session->txtimer_lock.
If j1939_tp_txtimer() on CPU 0 has re-armed a successor timer and is exiting
after releasing session->txtimer_lock:
CPU 0 (j1939_tp_txtimer)
queued = hrtimer_is_queued(&session->txtimer); /* true, keeps txtimer_ref */
spin_unlock_bh(&session->txtimer_lock);
return HRTIMER_NORESTART;
/* still running in __run_hrtimer() before clearing base->running */
CPU 1 (j1939_session_txtimer_cancel)
spin_lock_bh(&session->txtimer_lock);
session->txtimer_cancelers++;
ret = hrtimer_try_to_cancel(&session->txtimer); /* returns -1 */
spin_unlock_bh(&session->txtimer_lock);
ret = hrtimer_cancel(&session->txtimer);
CPU 2 (j1939_session_txtimer_cancel_async_locked)
/* holds session->txtimer_lock */
ret = hrtimer_try_to_cancel(&session->txtimer); /* returns -1 */
if (ret < 0 && session->txtimer_ref &&
hrtimer_is_queued(&session->txtimer)) /* evaluates true */
If CPU 0 then clears base->running in __run_hrtimer() and CPU 1's
hrtimer_cancel() dequeues the pending successor timer and returns 1 before
CPU 2 calls hrtimer_start():
CPU 2
hrtimer_start(&session->txtimer, 0, HRTIMER_MODE_REL_SOFT);
/* re-queues session->txtimer and returns false (ret == -1) */
CPU 1
if (ret == 1) {
spin_lock_bh(&session->txtimer_lock);
session->txtimer_ref = false;
put_ref = true;
spin_unlock_bh(&session->txtimer_lock);
}
...
j1939_session_put(session);
Wouldn't this leave session->txtimer queued in the hrtimer subsystem with
session->txtimer_ref set to false and its kref dropped, leading to a
WARN_ON_ONCE(!session->txtimer_ref) and a use-after-free of struct
j1939_session when j1939_tp_txtimer() fires? Should
j1939_session_txtimer_cancel_async_locked() avoid calling hrtimer_start() when
session->txtimer_cancelers is non-zero (since the synchronous canceler in
j1939_session_txtimer_cancel() is already waiting to cancel the timer)?
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1793681851.git.zihanx@nebusec.ai?part=1
next prev parent reply other threads:[~2026-11-03 5:48 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-03 5:24 [PATCH net v5 0/3] can: j1939: fix TX timer reference leak Zihan Xi
2026-11-03 5:24 ` [PATCH net v5 1/3] can: j1939: fix TX timer reference accounting Zihan Xi
2026-11-03 5:48 ` sashiko-bot [this message]
2026-11-04 18:38 ` netdev-bot+sashiko
2026-11-03 5:24 ` [PATCH net v5 2/3] can: j1939: cancel TX timer after RX timeout Zihan Xi
2026-11-03 5:54 ` sashiko-bot
2026-11-04 18:38 ` netdev-bot+sashiko
2026-11-03 5:24 ` [PATCH net v5 3/3] can: j1939: serialize session TX with state changes Zihan Xi
2026-11-03 5:46 ` sashiko-bot
2026-11-04 18:38 ` netdev-bot+sashiko
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-194598@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=sashiko-reviews@lists.linux.dev \
--cc=socketcan@hartkopp.net \
--cc=zihanx@nebusec.ai \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox