Commit 4f027cba authored by Oliver Hartkopp's avatar Oliver Hartkopp Committed by Marc Kleine-Budde

can: isotp: split tx timer into transmission and timeout

The timer for the transmission of isotp PDUs formerly had two functions:
1. send two consecutive frames with a given time gap
2. monitor the timeouts for flow control frames and the echo frames

This led to larger txstate checks and potentially to a problem discovered
by syzbot which enabled the panic_on_warn feature while testing.

The former 'txtimer' function is split into 'txfrtimer' and 'txtimer'
to handle the two above functionalities with separate timer callbacks.

The two simplified timers now run in one-shot mode and make the state
transitions (especially with isotp_rcv_echo) better understandable.

Fixes: 86633786 ("can: isotp: fix tx state handling for echo tx processing")
Reported-by: syzbot+5aed6c3aaba661f5b917@syzkaller.appspotmail.com
Cc: stable@vger.kernel.org # >= v6.0
Signed-off-by: default avatarOliver Hartkopp <socketcan@hartkopp.net>
Link: https://lore.kernel.org/all/20230104145701.2422-1-socketcan@hartkopp.netSigned-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent 823b2e42
...@@ -140,7 +140,7 @@ struct isotp_sock { ...@@ -140,7 +140,7 @@ struct isotp_sock {
canid_t rxid; canid_t rxid;
ktime_t tx_gap; ktime_t tx_gap;
ktime_t lastrxcf_tstamp; ktime_t lastrxcf_tstamp;
struct hrtimer rxtimer, txtimer; struct hrtimer rxtimer, txtimer, txfrtimer;
struct can_isotp_options opt; struct can_isotp_options opt;
struct can_isotp_fc_options rxfc, txfc; struct can_isotp_fc_options rxfc, txfc;
struct can_isotp_ll_options ll; struct can_isotp_ll_options ll;
...@@ -871,7 +871,7 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data) ...@@ -871,7 +871,7 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
} }
/* start timer to send next consecutive frame with correct delay */ /* start timer to send next consecutive frame with correct delay */
hrtimer_start(&so->txtimer, so->tx_gap, HRTIMER_MODE_REL_SOFT); hrtimer_start(&so->txfrtimer, so->tx_gap, HRTIMER_MODE_REL_SOFT);
} }
static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer) static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
...@@ -879,49 +879,39 @@ static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer) ...@@ -879,49 +879,39 @@ static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
struct isotp_sock *so = container_of(hrtimer, struct isotp_sock, struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
txtimer); txtimer);
struct sock *sk = &so->sk; struct sock *sk = &so->sk;
enum hrtimer_restart restart = HRTIMER_NORESTART;
switch (so->tx.state) { /* don't handle timeouts in IDLE state */
case ISOTP_SENDING: if (so->tx.state == ISOTP_IDLE)
return HRTIMER_NORESTART;
/* cfecho should be consumed by isotp_rcv_echo() here */ /* we did not get any flow control or echo frame in time */
if (!so->cfecho) {
/* start timeout for unlikely lost echo skb */
hrtimer_set_expires(&so->txtimer,
ktime_add(ktime_get(),
ktime_set(ISOTP_ECHO_TIMEOUT, 0)));
restart = HRTIMER_RESTART;
/* push out the next consecutive frame */ /* report 'communication error on send' */
isotp_send_cframe(so); sk->sk_err = ECOMM;
break; if (!sock_flag(sk, SOCK_DEAD))
} sk_error_report(sk);
/* cfecho has not been cleared in isotp_rcv_echo() */
pr_notice_once("can-isotp: cfecho %08X timeout\n", so->cfecho);
fallthrough;
case ISOTP_WAIT_FC: /* reset tx state */
case ISOTP_WAIT_FIRST_FC: so->tx.state = ISOTP_IDLE;
wake_up_interruptible(&so->wait);
/* we did not get any flow control frame in time */ return HRTIMER_NORESTART;
}
/* report 'communication error on send' */ static enum hrtimer_restart isotp_txfr_timer_handler(struct hrtimer *hrtimer)
sk->sk_err = ECOMM; {
if (!sock_flag(sk, SOCK_DEAD)) struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
sk_error_report(sk); txfrtimer);
/* reset tx state */ /* start echo timeout handling and cover below protocol error */
so->tx.state = ISOTP_IDLE; hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
wake_up_interruptible(&so->wait); HRTIMER_MODE_REL_SOFT);
break;
default: /* cfecho should be consumed by isotp_rcv_echo() here */
WARN_ONCE(1, "can-isotp: tx timer state %08X cfecho %08X\n", if (so->tx.state == ISOTP_SENDING && !so->cfecho)
so->tx.state, so->cfecho); isotp_send_cframe(so);
}
return restart; return HRTIMER_NORESTART;
} }
static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
...@@ -1198,6 +1188,7 @@ static int isotp_release(struct socket *sock) ...@@ -1198,6 +1188,7 @@ static int isotp_release(struct socket *sock)
} }
} }
hrtimer_cancel(&so->txfrtimer);
hrtimer_cancel(&so->txtimer); hrtimer_cancel(&so->txtimer);
hrtimer_cancel(&so->rxtimer); hrtimer_cancel(&so->rxtimer);
...@@ -1601,6 +1592,8 @@ static int isotp_init(struct sock *sk) ...@@ -1601,6 +1592,8 @@ static int isotp_init(struct sock *sk)
so->rxtimer.function = isotp_rx_timer_handler; so->rxtimer.function = isotp_rx_timer_handler;
hrtimer_init(&so->txtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT); hrtimer_init(&so->txtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
so->txtimer.function = isotp_tx_timer_handler; so->txtimer.function = isotp_tx_timer_handler;
hrtimer_init(&so->txfrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
so->txfrtimer.function = isotp_txfr_timer_handler;
init_waitqueue_head(&so->wait); init_waitqueue_head(&so->wait);
spin_lock_init(&so->rx_lock); spin_lock_init(&so->rx_lock);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment