Merge patch series "net: can: isotp-fixes"

Oliver Hartkopp <socketcan@hartkopp.net> says:

As sashiko-bot was not able to check the second patch this bundle is
re-posted with b4 preparation.

Link: https://patch.msgid.link/20260712-isotp-fixes-v10-0-793a1b1ce17f@hartkopp.net
[mkl: added stable@k.o on Cc, converted Link: -> Closes:]
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
Marc Kleine-Budde
2026-07-16 10:12:22 +02:00

View File

@@ -152,11 +152,13 @@ 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;
struct hrtimer rxtimer, txtimer, txfrtimer, echotimer;
struct can_isotp_options opt;
struct can_isotp_fc_options rxfc, txfc;
struct can_isotp_ll_options ll;
@@ -164,6 +166,7 @@ struct isotp_sock {
u32 force_tx_stmin;
u32 force_rx_stmin;
u32 cfecho; /* consecutive frame echo tag */
u32 tx_gen; /* generation, bumped per new tx transfer */
struct tpcon rx, tx;
struct list_head notifier;
wait_queue_head_t wait;
@@ -376,6 +379,15 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
hrtimer_cancel(&so->txtimer);
/* isotp_tx_timeout() may have given up on this job while
* hrtimer_cancel() above waited for it to finish; so->rx_lock
* (held by our caller isotp_rcv()) rules out a concurrent claim,
* so a plain recheck is enough here.
*/
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))) {
@@ -422,7 +434,7 @@ static int isotp_rcv_fc(struct isotp_sock *so, struct canfd_frame *cf, int ae)
so->tx.bs = 0;
so->tx.state = ISOTP_SENDING;
/* send CF frame and enable echo timeout handling */
hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
HRTIMER_MODE_REL_SOFT);
isotp_send_cframe(so);
break;
@@ -575,6 +587,14 @@ static int isotp_rcv_cf(struct sock *sk, struct canfd_frame *cf, int ae,
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;
@@ -870,20 +890,36 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
struct canfd_frame *cf = (struct canfd_frame *)skb->data;
/* only handle my own local echo CF/SF skb's (no FF!) */
if (skb->sk != sk || so->cfecho != *(u32 *)cf->data)
if (skb->sk != sk)
return;
/* unlike isotp_rcv_fc()/isotp_rcv_cf(), not already under so->rx_lock
* (no isotp_rcv() caller here), so take it ourselves
*/
spin_lock(&so->rx_lock);
/* so->cfecho may since belong to a new transfer; recheck under lock */
if (so->cfecho != *(u32 *)cf->data)
goto out_unlock;
/* cancel local echo timeout */
hrtimer_cancel(&so->txtimer);
hrtimer_cancel(&so->echotimer);
/* local echo skb with consecutive frame has been consumed */
so->cfecho = 0;
/* claiming a transfer also takes so->rx_lock, so a plain recheck
* is enough: so->tx.state can't have flipped to ISOTP_SENDING for
* a new claim while we're still in here
*/
if (so->tx.state != ISOTP_SENDING)
goto out_unlock;
if (so->tx.idx >= so->tx.len) {
/* we are done */
so->tx.state = ISOTP_IDLE;
wake_up_interruptible(&so->wait);
return;
goto out_unlock;
}
if (so->txfc.bs && so->tx.bs >= so->txfc.bs) {
@@ -891,53 +927,83 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
so->tx.state = ISOTP_WAIT_FC;
hrtimer_start(&so->txtimer, ktime_set(ISOTP_FC_TIMEOUT, 0),
HRTIMER_MODE_REL_SOFT);
return;
goto out_unlock;
}
/* no gap between data frames needed => use burst mode */
if (!so->tx_gap) {
/* enable echo timeout handling */
hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
HRTIMER_MODE_REL_SOFT);
isotp_send_cframe(so);
return;
goto out_unlock;
}
/* start timer to send next consecutive frame with correct delay */
hrtimer_start(&so->txfrtimer, so->tx_gap, HRTIMER_MODE_REL_SOFT);
out_unlock:
spin_unlock(&so->rx_lock);
}
static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
/* shared by so->txtimer's and so->echotimer's callbacks. Both timers get
* cancelled under so->rx_lock elsewhere, so this must stay lock-free to
* avoid deadlocking with that; uses so->tx_gen instead to avoid tainting
* a new transfer with an error from the one that just timed out.
*/
static enum hrtimer_restart isotp_tx_timeout(struct isotp_sock *so)
{
struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
txtimer);
struct sock *sk = &so->sk;
u32 gen = READ_ONCE(so->tx_gen);
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 the timeout if the state is still unchanged */
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);
if (READ_ONCE(so->tx_gen) == gen) {
/* 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;
}
/* so->txtimer: fires when a Flow Control frame does not arrive in time */
static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
{
struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
txtimer);
return isotp_tx_timeout(so);
}
/* so->echotimer: fires when a sent CF/SF's local echo does not arrive */
static enum hrtimer_restart isotp_echo_timer_handler(struct hrtimer *hrtimer)
{
struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
echotimer);
return isotp_tx_timeout(so);
}
static enum hrtimer_restart isotp_txfr_timer_handler(struct hrtimer *hrtimer)
{
struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
txfrtimer);
/* start echo timeout handling and cover below protocol error */
hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
hrtimer_start(&so->echotimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
HRTIMER_MODE_REL_SOFT);
/* cfecho should be consumed by isotp_rcv_echo() here */
@@ -958,13 +1024,24 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
int ae = (so->opt.flags & CAN_ISOTP_EXTEND_ADDR) ? 1 : 0;
int wait_tx_done = (so->opt.flags & CAN_ISOTP_WAIT_TX_DONE) ? 1 : 0;
s64 hrtimer_sec = ISOTP_ECHO_TIMEOUT;
struct hrtimer *tx_hrt = &so->echotimer;
u32 new_state = ISOTP_SENDING;
int off;
int err;
if (!so->bound || so->tx.state == ISOTP_SHUTDOWN)
return -EADDRNOTAVAIL;
while (cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SENDING) != ISOTP_IDLE) {
/* claim the socket under so->rx_lock: this serializes the claim
* with the RX path and with sendmsg()'s own error paths below, so
* none of them can ever see a transfer mid-claim
*/
for (;;) {
spin_lock_bh(&so->rx_lock);
if (READ_ONCE(so->tx.state) == ISOTP_IDLE)
break;
spin_unlock_bh(&so->rx_lock);
/* we do not support multiple buffers - for now */
if (msg->msg_flags & MSG_DONTWAIT)
return -EAGAIN;
@@ -973,9 +1050,29 @@ 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);
err = wait_event_interruptible(so->wait,
so->tx.state == ISOTP_IDLE);
if (err)
goto err_event_drop;
return err;
}
/* new transfer: bump so->tx_gen and drain the old one's timers,
* still under the so->rx_lock we just claimed the socket with
*/
WRITE_ONCE(so->tx.state, ISOTP_SENDING);
WRITE_ONCE(so->tx_gen, READ_ONCE(so->tx_gen) + 1);
hrtimer_cancel(&so->txtimer);
hrtimer_cancel(&so->echotimer);
hrtimer_cancel(&so->txfrtimer);
so->cfecho = 0;
spin_unlock_bh(&so->rx_lock);
/* 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 */
@@ -1086,18 +1183,33 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
so->cfecho = *(u32 *)cf->data;
} else {
/* standard flow control check */
so->tx.state = ISOTP_WAIT_FIRST_FC;
new_state = ISOTP_WAIT_FIRST_FC;
/* start timeout for FC */
hrtimer_sec = ISOTP_FC_TIMEOUT;
tx_hrt = &so->txtimer;
/* no CF echo tag for isotp_rcv_echo() (FF-mode) */
so->cfecho = 0;
}
}
hrtimer_start(&so->txtimer, ktime_set(hrtimer_sec, 0),
spin_lock_bh(&so->rx_lock);
if (so->tx.state == ISOTP_SHUTDOWN) {
/* isotp_release() has since taken over and already drained
* our timers - don't send into a socket that's going away
*/
spin_unlock_bh(&so->rx_lock);
kfree_skb(skb);
dev_put(dev);
wake_up_interruptible(&so->wait);
return -EADDRNOTAVAIL;
}
/* WAIT_FIRST_FC for standard FF, else stays ISOTP_SENDING */
so->tx.state = new_state;
hrtimer_start(tx_hrt, ktime_set(hrtimer_sec, 0),
HRTIMER_MODE_REL_SOFT);
spin_unlock_bh(&so->rx_lock);
/* send the first or only CAN frame */
cf->flags = so->ll.tx_flags;
@@ -1110,13 +1222,10 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
pr_notice_once("can-isotp: %s: can_send_ret %pe\n",
__func__, ERR_PTR(err));
spin_lock_bh(&so->rx_lock);
/* no transmission -> no timeout monitoring */
hrtimer_cancel(&so->txtimer);
/* reset consecutive frame echo tag */
so->cfecho = 0;
goto err_out_drop;
hrtimer_cancel(tx_hrt);
goto err_out_drop_locked;
}
if (wait_tx_done) {
@@ -1132,14 +1241,21 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
return size;
err_out_drop:
/* claimed but nothing sent yet - no timer to cancel */
spin_lock_bh(&so->rx_lock);
goto err_out_drop_locked;
err_event_drop:
/* got signal: force tx state machine to be idle */
so->tx.state = ISOTP_IDLE;
/* interrupted waiting on our own transfer - drain its timers */
spin_lock_bh(&so->rx_lock);
hrtimer_cancel(&so->txfrtimer);
hrtimer_cancel(&so->txtimer);
err_out_drop:
/* drop this PDU and unlock a potential wait queue */
hrtimer_cancel(&so->echotimer);
err_out_drop_locked:
/* release the claim; so->rx_lock still held from above */
so->cfecho = 0;
so->tx.state = ISOTP_IDLE;
spin_unlock_bh(&so->rx_lock);
wake_up_interruptible(&so->wait);
return err;
@@ -1201,13 +1317,20 @@ static int isotp_release(struct socket *sock)
so = isotp_sk(sk);
net = sock_net(sk);
/* wait for complete transmission of current pdu */
while (wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0 &&
cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SHUTDOWN) != ISOTP_IDLE)
/* best-effort: wait for a running pdu to finish, but don't block on
* it forever - give up after the first signal
*/
while (so->tx.state != ISOTP_IDLE &&
wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0)
;
/* force state machines to be idle also when a signal occurred */
/* claim the socket under so->rx_lock like sendmsg() does, so its
* claim can't race the forced ISOTP_SHUTDOWN below; force it
* unconditionally, even when a signal cut the wait above short
*/
spin_lock_bh(&so->rx_lock);
so->tx.state = ISOTP_SHUTDOWN;
spin_unlock_bh(&so->rx_lock);
so->rx.state = ISOTP_IDLE;
spin_lock(&isotp_notifier_lock);
@@ -1219,35 +1342,42 @@ static int isotp_release(struct socket *sock)
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;
/* 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);
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);
synchronize_rcu();
}
}
can_rx_unregister(net, so->dev, so->txid,
SINGLE_MASK(so->txid),
isotp_rcv_echo, sk);
netdev_put(so->dev, &so->dev_tracker);
}
hrtimer_cancel(&so->txfrtimer);
hrtimer_cancel(&so->txtimer);
hrtimer_cancel(&so->rxtimer);
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.
*/
synchronize_rcu();
hrtimer_cancel(&so->txfrtimer);
hrtimer_cancel(&so->txtimer);
hrtimer_cancel(&so->echotimer);
hrtimer_cancel(&so->rxtimer);
sock_orphan(sk);
sock->sk = NULL;
@@ -1303,6 +1433,7 @@ 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) {
@@ -1310,6 +1441,17 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
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;
@@ -1322,14 +1464,12 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
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;
@@ -1347,16 +1487,25 @@ static int isotp_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int l
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;
@@ -1559,7 +1708,7 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
if (!net_eq(dev_net(dev), sock_net(sk)))
return;
if (so->ifindex != dev->ifindex)
if (so->dev != dev)
return;
switch (msg) {
@@ -1575,10 +1724,12 @@ static void isotp_notify(struct isotp_sock *so, unsigned long msg,
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;
@@ -1638,6 +1789,7 @@ static int isotp_init(struct sock *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;
@@ -1664,10 +1816,14 @@ static int isotp_init(struct sock *sk)
so->rx.buflen = ARRAY_SIZE(so->rx.sbuf);
so->tx.buflen = ARRAY_SIZE(so->tx.sbuf);
hrtimer_setup(&so->rxtimer, isotp_rx_timer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
hrtimer_setup(&so->txtimer, isotp_tx_timer_handler, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
hrtimer_setup(&so->txfrtimer, isotp_txfr_timer_handler, CLOCK_MONOTONIC,
HRTIMER_MODE_REL_SOFT);
hrtimer_setup(&so->rxtimer, isotp_rx_timer_handler,
CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
hrtimer_setup(&so->txtimer, isotp_tx_timer_handler,
CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
hrtimer_setup(&so->echotimer, isotp_echo_timer_handler,
CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
hrtimer_setup(&so->txfrtimer, isotp_txfr_timer_handler,
CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
init_waitqueue_head(&so->wait);
spin_lock_init(&so->rx_lock);