Skip to content

Commit d7b6357

Browse files
committed
can: isotp: fix race between isotp_sendsmg() and isotp_release()
JIRA: https://issues.redhat.com/browse/RHEL-80832 commit 0517374 Author: Oliver Hartkopp <socketcan@hartkopp.net> Date: Fri Mar 31 15:19:35 2023 +0200 can: isotp: fix race between isotp_sendsmg() and isotp_release() As discussed with Dae R. Jeong and Hillf Danton here [1] the sendmsg() function in isotp.c might get into a race condition when restoring the former tx.state from the old_state. Remove the old_state concept and implement proper locking for the ISOTP_IDLE transitions in isotp_sendmsg(), inspired by a simplification idea from Hillf Danton. Introduce a new tx.state ISOTP_SHUTDOWN and use the same locking mechanism from isotp_release() which resolves a potential race between isotp_sendsmg() and isotp_release(). [1] https://lore.kernel.org/linux-can/ZB%2F93xJxq%2FBUqAgG@dragonet v1: https://lore.kernel.org/all/20230331102114.15164-1-socketcan@hartkopp.net v2: https://lore.kernel.org/all/20230331123600.3550-1-socketcan@hartkopp.net take care of signal interrupts for wait_event_interruptible() in isotp_release() v3: https://lore.kernel.org/all/20230331130654.9886-1-socketcan@hartkopp.net take care of signal interrupts for wait_event_interruptible() in isotp_sendmsg() in the wait_tx_done case v4: https://lore.kernel.org/all/20230331131935.21465-1-socketcan@hartkopp.net take care of signal interrupts for wait_event_interruptible() in isotp_sendmsg() in ALL cases Cc: Dae R. Jeong <threeearcat@gmail.com> Cc: Hillf Danton <hdanton@sina.com> Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net> Fixes: 4f027cb ("can: isotp: split tx timer into transmission and timeout") Link: https://lore.kernel.org/all/20230331131935.21465-1-socketcan@hartkopp.net Cc: stable@vger.kernel.org [mkl: rephrase commit message] Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de> Signed-off-by: Radu Rendec <rrendec@redhat.com>
1 parent 10e5e8d commit d7b6357

File tree

1 file changed

+31
-24
lines changed

1 file changed

+31
-24
lines changed

net/can/isotp.c

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ enum {
119119
ISOTP_WAIT_FIRST_FC,
120120
ISOTP_WAIT_FC,
121121
ISOTP_WAIT_DATA,
122-
ISOTP_SENDING
122+
ISOTP_SENDING,
123+
ISOTP_SHUTDOWN,
123124
};
124125

125126
struct tpcon {
@@ -880,8 +881,8 @@ static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
880881
txtimer);
881882
struct sock *sk = &so->sk;
882883

883-
/* don't handle timeouts in IDLE state */
884-
if (so->tx.state == ISOTP_IDLE)
884+
/* don't handle timeouts in IDLE or SHUTDOWN state */
885+
if (so->tx.state == ISOTP_IDLE || so->tx.state == ISOTP_SHUTDOWN)
885886
return HRTIMER_NORESTART;
886887

887888
/* we did not get any flow control or echo frame in time */
@@ -918,7 +919,6 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
918919
{
919920
struct sock *sk = sock->sk;
920921
struct isotp_sock *so = isotp_sk(sk);
921-
u32 old_state = so->tx.state;
922922
struct sk_buff *skb;
923923
struct net_device *dev;
924924
struct canfd_frame *cf;
@@ -928,23 +928,24 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
928928
int off;
929929
int err;
930930

931-
if (!so->bound)
931+
if (!so->bound || so->tx.state == ISOTP_SHUTDOWN)
932932
return -EADDRNOTAVAIL;
933933

934+
wait_free_buffer:
934935
/* we do not support multiple buffers - for now */
935-
if (cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SENDING) != ISOTP_IDLE ||
936-
wq_has_sleeper(&so->wait)) {
937-
if (msg->msg_flags & MSG_DONTWAIT) {
938-
err = -EAGAIN;
939-
goto err_out;
940-
}
936+
if (wq_has_sleeper(&so->wait) && (msg->msg_flags & MSG_DONTWAIT))
937+
return -EAGAIN;
941938

942-
/* wait for complete transmission of current pdu */
943-
err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
944-
if (err)
945-
goto err_out;
939+
/* wait for complete transmission of current pdu */
940+
err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
941+
if (err)
942+
goto err_event_drop;
946943

947-
so->tx.state = ISOTP_SENDING;
944+
if (cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SENDING) != ISOTP_IDLE) {
945+
if (so->tx.state == ISOTP_SHUTDOWN)
946+
return -EADDRNOTAVAIL;
947+
948+
goto wait_free_buffer;
948949
}
949950

950951
if (!size || size > MAX_MSG_LENGTH) {
@@ -1074,7 +1075,9 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
10741075

10751076
if (wait_tx_done) {
10761077
/* wait for complete transmission of current pdu */
1077-
wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
1078+
err = wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
1079+
if (err)
1080+
goto err_event_drop;
10781081

10791082
err = sock_error(sk);
10801083
if (err)
@@ -1083,13 +1086,15 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
10831086

10841087
return size;
10851088

1089+
err_event_drop:
1090+
/* got signal: force tx state machine to be idle */
1091+
so->tx.state = ISOTP_IDLE;
1092+
hrtimer_cancel(&so->txfrtimer);
1093+
hrtimer_cancel(&so->txtimer);
10861094
err_out_drop:
10871095
/* drop this PDU and unlock a potential wait queue */
1088-
old_state = ISOTP_IDLE;
1089-
err_out:
1090-
so->tx.state = old_state;
1091-
if (so->tx.state == ISOTP_IDLE)
1092-
wake_up_interruptible(&so->wait);
1096+
so->tx.state = ISOTP_IDLE;
1097+
wake_up_interruptible(&so->wait);
10931098

10941099
return err;
10951100
}
@@ -1151,10 +1156,12 @@ static int isotp_release(struct socket *sock)
11511156
net = sock_net(sk);
11521157

11531158
/* wait for complete transmission of current pdu */
1154-
wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
1159+
while (wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE) == 0 &&
1160+
cmpxchg(&so->tx.state, ISOTP_IDLE, ISOTP_SHUTDOWN) != ISOTP_IDLE)
1161+
;
11551162

11561163
/* force state machines to be idle also when a signal occurred */
1157-
so->tx.state = ISOTP_IDLE;
1164+
so->tx.state = ISOTP_SHUTDOWN;
11581165
so->rx.state = ISOTP_IDLE;
11591166

11601167
spin_lock(&isotp_notifier_lock);

0 commit comments

Comments
 (0)