Linux CAN drivers development
 help / color / mirror / Atom feed
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

  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