Skip to content

Commit 5af3c6c

Browse files
rluboscarlescufi
authored andcommitted
net: tcp: Fix possible deadlock in tcp_in()
After introducing SO_SNDBUF socket option, a possible deadlock situation slipped into the TCP implementation. The scenario for the deadlock: * application thread tries to send some data, it enters net_context_send() which locks the context mutex, * internal context_sendto() blocks on a TX packet allocation, if the TX pool is empty rescheduling takes place, * now, if at the same time some incoming packet has arrived (ACK for example), TCP stack enters tcp_in() function from a different thread. The function locks the TCP connection mutex, and tries to obtain the SNDBUF option value. net_context_get_option() tries to lock the context mutex, but it is already held by the transmitting thread, so the receiver thread blocks * when TX packet is available again, the transmitting thread unblocks and tries to pass the packet down to TCP stack. net_tcp_queue_data() is called which attempts to lock the TCP connection mutex, but it is already held by the receiving thread. Both threads are in a deadlock now with no chance to recover. Fix this, by obtaining the SNDBUF option value in tcp_in() before locking the TCP connection mutex. Signed-off-by: Robert Lubos <[email protected]>
1 parent b8b5738 commit 5af3c6c

File tree

1 file changed

+9
-12
lines changed

1 file changed

+9
-12
lines changed

subsys/net/ip/tcp.c

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1744,12 +1744,19 @@ static void tcp_in(struct tcp *conn, struct net_pkt *pkt)
17441744
struct k_fifo *recv_data_fifo;
17451745
size_t len;
17461746
int ret;
1747+
int sndbuf_opt = 0;
17471748

17481749
if (th) {
17491750
/* Currently we ignore ECN and CWR flags */
17501751
fl = th_flags(th) & ~(ECN | CWR);
17511752
}
17521753

1754+
if (IS_ENABLED(CONFIG_NET_CONTEXT_SNDBUF) &&
1755+
conn->state != TCP_SYN_SENT) {
1756+
(void)net_context_get_option(conn->context, NET_OPT_SNDBUF,
1757+
&sndbuf_opt, NULL);
1758+
}
1759+
17531760
k_mutex_lock(&conn->lock, K_FOREVER);
17541761

17551762
NET_DBG("%s", log_strdup(tcp_conn_state(conn, pkt)));
@@ -1783,9 +1790,6 @@ static void tcp_in(struct tcp *conn, struct net_pkt *pkt)
17831790

17841791
if (th) {
17851792
size_t max_win;
1786-
int sndbuf;
1787-
size_t sndbuf_len;
1788-
17891793

17901794
conn->send_win = ntohs(th_win(th));
17911795

@@ -1802,15 +1806,8 @@ static void tcp_in(struct tcp *conn, struct net_pkt *pkt)
18021806
CONFIG_NET_BUF_DATA_SIZE) / 3;
18031807
}
18041808

1805-
if (IS_ENABLED(CONFIG_NET_CONTEXT_SNDBUF) &&
1806-
conn->state != TCP_SYN_SENT &&
1807-
net_context_get_option(conn->context,
1808-
NET_OPT_SNDBUF,
1809-
&sndbuf,
1810-
&sndbuf_len) == 0) {
1811-
if (sndbuf > 0) {
1812-
max_win = sndbuf;
1813-
}
1809+
if (sndbuf_opt > 0) {
1810+
max_win = sndbuf_opt;
18141811
}
18151812

18161813
max_win = MAX(max_win, NET_IPV6_MTU);

0 commit comments

Comments
 (0)