* [PATCH v6 0/5] net: can: isotp-fixes
@ 2026-07-10 20:42 Oliver Hartkopp via B4 Relay
2026-07-10 20:42 ` [PATCH v6 1/5] can: isotp: use unconditional synchronize_rcu() in isotp_release() Oliver Hartkopp via B4 Relay
` (4 more replies)
0 siblings, 5 replies; 11+ messages in thread
From: Oliver Hartkopp via B4 Relay @ 2026-07-10 20:42 UTC (permalink / raw)
To: linux-can; +Cc: Oliver Hartkopp, Marc Kleine-Budde, Nico Yip, sashiko-bot
As sashiko-bot was not able to check the second patch this bundle is
re-posted with b4 preparation.
Fixed the remarks reported by sashiko-bot of V5 patchset.
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
---
Changes in v6:
- patch 2: check for rx state and tx state to be IDLE in isotp_bind() (sashiko-bot)
- patch 3: double check rx/tx states after all hrtimer_cancel() calls (sashiko-bot)
move ISOTP_IDLE in err_event_drop for the same reason
- patch 4: use READ_ONCE() to prevent compiler load tearing as remarked by sashiko-bot
- Link to v5: https://patch.msgid.link/20260710-isotp-fixes-v5-0-15174d82d6f1@hartkopp.net
Changes in v5:
- patch 3: new: state change re-check after hrtimer_cancel()
- patch 4: new: handle state transistion with cmpxchg()
- Link to v4: https://patch.msgid.link/20260710-isotp-fixes-v4-0-2a4af437f61b@hartkopp.net
Changes in v4:
- patch 2: use a simpler method to reject a re-binding that can be implemented
inside the lock: No waitqueue waiting just exit.
- Link to v3: https://patch.msgid.link/20260710-isotp-fixes-v3-0-08db68e27d0b@hartkopp.net
Changes in v3:
- patch 2: go back to initial idea without hard resetting the state machine and
timers but let these come to a graceful end. Even a new bind() at netdev
shutdown now leads to this graceful statemachine shutdown via the wait queue.
- Link to v2: https://patch.msgid.link/20260710-isotp-fixes-v2-0-bc57e26594b2@hartkopp.net
Changes in v2:
- patch 2: shutdown state machine to fix sashiko-bot complains
Link: https://lore.kernel.org/linux-can/20260710094807.A50DD1F000E9@smtp.kernel.org/
- Link to v1: https://patch.msgid.link/20260710-isotp-fixes-v1-0-75a1d11d7df9@hartkopp.net
---
Oliver Hartkopp (5):
can: isotp: use unconditional synchronize_rcu() in isotp_release()
can: isotp: fix use-after-free race with concurrent NETDEV_UNREGISTER
can: isotp: fix race between RX/TX timers and frame reception
can: isotp: fix lock-free state transition in tx timer handler
can: isotp: fix state machine corruption on signal interruption
net/can/isotp.c | 137 +++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 102 insertions(+), 35 deletions(-)
---
base-commit: a635d6748234582ea287c5ffeae28b9b23f91c7e
change-id: 20260710-isotp-fixes-ead7480d67bf
Best regards,
--
Oliver Hartkopp <socketcan@hartkopp.net>
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v6 1/5] can: isotp: use unconditional synchronize_rcu() in isotp_release() 2026-07-10 20:42 [PATCH v6 0/5] net: can: isotp-fixes Oliver Hartkopp via B4 Relay @ 2026-07-10 20:42 ` Oliver Hartkopp via B4 Relay 2026-07-10 20:58 ` sashiko-bot 2026-07-10 20:42 ` [PATCH v6 2/5] can: isotp: fix use-after-free race with concurrent NETDEV_UNREGISTER Oliver Hartkopp via B4 Relay ` (3 subsequent siblings) 4 siblings, 1 reply; 11+ messages in thread From: Oliver Hartkopp via B4 Relay @ 2026-07-10 20:42 UTC (permalink / raw) To: linux-can; +Cc: Oliver Hartkopp, Marc Kleine-Budde, Nico Yip From: Oliver Hartkopp <socketcan@hartkopp.net> isotp_notify() unregisters the (RCU) CAN filters via can_rx_unregister() and clears so->bound without waiting for a grace period. isotp_release() uses so->bound to decide whether it needs to call synchronize_rcu() before cancelling so->rxtimer, so when NETDEV_UNREGISTER runs first it skips that synchronize_rcu() and can cancel the timer while an in-flight isotp_rcv() is still executing and about to re-arm it via isotp_send_fc(), leading to a use-after-free timer callback on the freed socket. sakisho-bot remarked a problem with rtnl_lock held in isotp_notify(), therefore make isotp_release() always call synchronize_rcu() before cancelling the timers, regardless of so->bound. This still closes the original race (isotp_notify() clearing so->bound without waiting for in-flight isotp_rcv() callers before isotp_release() cancels the RX timer) without adding any RCU wait to the netdevice notifier path. Fixes: 14a4696bc311 ("can: isotp: isotp_release(): omit unintended hrtimer restart on socket release") Closes: https://lore.kernel.org/linux-can/20260707085210.6B6C01F000E9@smtp.kernel.org/ Reported-by: Nico Yip <zdi-disclosures@trendmicro.com> (ZDI-CAN-31764) Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net> --- net/can/isotp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index c48b4a818297..d30937345bcd 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -1235,15 +1235,22 @@ static int isotp_release(struct socket *sock) can_rx_unregister(net, dev, so->txid, SINGLE_MASK(so->txid), isotp_rcv_echo, sk); dev_put(dev); - synchronize_rcu(); } } } + /* Always wait for a grace period before touching the timers below. + * A concurrent NETDEV_UNREGISTER may have already unregistered our + * filters and cleared so->bound in isotp_notify() without waiting + * for in-flight isotp_rcv() callers to finish, so this call must not + * be skipped just because so->bound is already 0 here. + */ + synchronize_rcu(); + hrtimer_cancel(&so->txfrtimer); hrtimer_cancel(&so->txtimer); hrtimer_cancel(&so->rxtimer); so->ifindex = 0; -- 2.53.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v6 1/5] can: isotp: use unconditional synchronize_rcu() in isotp_release() 2026-07-10 20:42 ` [PATCH v6 1/5] can: isotp: use unconditional synchronize_rcu() in isotp_release() Oliver Hartkopp via B4 Relay @ 2026-07-10 20:58 ` sashiko-bot 0 siblings, 0 replies; 11+ messages in thread From: sashiko-bot @ 2026-07-10 20:58 UTC (permalink / raw) To: Oliver Hartkopp Cc: Marc Kleine-Budde, Vincent Mailhol, Oliver Hartkopp, Oleksij Rempel, linux-can Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: Pre-existing issues: - [High] Race conditions between isotp_release/isotp_bind and NETDEV_UNREGISTER cause CAN filters to be leaked, leading to a Use-After-Free of the socket. -- --- Patch [1]: [PATCH v6 1/5] can: isotp: use unconditional synchronize_rcu() in isotp_release() --- commit 9e3b5ef819dd616609004db7956c6318a9ae781a Author: Oliver Hartkopp <socketcan@hartkopp.net> can: isotp: use unconditional synchronize_rcu() in isotp_release() isotp_notify() unregisters the (RCU) CAN filters via can_rx_unregister() and clears so->bound without waiting for a grace period. isotp_release() uses so->bound to decide whether it needs to call synchronize_rcu() before cancelling so->rxtimer, so when NETDEV_UNREGISTER runs first it skips that synchronize_rcu() and can cancel the timer while an in-flight isotp_rcv() is still executing and about to re-arm it via isotp_send_fc(), leading to a use-after-free timer callback on the freed socket. sakisho-bot remarked a problem with rtnl_lock held in isotp_notify(), therefore make isotp_release() always call synchronize_rcu() before cancelling the timers, regardless of so->bound. This still closes the original race (isotp_notify() clearing so->bound without waiting for in-flight isotp_rcv() callers before isotp_release() cancels the RX timer) without adding any RCU wait to the netdevice notifier path. Fixes: 14a4696bc311 ("can: isotp: isotp_release(): omit unintended hrtimer restart on socket release") Closes: https://lore.kernel.org/linux-can/20260707085210.6B6C01F000E9@smtp.kernel.org/ Reported-by: Nico Yip <zdi-disclosures@trendmicro.com> (ZDI-CAN-31764) > diff --git a/net/can/isotp.c b/net/can/isotp.c > index c48b4a818297e..d30937345bcdc 100644 > --- a/net/can/isotp.c > +++ b/net/can/isotp.c > @@ -1237,11 +1237,18 @@ static int isotp_release(struct socket *sock) > SINGLE_MASK(so->txid), > isotp_rcv_echo, sk); > dev_put(dev); > - synchronize_rcu(); > } > } > } > > + /* Always wait for a grace period before touching the timers below. > + * A concurrent NETDEV_UNREGISTER may have already unregistered our > + * filters and cleared so->bound in isotp_notify() without waiting > + * for in-flight isotp_rcv() callers to finish, so this call must not > + * be skipped just because so->bound is already 0 here. > + */ > + synchronize_rcu(); > + [Severity: High] This is a pre-existing issue, but can race conditions between isotp_release() or isotp_bind() and NETDEV_UNREGISTER cause CAN filters to be leaked, potentially leading to a use-after-free of the socket? If a user creates a CAN interface, binds an ISOTP socket, and concurrently unregisters the interface while closing the socket, isotp_release() removes the socket from the notifier list: net/can/isotp.c:isotp_release() { ... list_del(&so->notifier); spin_unlock(&isotp_notifier_lock); lock_sock(sk); ... } This blinds the NETDEV_UNREGISTER notifier. If isotp_release() executes before isotp_notify(), the notifier skips the socket. When isotp_release() then tries to clean up the filters, if the netdevice is already unlisted, it might skip the can_rx_unregister() call. The socket would then be freed, but the active CAN filter could still point to it. A similar window exists in isotp_bind(), where the filters are registered before so->ifindex is updated: net/can/isotp.c:isotp_bind() { ... if (isotp_register_rxid(so)) can_rx_register(net, dev, rx_id, SINGLE_MASK(rx_id), ...); can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id), ...); dev_put(dev); /* switch to new settings */ so->ifindex = ifindex; so->rxid = rx_id; ... } If a concurrent NETDEV_UNREGISTER runs during this window, isotp_notify() ignores the socket because it locklessly checks if (so->ifindex != dev->ifindex), bypassing the unregister and leaking the filter when the socket is later released. Could this allow a local unprivileged user utilizing virtual CAN (vcan) devices to trigger a use-after-free? > hrtimer_cancel(&so->txfrtimer); > hrtimer_cancel(&so->txtimer); > hrtimer_cancel(&so->rxtimer); -- Sashiko AI review · https://sashiko.dev/#/patchset/20260710-isotp-fixes-v6-0-a13c2539093d@hartkopp.net?part=1 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v6 2/5] can: isotp: fix use-after-free race with concurrent NETDEV_UNREGISTER 2026-07-10 20:42 [PATCH v6 0/5] net: can: isotp-fixes Oliver Hartkopp via B4 Relay 2026-07-10 20:42 ` [PATCH v6 1/5] can: isotp: use unconditional synchronize_rcu() in isotp_release() Oliver Hartkopp via B4 Relay @ 2026-07-10 20:42 ` Oliver Hartkopp via B4 Relay 2026-07-10 20:56 ` sashiko-bot 2026-07-10 20:42 ` [PATCH v6 3/5] can: isotp: fix race between RX/TX timers and frame reception Oliver Hartkopp via B4 Relay ` (2 subsequent siblings) 4 siblings, 1 reply; 11+ messages in thread From: Oliver Hartkopp via B4 Relay @ 2026-07-10 20:42 UTC (permalink / raw) To: linux-can; +Cc: Oliver Hartkopp, Marc Kleine-Budde, sashiko-bot From: Oliver Hartkopp <socketcan@hartkopp.net> isotp_release() looked up the bound network device via dev_get_by_index() using the stored ifindex. During device unregistration the device is unlisted from the ifindex hash before the NETDEV_UNREGISTER notifier chain runs, so a concurrent isotp_release() could find no device, skip can_rx_unregister() entirely, and still proceed to free the socket. Since isotp_release() had already removed itself from the isotp notifier list at that point, isotp_notify() would never get a chance to clean up either, leaving a stale CAN filter that keeps pointing at the freed socket. Fix this the same way raw.c already does: hold a tracked reference to the bound net_device in the socket (so->dev/so->dev_tracker) from bind() onward instead of re-resolving it from the ifindex, and serialize bind()/release() with rtnl_lock() so that so->dev is always consistent with what the NETDEV_UNREGISTER notifier sees. so->dev stays valid regardless of ifindex-hash unlisting, and is only ever cleared by whichever of isotp_release()/isotp_notify() gets there first, so the filter is always removed exactly once. isotp_bind() now rejects a (re)bind with -EAGAIN while so->[tx|rx].state isn't ISOTP_IDLE yet, so a timer left running by a prior NETDEV_UNREGISTER can't act on a newly bound so->ifindex. Both checks share the same lock_sock() section, so there is no window in which a concurrent isotp_notify() clearing so->bound could be missed. Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol") Reported-by: sashiko-bot@kernel.org Link: https://lore.kernel.org/linux-can/20260707101420.47F261F000E9@smtp.kernel.org/ Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net> --- net/can/isotp.c | 87 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 28 deletions(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index d30937345bcd..44c044eb83e1 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -150,10 +150,12 @@ struct tpcon { struct isotp_sock { struct sock sk; int bound; int ifindex; + struct net_device *dev; + netdevice_tracker dev_tracker; canid_t txid; canid_t rxid; ktime_t tx_gap; ktime_t lastrxcf_tstamp; struct hrtimer rxtimer, txtimer, txfrtimer; @@ -976,10 +978,18 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE); if (err) goto err_event_drop; } + /* so->bound is only checked once above - a wakeup may have + * unbound/rebound the socket meanwhile, so re-validate it + */ + if (!so->bound) { + err = -EADDRNOTAVAIL; + goto err_out_drop; + } + /* PDU size > default => try max_pdu_size */ if (size > so->tx.buflen && so->tx.buflen < max_pdu_size) { u8 *newbuf = kmalloc(max_pdu_size, GFP_KERNEL); if (newbuf) { @@ -1217,32 +1227,34 @@ static int isotp_release(struct socket *sock) spin_lock(&isotp_notifier_lock); } list_del(&so->notifier); spin_unlock(&isotp_notifier_lock); + rtnl_lock(); lock_sock(sk); - /* remove current filters & unregister */ - if (so->bound) { - if (so->ifindex) { - struct net_device *dev; - - dev = dev_get_by_index(net, so->ifindex); - if (dev) { - if (isotp_register_rxid(so)) - can_rx_unregister(net, dev, so->rxid, - SINGLE_MASK(so->rxid), - isotp_rcv, sk); - - can_rx_unregister(net, dev, so->txid, - SINGLE_MASK(so->txid), - isotp_rcv_echo, sk); - dev_put(dev); - } - } + /* remove current filters & unregister + * tracked reference so->dev is taken at bind() time with rtnl_lock + */ + if (so->bound && so->dev) { + if (isotp_register_rxid(so)) + can_rx_unregister(net, so->dev, so->rxid, + SINGLE_MASK(so->rxid), + isotp_rcv, sk); + + can_rx_unregister(net, so->dev, so->txid, + SINGLE_MASK(so->txid), + isotp_rcv_echo, sk); + netdev_put(so->dev, &so->dev_tracker); } + so->ifindex = 0; + so->bound = 0; + so->dev = NULL; + + rtnl_unlock(); + /* Always wait for a grace period before touching the timers below. * A concurrent NETDEV_UNREGISTER may have already unregistered our * filters and cleared so->bound in isotp_notify() without waiting * for in-flight isotp_rcv() callers to finish, so this call must not * be skipped just because so->bound is already 0 here. @@ -1251,13 +1263,10 @@ static int isotp_release(struct socket *sock) hrtimer_cancel(&so->txfrtimer); hrtimer_cancel(&so->txtimer); hrtimer_cancel(&so->rxtimer); - so->ifindex = 0; - so->bound = 0; - sock_orphan(sk); sock->sk = NULL; release_sock(sk); sock_prot_inuse_add(net, sk->sk_prot, -1); @@ -1308,17 +1317,29 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l } if (!addr->can_ifindex) return -ENODEV; + rtnl_lock(); lock_sock(sk); if (so->bound) { err = -EINVAL; goto out; } + /* A transmission or reception that outlived a previous binding + * (unbound by NETDEV_UNREGISTER) may still be draining; the FC/echo + * and RX watchdog timers bound how long this takes. Checked together + * with so->bound in the same lock_sock() section above, so there is + * no window in which a concurrent isotp_notify() could be missed. + */ + if (so->tx.state != ISOTP_IDLE || so->rx.state != ISOTP_IDLE) { + err = -EAGAIN; + goto out; + } + /* ensure different CAN IDs when the rx_id is to be registered */ if (isotp_register_rxid(so) && rx_id == tx_id) { err = -EADDRNOTAVAIL; goto out; } @@ -1327,18 +1348,16 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l if (!dev) { err = -ENODEV; goto out; } if (dev->type != ARPHRD_CAN) { - dev_put(dev); err = -ENODEV; - goto out; + goto out_put_dev; } if (READ_ONCE(dev->mtu) < so->ll.mtu) { - dev_put(dev); err = -EINVAL; - goto out; + goto out_put_dev; } if (!(dev->flags & IFF_UP)) notify_enetdown = 1; ifindex = dev->ifindex; @@ -1352,20 +1371,29 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l /* register for echo skb's */ can_rx_register(net, dev, tx_id, SINGLE_MASK(tx_id), isotp_rcv_echo, sk, "isotpe", sk); - dev_put(dev); - /* switch to new settings */ so->ifindex = ifindex; so->rxid = rx_id; so->txid = tx_id; so->bound = 1; + /* bind() ok -> hold a reference for so->dev so that isotp_release() + * can safely reach the device later, even if a concurrent + * NETDEV_UNREGISTER has already unlisted it by ifindex. + */ + so->dev = dev; + netdev_hold(so->dev, &so->dev_tracker, GFP_KERNEL); + +out_put_dev: + /* remove potential reference from dev_get_by_index() */ + dev_put(dev); out: release_sock(sk); + rtnl_unlock(); if (notify_enetdown) { sk->sk_err = ENETDOWN; if (!sock_flag(sk, SOCK_DEAD)) sk_error_report(sk); @@ -1564,11 +1592,11 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg, struct sock *sk = &so->sk; if (!net_eq(dev_net(dev), sock_net(sk))) return; - if (so->ifindex != dev->ifindex) + if (so->dev != dev) return; switch (msg) { case NETDEV_UNREGISTER: lock_sock(sk); @@ -1580,14 +1608,16 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg, isotp_rcv, sk); can_rx_unregister(dev_net(dev), dev, so->txid, SINGLE_MASK(so->txid), isotp_rcv_echo, sk); + netdev_put(so->dev, &so->dev_tracker); } so->ifindex = 0; so->bound = 0; + so->dev = NULL; release_sock(sk); sk->sk_err = ENODEV; if (!sock_flag(sk, SOCK_DEAD)) sk_error_report(sk); @@ -1643,10 +1673,11 @@ static int isotp_init(struct sock *sk) { struct isotp_sock *so = isotp_sk(sk); so->ifindex = 0; so->bound = 0; + so->dev = NULL; so->opt.flags = CAN_ISOTP_DEFAULT_FLAGS; so->opt.ext_address = CAN_ISOTP_DEFAULT_EXT_ADDRESS; so->opt.rx_ext_address = CAN_ISOTP_DEFAULT_EXT_ADDRESS; so->opt.rxpad_content = CAN_ISOTP_DEFAULT_PAD_CONTENT; -- 2.53.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v6 2/5] can: isotp: fix use-after-free race with concurrent NETDEV_UNREGISTER 2026-07-10 20:42 ` [PATCH v6 2/5] can: isotp: fix use-after-free race with concurrent NETDEV_UNREGISTER Oliver Hartkopp via B4 Relay @ 2026-07-10 20:56 ` sashiko-bot 0 siblings, 0 replies; 11+ messages in thread From: sashiko-bot @ 2026-07-10 20:56 UTC (permalink / raw) To: Oliver Hartkopp Cc: Oliver Hartkopp, Vincent Mailhol, Marc Kleine-Budde, Oleksij Rempel, linux-can Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: Pre-existing issues: - [High] The wait loop error path in `isotp_sendmsg()` resets the transmission state machine despite not owning it, which can silently corrupt another thread's active transmission and lead to an information leak. -- --- Patch [2]: [PATCH v6 2/5] can: isotp: fix use-after-free race with concurrent NETDEV_UNREGISTER --- commit 1c03594f024dd1735bb9c085227d4a04352dcd2f Author: Oliver Hartkopp <socketcan@hartkopp.net> can: isotp: fix use-after-free race with concurrent NETDEV_UNREGISTER This commit fixes a race condition during device unregistration by holding a tracked reference to the bound net_device and serializing bind and release with rtnl_lock(). Link: https://lore.kernel.org/linux-can/20260707101420.47F261F000E9@smtp.kernel.org/ > diff --git a/net/can/isotp.c b/net/can/isotp.c > --- a/net/can/isotp.c > +++ b/net/can/isotp.c > @@ -978,6 +980,14 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) > goto err_event_drop; [Severity: High] This is a pre-existing issue, but does this error path corrupt the state machine for another thread? If Thread A acquires the state (so->tx.state = ISOTP_SENDING) and starts transmission, and Thread B calls isotp_sendmsg(), Thread B will fail the cmpxchg() and block in wait_event_interruptible(). If Thread B receives a signal, it jumps to err_event_drop and forces so->tx.state = ISOTP_IDLE, even though Thread A still owns the active transmission. Could Thread C then call isotp_sendmsg(), acquire the now-IDLE state machine, and begin setting up a new transmission? If a delayed flow control frame for Thread A's transmission arrives, it seems the state machine could mistakenly transmit consecutive frames containing the newly allocated, uninitialized memory from Thread C onto the CAN bus. > } > > + /* so->bound is only checked once above - a wakeup may have > + * unbound/rebound the socket meanwhile, so re-validate it > + */ > + if (!so->bound) { > + err = -EADDRNOTAVAIL; > + goto err_out_drop; > + } -- Sashiko AI review · https://sashiko.dev/#/patchset/20260710-isotp-fixes-v6-0-a13c2539093d@hartkopp.net?part=2 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v6 3/5] can: isotp: fix race between RX/TX timers and frame reception 2026-07-10 20:42 [PATCH v6 0/5] net: can: isotp-fixes Oliver Hartkopp via B4 Relay 2026-07-10 20:42 ` [PATCH v6 1/5] can: isotp: use unconditional synchronize_rcu() in isotp_release() Oliver Hartkopp via B4 Relay 2026-07-10 20:42 ` [PATCH v6 2/5] can: isotp: fix use-after-free race with concurrent NETDEV_UNREGISTER Oliver Hartkopp via B4 Relay @ 2026-07-10 20:42 ` Oliver Hartkopp via B4 Relay 2026-07-10 20:55 ` sashiko-bot 2026-07-10 20:42 ` [PATCH v6 4/5] can: isotp: fix lock-free state transition in tx timer handler Oliver Hartkopp via B4 Relay 2026-07-10 20:42 ` [PATCH v6 5/5] can: isotp: fix state machine corruption on signal interruption Oliver Hartkopp via B4 Relay 4 siblings, 1 reply; 11+ messages in thread From: Oliver Hartkopp via B4 Relay @ 2026-07-10 20:42 UTC (permalink / raw) To: linux-can; +Cc: Oliver Hartkopp, Marc Kleine-Budde, sashiko-bot From: Oliver Hartkopp <socketcan@hartkopp.net> When receiving a Consecutive Frame, Flow Control frame, or local echo frame, hrtimer_cancel() is called to stop the corresponding watchdog timer. If the timer handler was already running concurrently on another CPU, hrtimer_cancel() waits for it to finish. By the time it returns, the timer handler may have already reported a timeout error and reset the state machine. Blindly continuing corrupts the newly reset or idle state, and can let a concurrent sendmsg() claim the same IDLE state at the same time. Fix this by re-checking the state right after canceling the timer in all three call sites (isotp_rcv_cf(), isotp_rcv_fc(), isotp_rcv_echo()). If the state changed, the transfer has already timed out, so drop the frame instead of resuming it. For the same reason, isotp_sendmsg()'s err_event_drop path must cancel so->txfrtimer/txtimer before setting so->tx.state to ISOTP_IDLE, not after: otherwise a concurrent sendmsg() could claim ISOTP_SENDING and start filling so->tx.buf while a still-armed timer from the aborted transfer fires and sends a stale frame from it. Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol") Reported-by: sashiko-bot@kernel.org Link: https://lore.kernel.org/linux-can/20260710142146.BDAE61F000E9@smtp.kernel.org/ Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net> --- net/can/isotp.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index 44c044eb83e1..5ef2a610fc17 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -376,10 +376,19 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae) so->tx.state != ISOTP_WAIT_FIRST_FC) return 0; hrtimer_cancel(&so->txtimer); + /* isotp_tx_timer_handler() may have raced us for so->tx.state while + * hrtimer_cancel() above waited for it to finish, already reporting + * the tx error and resetting the state; don't resume a tx job that + * has already been given up on. + */ + if (so->tx.state != ISOTP_WAIT_FC && + so->tx.state != ISOTP_WAIT_FIRST_FC) + return 1; + if ((cf->len < ae + FC_CONTENT_SZ) || ((so->opt.flags & ISOTP_CHECK_PADDING) && check_pad(so, cf, ae + FC_CONTENT_SZ, so->opt.rxpad_content))) { /* malformed PDU - report 'not a data message' */ sk->sk_err = EBADMSG; @@ -575,10 +584,18 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae, so->lastrxcf_tstamp = skb->tstamp; } hrtimer_cancel(&so->rxtimer); + /* isotp_rx_timer_handler() may have raced us for so->rx.state + * while hrtimer_cancel() above waited for it to finish, already + * reporting ETIMEDOUT and resetting the reception; don't process + * this CF into a reassembly that has already been given up on. + */ + if (so->rx.state != ISOTP_WAIT_DATA) + return 1; + /* CFs are never longer than the FF */ if (cf->len > so->rx.ll_dl) return 1; /* CFs have usually the LL_DL length */ @@ -879,10 +896,18 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data) hrtimer_cancel(&so->txtimer); /* local echo skb with consecutive frame has been consumed */ so->cfecho = 0; + /* isotp_tx_timer_handler() may have raced us for so->tx.state while + * hrtimer_cancel() above waited for it to finish, already reporting + * the tx error and resetting the state; don't resume a tx job that + * has already been given up on. + */ + if (so->tx.state != ISOTP_SENDING) + return; + if (so->tx.idx >= so->tx.len) { /* we are done */ so->tx.state = ISOTP_IDLE; wake_up_interruptible(&so->wait); return; @@ -1141,12 +1166,11 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) } return size; err_event_drop: - /* got signal: force tx state machine to be idle */ - so->tx.state = ISOTP_IDLE; + /* got signal: force tx state machine to be ISOTP_IDLE */ hrtimer_cancel(&so->txfrtimer); hrtimer_cancel(&so->txtimer); err_out_drop: /* drop this PDU and unlock a potential wait queue */ so->tx.state = ISOTP_IDLE; -- 2.53.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v6 3/5] can: isotp: fix race between RX/TX timers and frame reception 2026-07-10 20:42 ` [PATCH v6 3/5] can: isotp: fix race between RX/TX timers and frame reception Oliver Hartkopp via B4 Relay @ 2026-07-10 20:55 ` sashiko-bot 0 siblings, 0 replies; 11+ messages in thread From: sashiko-bot @ 2026-07-10 20:55 UTC (permalink / raw) To: Oliver Hartkopp Cc: Marc Kleine-Budde, Oliver Hartkopp, Vincent Mailhol, Oleksij Rempel, linux-can Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider: New issues: - [High] Moving `so->tx.state = ISOTP_IDLE` after `hrtimer_cancel()` in the `err_event_drop` path introduces a race condition with softirq receive handlers, leading to stray timers that can corrupt subsequent transmissions. - [High] The delayed `so->tx.state = ISOTP_IDLE` assignment in the error path unconditionally clobbers the state, which can corrupt a new, concurrently running transfer. - [High] The newly added TX state checks after `hrtimer_cancel()` in `isotp_rcv_fc()` and `isotp_rcv_echo()` are vulnerable to an ABA race condition, allowing stale frames to corrupt new transfers. Pre-existing issues: - [High] Missing cleanup of `so->cfecho` in `isotp_sendmsg()` error paths allows stale local echoes to corrupt subsequent transfers. -- --- Patch [3]: [PATCH v6 3/5] can: isotp: fix race between RX/TX timers and frame reception --- commit 4f481801d9d9059ae72ceaf6467d32f307eddbb5 Author: Oliver Hartkopp <socketcan@hartkopp.net> can: isotp: fix race between RX/TX timers and frame reception This patch fixes a race condition between hrtimer execution and frame reception by checking the state after hrtimer_cancel(). It also reorders timer cancellation and state reset in isotp_sendmsg()'s error path to prevent concurrent sendmsg calls from corrupting state. 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..5ef2a610fc172 100644 > --- a/net/can/isotp.c > +++ b/net/can/isotp.c [ ... ] > @@ -378,6 +378,15 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae) > > hrtimer_cancel(&so->txtimer); > > + /* isotp_tx_timer_handler() may have raced us for so->tx.state while > + * hrtimer_cancel() above waited for it to finish, already reporting > + * the tx error and resetting the state; don't resume a tx job that > + * has already been given up on. > + */ > + if (so->tx.state != ISOTP_WAIT_FC && > + so->tx.state != ISOTP_WAIT_FIRST_FC) > + return 1; > + [Severity: High] Is this lockless state check vulnerable to an ABA transition? While hrtimer_cancel() spins waiting for a timer handler, the timer handler could reset the state to ISOTP_IDLE. If a concurrent isotp_sendmsg() from another thread starts a new transfer during this window, it could locklessly transition the state back to the exact same expected state (e.g., ISOTP_WAIT_FIRST_FC). When hrtimer_cancel() returns, this check would pass because the state matches, but it would be evaluating a different transfer. Could this allow a stale Flow Control frame to corrupt the new transfer? [ ... ] > @@ -881,6 +898,14 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data) > /* local echo skb with consecutive frame has been consumed */ > so->cfecho = 0; > > + /* isotp_tx_timer_handler() may have raced us for so->tx.state while > + * hrtimer_cancel() above waited for it to finish, already reporting > + * the tx error and resetting the state; don't resume a tx job that > + * has already been given up on. > + */ > + if (so->tx.state != ISOTP_SENDING) > + return; > + [Severity: High] Could the same ABA race happen here? If a timer resets the state to ISOTP_IDLE while hrtimer_cancel() waits, and a concurrent isotp_sendmsg() starts a new transfer and sets the state to ISOTP_SENDING, this check would pass for the new transfer. Would this allow a stale local echo to erroneously resume the newly started transfer? [ ... ] > @@ -1143,8 +1168,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) > return size; > > err_event_drop: > - /* got signal: force tx state machine to be idle */ > - so->tx.state = ISOTP_IDLE; > + /* got signal: force tx state machine to be ISOTP_IDLE */ > hrtimer_cancel(&so->txfrtimer); > hrtimer_cancel(&so->txtimer); > err_out_drop: [Severity: High] Does delaying the state reset to after hrtimer_cancel() create a window where concurrent RX softirqs can observe an active state? If isotp_sendmsg() is interrupted by a signal, it now calls hrtimer_cancel() while so->tx.state is still visibly active (e.g., ISOTP_SENDING). If a CAN receive handler (like isotp_rcv_echo()) executes concurrently in softirq context, it locklessly checks so->tx.state, sees it is active, and can re-arm the transmission timers. When this error path completes, it sets the state to ISOTP_IDLE, leaving the newly armed timers active. When a subsequent sendmsg() begins, could these stray timers fire unexpectedly and corrupt the new transfer's sequence? [Severity: High] Does the delayed assignment of ISOTP_IDLE in err_out_drop clobber concurrently claimed state? While hrtimer_cancel() waits for a timer handler to finish (which resets the state to ISOTP_IDLE), a concurrent thread could call isotp_sendmsg(), successfully claim the ISOTP_IDLE state, and begin a new transfer. When the original thread finishes hrtimer_cancel(), it unconditionally executes so->tx.state = ISOTP_IDLE (located under err_out_drop:). Could this abruptly clobber the new transfer's state and expose its buffer to concurrent overwrites? [Severity: High] This is a pre-existing issue, but does the error path fail to clean up so->cfecho? If a transmission is aborted via a signal, the state is forced to ISOTP_IDLE but so->cfecho is not cleared and continues to hold the local echo tag of the aborted frame. When a subsequent sendmsg() sets the state to ISOTP_SENDING and begins preparing the buffer, a stale echo could arrive. If isotp_rcv_echo() sees a matching so->cfecho and an active ISOTP_SENDING state, could it process the echo and initiate a transmission containing uninitialized heap data? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260710-isotp-fixes-v6-0-a13c2539093d@hartkopp.net?part=3 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v6 4/5] can: isotp: fix lock-free state transition in tx timer handler 2026-07-10 20:42 [PATCH v6 0/5] net: can: isotp-fixes Oliver Hartkopp via B4 Relay ` (2 preceding siblings ...) 2026-07-10 20:42 ` [PATCH v6 3/5] can: isotp: fix race between RX/TX timers and frame reception Oliver Hartkopp via B4 Relay @ 2026-07-10 20:42 ` Oliver Hartkopp via B4 Relay 2026-07-10 20:57 ` sashiko-bot 2026-07-10 20:42 ` [PATCH v6 5/5] can: isotp: fix state machine corruption on signal interruption Oliver Hartkopp via B4 Relay 4 siblings, 1 reply; 11+ messages in thread From: Oliver Hartkopp via B4 Relay @ 2026-07-10 20:42 UTC (permalink / raw) To: linux-can; +Cc: Oliver Hartkopp, Marc Kleine-Budde, sashiko-bot From: Oliver Hartkopp <socketcan@hartkopp.net> Commit 051737439eae ("can: isotp: fix race between isotp_sendsmg() and isotp_release()") introduced a lock-free state machine check to prevent race conditions between the TX timer and concurrent state updates. However, the original patch missed replacing the initial state checks and left the late assignment of ISOTP_IDLE as a blind, non-atomic write. Fix this by properly sampling the initial state into 'old_state' and using cmpxchg() to atomically move the state to ISOTP_IDLE. If the state changed concurrently (e.g., due to an incoming echo or a new sendmsg), the timeout is stale and we bail out safely without corrupting the state machine. Fixes: 43a08c3bdac4cb ("can: isotp: isotp_sendmsg(): fix TX buffer concurrent access in isotp_sendmsg()") Reported-by: sashiko-bot@kernel.org Link: https://lore.kernel.org/linux-can/20260710142146.BDAE61F000E9@smtp.kernel.org/ Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net> --- net/can/isotp.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index 5ef2a610fc17..0826ae837246 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -937,24 +937,29 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data) static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer) { struct isotp_sock *so = container_of(hrtimer, struct isotp_sock, txtimer); struct sock *sk = &so->sk; + u32 old_state = READ_ONCE(so->tx.state); /* don't handle timeouts in IDLE or SHUTDOWN state */ - if (so->tx.state == ISOTP_IDLE || so->tx.state == ISOTP_SHUTDOWN) + if (old_state == ISOTP_IDLE || old_state == ISOTP_SHUTDOWN) + return HRTIMER_NORESTART; + + /* Only claim timeout if state is unchanged to avoid overwriting + * concurrent state updates from isotp_sendmsg/isotp_rcv_echo. + */ + if (cmpxchg(&so->tx.state, old_state, ISOTP_IDLE) != old_state) return HRTIMER_NORESTART; /* we did not get any flow control or echo frame in time */ /* report 'communication error on send' */ sk->sk_err = ECOMM; if (!sock_flag(sk, SOCK_DEAD)) sk_error_report(sk); - /* reset tx state */ - so->tx.state = ISOTP_IDLE; wake_up_interruptible(&so->wait); return HRTIMER_NORESTART; } -- 2.53.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v6 4/5] can: isotp: fix lock-free state transition in tx timer handler 2026-07-10 20:42 ` [PATCH v6 4/5] can: isotp: fix lock-free state transition in tx timer handler Oliver Hartkopp via B4 Relay @ 2026-07-10 20:57 ` sashiko-bot 0 siblings, 0 replies; 11+ messages in thread From: sashiko-bot @ 2026-07-10 20:57 UTC (permalink / raw) To: Oliver Hartkopp Cc: Vincent Mailhol, linux-can, Marc Kleine-Budde, Oleksij Rempel, Oliver Hartkopp Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: New issues: - [High] Early state transition to ISOTP_IDLE in the TX timer handler introduces a race condition that can corrupt the error state of a concurrent transmission. Pre-existing issues: - [High] A signal during the `isotp_sendmsg()` wait loop can abort another thread's active transmission on a shared socket. -- --- Patch [4]: [PATCH v6 4/5] can: isotp: fix lock-free state transition in tx timer handler --- commit 3453d78776681a05466b1147a03618091e6287bc Author: Oliver Hartkopp <socketcan@hartkopp.net> can: isotp: fix lock-free state transition in tx timer handler This commit introduces a cmpxchg to safely transition the state machine to ISOTP_IDLE in the tx timer handler. It ensures the state is only updated if it hasn't been changed concurrently. 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 5ef2a610fc172..0826ae8372467 100644 > --- a/net/can/isotp.c > +++ b/net/can/isotp.c > @@ -939,9 +939,16 @@ static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer) [ ... ] > + /* Only claim timeout if state is unchanged to avoid overwriting > + * concurrent state updates from isotp_sendmsg/isotp_rcv_echo. > + */ > + if (cmpxchg(&so->tx.state, old_state, ISOTP_IDLE) != old_state) > return HRTIMER_NORESTART; > > /* we did not get any flow control or echo frame in time */ > @@ -951,8 +958,6 @@ static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer) > if (!sock_flag(sk, SOCK_DEAD)) > sk_error_report(sk); [Severity: High] Does this early state transition to ISOTP_IDLE introduce a race condition? If the cmpxchg successfully sets the state to ISOTP_IDLE, another thread calling isotp_sendmsg() on a different CPU could see the IDLE state and immediately start a new transmission before this timer handler completes. If that happens, the timer thread would resume and execute sk->sk_err = ECOMM and sk_error_report(sk), which might incorrectly taint the new transmission with a communication error. > - /* reset tx state */ > - so->tx.state = ISOTP_IDLE; > wake_up_interruptible(&so->wait); > > return HRTIMER_NORESTART; [Severity: High] This is a pre-existing issue, but can a signal during the wait loop in isotp_sendmsg() abort another thread's active transmission? Looking at isotp_sendmsg(), if a concurrent sender fails to acquire the state and waits: net/can/isotp.c:isotp_sendmsg() err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE); if (err) goto err_event_drop; If a signal interrupts this wait, the thread jumps to err_event_drop where it unconditionally cancels the timers and resets the state: err_event_drop: hrtimer_cancel(&so->txfrtimer); hrtimer_cancel(&so->txtimer); err_out_drop: so->tx.state = ISOTP_IDLE; Since this thread was only waiting and never claimed the state machine, will this forcefully cancel the active transmission of the thread that actually owned it? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260710-isotp-fixes-v6-0-a13c2539093d@hartkopp.net?part=4 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v6 5/5] can: isotp: fix state machine corruption on signal interruption 2026-07-10 20:42 [PATCH v6 0/5] net: can: isotp-fixes Oliver Hartkopp via B4 Relay ` (3 preceding siblings ...) 2026-07-10 20:42 ` [PATCH v6 4/5] can: isotp: fix lock-free state transition in tx timer handler Oliver Hartkopp via B4 Relay @ 2026-07-10 20:42 ` Oliver Hartkopp via B4 Relay 2026-07-10 20:52 ` sashiko-bot 4 siblings, 1 reply; 11+ messages in thread From: Oliver Hartkopp via B4 Relay @ 2026-07-10 20:42 UTC (permalink / raw) To: linux-can; +Cc: Oliver Hartkopp, Marc Kleine-Budde From: Oliver Hartkopp <socketcan@hartkopp.net> If a thread blocked in isotp_sendmsg() waiting for a previous transfer to finish is interrupted by a signal, jumping to the cleanup labels incorrectly cancels active timers and forces 'so->tx.state' back to ISOTP_IDLE. Since this thread never owned the active transmission, it abruptly aborts another thread's legitimate transfer, so return the interruption error directly. Fixes: e057dd3fc20f ("can: add ISO 15765-2:2016 transport protocol") Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net> --- net/can/isotp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/can/isotp.c b/net/can/isotp.c index 0826ae837246..b8562f8c6de0 100644 --- a/net/can/isotp.c +++ b/net/can/isotp.c @@ -1005,11 +1005,11 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) return -EADDRNOTAVAIL; /* wait for complete transmission of current pdu */ err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE); if (err) - goto err_event_drop; + return err; } /* so->bound is only checked once above - a wakeup may have * unbound/rebound the socket meanwhile, so re-validate it */ -- 2.53.0 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v6 5/5] can: isotp: fix state machine corruption on signal interruption 2026-07-10 20:42 ` [PATCH v6 5/5] can: isotp: fix state machine corruption on signal interruption Oliver Hartkopp via B4 Relay @ 2026-07-10 20:52 ` sashiko-bot 0 siblings, 0 replies; 11+ messages in thread From: sashiko-bot @ 2026-07-10 20:52 UTC (permalink / raw) To: Oliver Hartkopp Cc: linux-can, Oliver Hartkopp, Marc Kleine-Budde, Vincent Mailhol, Oleksij Rempel Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: Pre-existing issues: - [High] Signal interruption of a transmission (e.g., during CAN_ISOTP_WAIT_TX_DONE) or a TX timeout aborts an active transmission by forcing state to ISOTP_IDLE but fails to clear `so->cfecho`. This exposes a race condition with delayed local loopback echoes that can leak uninitialized kernel heap memory or corrupt subsequent transmissions. -- --- Patch [5]: [PATCH v6 5/5] can: isotp: fix state machine corruption on signal interruption --- commit 0fb83cf387b5c9feb7682bf6c41fda7dbcb7b3d2 Author: Oliver Hartkopp <socketcan@hartkopp.net> can: isotp: fix state machine corruption on signal interruption If a thread blocked in isotp_sendmsg() waiting for a previous transfer to finish is interrupted by a signal, jumping to the cleanup labels incorrectly cancels active timers and forces 'so->tx.state' back to ISOTP_IDLE. Since this thread never owned the active transmission, it abruptly aborts another thread's legitimate transfer, so return the interruption error directly. > diff --git a/net/can/isotp.c b/net/can/isotp.c > index 0826ae8372467..b8562f8c6de04 100644 > --- a/net/can/isotp.c > +++ b/net/can/isotp.c > @@ -1007,7 +1007,7 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) > /* wait for complete transmission of current pdu */ > err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE); > if (err) > - goto err_event_drop; > + return err; > } [Severity: High] This isn't a bug introduced by this patch, but does resetting the state to ISOTP_IDLE without clearing so->cfecho expose a race condition with delayed local loopback echoes? For example, if a transmission is interrupted during CAN_ISOTP_WAIT_TX_DONE, the wait loop is aborted and the state is reset: net/can/isotp.c:isotp_sendmsg() { ... err_event_drop: /* got signal: force tx state machine to be ISOTP_IDLE */ hrtimer_cancel(&so->txfrtimer); hrtimer_cancel(&so->txtimer); err_out_drop: /* drop this PDU and unlock a potential wait queue */ so->tx.state = ISOTP_IDLE; wake_up_interruptible(&so->wait); return err; } Similarly, a TX timeout resets the state without clearing so->cfecho: net/can/isotp.c:isotp_tx_timer_handler() { ... if (cmpxchg(&so->tx.state, old_state, ISOTP_IDLE) != old_state) return HRTIMER_NORESTART; /* we did not get any flow control or echo frame in time */ ... } If a user starts a new transmission with a payload larger than so->tx.buflen, the kernel sets so->tx.state to ISOTP_SENDING and allocates a new uninitialized buffer. If a delayed loopback echo from the aborted transmission arrives at this time, could it be erroneously processed by the echo handler? net/can/isotp.c:isotp_rcv_echo() { ... /* local echo skb with consecutive frame has been consumed */ so->cfecho = 0; /* isotp_tx_timer_handler() may have raced us for so->tx.state while * hrtimer_cancel() above waited for it to finish, already reporting * the tx error and resetting the state; don't resume a tx job that * has already been given up on. */ if (so->tx.state != ISOTP_SENDING) return; if (so->tx.idx >= so->tx.len) { /* we are done */ ... } Because so->cfecho still matches and so->tx.state is now ISOTP_SENDING, could this result in transmitting uninitialized kernel heap memory over the CAN bus by continuing transmission using the old so->tx.idx and so->tx.len but the new uninitialized so->tx.buf? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260710-isotp-fixes-v6-0-a13c2539093d@hartkopp.net?part=5 ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-07-10 20:58 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-07-10 20:42 [PATCH v6 0/5] net: can: isotp-fixes Oliver Hartkopp via B4 Relay 2026-07-10 20:42 ` [PATCH v6 1/5] can: isotp: use unconditional synchronize_rcu() in isotp_release() Oliver Hartkopp via B4 Relay 2026-07-10 20:58 ` sashiko-bot 2026-07-10 20:42 ` [PATCH v6 2/5] can: isotp: fix use-after-free race with concurrent NETDEV_UNREGISTER Oliver Hartkopp via B4 Relay 2026-07-10 20:56 ` sashiko-bot 2026-07-10 20:42 ` [PATCH v6 3/5] can: isotp: fix race between RX/TX timers and frame reception Oliver Hartkopp via B4 Relay 2026-07-10 20:55 ` sashiko-bot 2026-07-10 20:42 ` [PATCH v6 4/5] can: isotp: fix lock-free state transition in tx timer handler Oliver Hartkopp via B4 Relay 2026-07-10 20:57 ` sashiko-bot 2026-07-10 20:42 ` [PATCH v6 5/5] can: isotp: fix state machine corruption on signal interruption Oliver Hartkopp via B4 Relay 2026-07-10 20:52 ` sashiko-bot
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox