From: sashiko-bot@kernel.org
To: "Oliver Hartkopp" <socketcan@hartkopp.net>
Cc: "Oliver Hartkopp" <socketcan@hartkopp.net>,
linux-can@vger.kernel.org, "Vincent Mailhol" <mailhol@kernel.org>,
"Oleksij Rempel" <o.rempel@pengutronix.de>,
"Marc Kleine-Budde" <mkl@pengutronix.de>
Subject: Re: [PATCH v9 3/4] can: isotp: fix race between RX/TX timers and frame reception
Date: Sun, 12 Jul 2026 13:58:36 +0000 [thread overview]
Message-ID: <20260712135836.B28FE1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260712-isotp-fixes-v9-3-137840d7f0f4@hartkopp.net>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] The lock-free state claim mechanism in isotp_sendmsg() races with the generation bump, causing state clobbering that leads to concurrent buffer corruption and an Out-Of-Bounds (OOB) read leaking kernel memory to the CAN bus.
--
--- Patch [3]: [PATCH v9 3/4] can: isotp: fix race between RX/TX timers and frame reception ---
commit b972a6bcac8d8b55930b5c1372f223dd7d9a46de
Author: Oliver Hartkopp <socketcan@hartkopp.net>
can: isotp: fix race between RX/TX timers and frame reception
This commit attempts to fix a race by introducing a generation counter
(tx_gen) to distinguish between old and new TX transfers. It also expands
rx_lock to cover the TX side for proper synchronization.
Link: https://lore.kernel.org/linux-can/20260710142146.BDAE61F000E9@smtp.kernel.org/
> diff --git a/net/can/isotp.c b/net/can/isotp.c
> index 44c044eb83e12..c4ca8a64e7ce1 100644
> --- a/net/can/isotp.c
> +++ b/net/can/isotp.c
> @@ -870,22 +888,42 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
[ ... ]
> + /* so->rx_lock stops a new claim from arming a timer of its own
> + * while we're still here, but not from the lock-free cmpxchg()
> + * claim itself: state alone can't tell our SENDING from a new
> + * transfer's, so also gate on gen.
> + */
> + if (so->tx.state != ISOTP_SENDING || READ_ONCE(so->tx_gen) != gen)
> + goto out_unlock;
[Severity: Critical]
Does the echo receiver similarly validate against a stale tx_gen if a new
transfer is blocked waiting for rx_lock?
If the generation hasn't been bumped yet, it appears this could process echoes
for the wrong transfer and inadvertently clobber its state.
> @@ -980,6 +1050,24 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
> goto err_event_drop;
> }
>
> + /* new transfer: bump the generation (gen is used below and by
> + * isotp_tx_timeout()/isotp_rcv_echo()) and drain the old one -
> + * cancel its timers ourselves in case one is still concurrently
> + * completing it, and take so->rx_lock so no isotp_rcv_fc()/
> + * isotp_rcv_echo() call for it is still in flight. The bump and
> + * the so->cfecho reset happen in the same so->rx_lock section so
> + * isotp_rcv_echo() can never observe the new gen alongside the
> + * old, not-yet-cleared cfecho tag.
> + */
> + spin_lock_bh(&so->rx_lock);
> + gen = READ_ONCE(so->tx_gen) + 1;
> + WRITE_ONCE(so->tx_gen, gen);
[Severity: Critical]
Because cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SENDING) happens before
this lock is acquired, is there a window where a new transfer has claimed the
state but hasn't bumped tx_gen yet?
> @@ -1142,14 +1231,36 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
>
> return size;
>
> +err_out_drop:
> + spin_lock_bh(&so->rx_lock);
> + goto err_out_drop_locked;
> err_event_drop:
[ ... ]
> +err_out_drop_locked:
> + /* so->rx_lock stops a new claim from cancelling anything of its own
> + * while we're still here, but not from the lock-free cmpxchg()
> + * claim itself; only drop our own PDU if that hasn't happened
> + */
> + if (READ_ONCE(so->tx_gen) == gen) {
> + so->cfecho = 0;
> + so->tx.state = ISOTP_IDLE;
> + }
> + spin_unlock_bh(&so->rx_lock);
> wake_up_interruptible(&so->wait);
>
> return err;
[Severity: Critical]
Can this lock-free claim in isotp_sendmsg() race with the generation bump and
cause state clobbering?
If Thread A is in err_event_drop() and holds rx_lock, and Thread C (a timeout
timer executing isotp_tx_timeout()) locklessly sets the state to ISOTP_IDLE:
Thread C (isotp_tx_timeout)
cmpxchg(&so->tx.state, old_state, ISOTP_IDLE);
Then Thread B calls isotp_sendmsg(), sees ISOTP_IDLE, claims it to
ISOTP_SENDING locklessly, and blocks on rx_lock:
Thread B (isotp_sendmsg)
cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SENDING);
...
spin_lock_bh(&so->rx_lock); // blocks
When Thread A resumes in err_out_drop_locked, it checks tx_gen. Since Thread
B is blocked, tx_gen hasn't been bumped yet. Will Thread A then incorrectly
pass the check and reset the state to ISOTP_IDLE?
Thread A (err_out_drop_locked)
if (READ_ONCE(so->tx_gen) == gen) {
so->tx.state = ISOTP_IDLE;
}
Once Thread A drops the lock, Thread B will bump tx_gen and start writing to
tx.buf. But because the state is now ISOTP_IDLE, Thread D could successfully
call isotp_sendmsg() and concurrently claim the socket.
Could this lead to Threads B and D concurrently modifying tx.idx in
isotp_fill_dataframe(), causing an out-of-bounds read that leaks adjacent
kernel memory over the CAN bus?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260712-isotp-fixes-v9-0-137840d7f0f4@hartkopp.net?part=3
next prev parent reply other threads:[~2026-07-12 13:58 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-12 13:44 [PATCH v9 0/4] net: can: isotp-fixes Oliver Hartkopp via B4 Relay
2026-07-12 13:44 ` [PATCH v9 1/4] can: isotp: use unconditional synchronize_rcu() in isotp_release() Oliver Hartkopp via B4 Relay
2026-07-12 14:03 ` sashiko-bot
2026-07-12 13:44 ` [PATCH v9 2/4] can: isotp: fix use-after-free race with concurrent NETDEV_UNREGISTER Oliver Hartkopp via B4 Relay
2026-07-12 13:44 ` [PATCH v9 3/4] can: isotp: fix race between RX/TX timers and frame reception Oliver Hartkopp via B4 Relay
2026-07-12 13:58 ` sashiko-bot [this message]
2026-07-12 13:44 ` [PATCH v9 4/4] can: isotp: fix state machine corruption on signal interruption Oliver Hartkopp via B4 Relay
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=20260712135836.B28FE1F000E9@smtp.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 \
/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