Skip to content

Commit 0525cb2

Browse files
committed
net: tcp: fix deadlock on slow peer
Unlocks the mutex needed by the code for processing ACKs while waiting for TX packets to become available. Before the mutex can be unlocked, it must be locked by tcp_syn_rcvd(), tcp_synack_received(), and backlog_ack_timeout(), which in turn requires us to unlock it in net_tcp_connect() while waiting for tcp_synack_received() to be called. Fixes #14571 Signed-off-by: Daniel Glöckner <[email protected]>
1 parent c3cf543 commit 0525cb2

File tree

3 files changed

+89
-37
lines changed

3 files changed

+89
-37
lines changed

subsys/net/ip/net_context.c

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,32 +1229,37 @@ static struct net_pkt *context_alloc_pkt(struct net_context *context,
12291229
size_t len, s32_t timeout)
12301230
{
12311231
struct net_pkt *pkt;
1232+
struct net_if *interface = net_context_get_iface(context);
1233+
sa_family_t family = net_context_get_family(context);
1234+
u16_t ip_proto = net_context_get_ip_proto(context);
12321235

12331236
#if defined(CONFIG_NET_CONTEXT_NET_PKT_POOL)
12341237
if (context->tx_slab) {
1235-
pkt = net_pkt_alloc_from_slab(context->tx_slab(), timeout);
1236-
if (!pkt) {
1237-
return NULL;
1238-
}
1238+
struct k_mem_slab *slab = context->tx_slab();
12391239

1240-
net_pkt_set_iface(pkt, net_context_get_iface(context));
1241-
net_pkt_set_family(pkt, net_context_get_family(context));
1242-
net_pkt_set_context(pkt, context);
1243-
1244-
if (net_pkt_alloc_buffer(pkt, len,
1245-
net_context_get_ip_proto(context),
1246-
timeout)) {
1247-
net_pkt_unref(pkt);
1248-
return NULL;
1240+
k_mutex_unlock(&context->lock);
1241+
pkt = net_pkt_alloc_from_slab(slab, timeout);
1242+
if (pkt != NULL) {
1243+
net_pkt_set_iface(pkt, interface);
1244+
net_pkt_set_family(pkt, family);
1245+
net_pkt_set_context(pkt, context);
1246+
1247+
if (net_pkt_alloc_buffer(pkt, len,
1248+
ip_proto,
1249+
timeout) < 0) {
1250+
net_pkt_unref(pkt);
1251+
pkt = NULL;
1252+
}
12491253
}
12501254

1255+
k_mutex_lock(&context->lock, K_FOREVER);
12511256
return pkt;
12521257
}
12531258
#endif
1254-
pkt = net_pkt_alloc_with_buffer(net_context_get_iface(context), len,
1255-
net_context_get_family(context),
1256-
net_context_get_ip_proto(context),
1259+
k_mutex_unlock(&context->lock);
1260+
pkt = net_pkt_alloc_with_buffer(interface, len, family, ip_proto,
12571261
timeout);
1262+
k_mutex_lock(&context->lock, K_FOREVER);
12581263
if (pkt) {
12591264
net_pkt_set_context(pkt, context);
12601265
}

subsys/net/ip/tcp.c

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -396,16 +396,23 @@ static int prepare_segment(struct net_tcp *tcp,
396396
pkt->buffer = NULL;
397397
pkt_allocated = false;
398398

399+
k_mutex_unlock(&context->lock);
399400
status = net_pkt_alloc_buffer(pkt, segment->optlen,
400401
IPPROTO_TCP, ALLOC_TIMEOUT);
402+
k_mutex_lock(&context->lock, K_FOREVER);
401403
if (status) {
402404
goto fail;
403405
}
404406
} else {
405-
pkt = net_pkt_alloc_with_buffer(net_context_get_iface(context),
407+
struct net_if *interface = net_context_get_iface(context);
408+
sa_family_t family = net_context_get_family(context);
409+
410+
k_mutex_unlock(&context->lock);
411+
pkt = net_pkt_alloc_with_buffer(interface,
406412
segment->optlen,
407-
net_context_get_family(context),
413+
family,
408414
IPPROTO_TCP, ALLOC_TIMEOUT);
415+
k_mutex_lock(&context->lock, K_FOREVER);
409416
if (!pkt) {
410417
return -ENOMEM;
411418
}
@@ -1459,7 +1466,9 @@ static void backlog_ack_timeout(struct k_work *work)
14591466
* RST packet might be invalid. Cache local address
14601467
* and use it in RST message preparation.
14611468
*/
1469+
k_mutex_lock(&backlog->tcp->context->lock, K_FOREVER);
14621470
send_reset(backlog->tcp->context, NULL, &backlog->remote);
1471+
k_mutex_unlock(&backlog->tcp->context->lock);
14631472

14641473
(void)memset(backlog, 0, sizeof(struct tcp_backlog_entry));
14651474
}
@@ -2099,7 +2108,9 @@ NET_CONN_CB(tcp_synack_received)
20992108
struct net_tcp_hdr *tcp_hdr = proto_hdr->tcp;
21002109
int ret;
21012110

2102-
NET_ASSERT(context && context->tcp);
2111+
NET_ASSERT(context);
2112+
k_mutex_lock(&context->lock, K_FOREVER);
2113+
NET_ASSERT(context->tcp); /* protected by lock */
21032114

21042115
switch (net_tcp_get_state(context->tcp)) {
21052116
case NET_TCP_SYN_SENT:
@@ -2108,7 +2119,7 @@ NET_CONN_CB(tcp_synack_received)
21082119
default:
21092120
NET_DBG("Context %p in wrong state %d",
21102121
context, net_tcp_get_state(context->tcp));
2111-
return NET_DROP;
2122+
goto unlock;
21122123
}
21132124

21142125
net_pkt_set_context(pkt, context);
@@ -2119,7 +2130,7 @@ NET_CONN_CB(tcp_synack_received)
21192130
/* We only accept RST packet that has valid seq field. */
21202131
if (!net_tcp_validate_seq(context->tcp, tcp_hdr)) {
21212132
net_stats_update_tcp_seg_rsterr(net_pkt_iface(pkt));
2122-
return NET_DROP;
2133+
goto unlock;
21232134
}
21242135

21252136
net_stats_update_tcp_seg_rst(net_pkt_iface(pkt));
@@ -2131,7 +2142,7 @@ NET_CONN_CB(tcp_synack_received)
21312142
context->user_data);
21322143
}
21332144

2134-
return NET_DROP;
2145+
goto unlock;
21352146
}
21362147

21372148
if (NET_TCP_FLAGS(tcp_hdr) & NET_TCP_SYN) {
@@ -2166,7 +2177,7 @@ NET_CONN_CB(tcp_synack_received)
21662177
if (ret < 0) {
21672178
NET_DBG("Cannot register TCP handler (%d)", ret);
21682179
send_reset(context, &local_addr, &remote_addr);
2169-
return NET_DROP;
2180+
goto unlock;
21702181
}
21712182

21722183
net_tcp_change_state(context->tcp, NET_TCP_ESTABLISHED);
@@ -2181,6 +2192,8 @@ NET_CONN_CB(tcp_synack_received)
21812192
}
21822193
}
21832194

2195+
unlock:
2196+
k_mutex_unlock(&context->lock);
21842197
return NET_DROP;
21852198
}
21862199

@@ -2239,8 +2252,11 @@ NET_CONN_CB(tcp_syn_rcvd)
22392252
struct sockaddr_ptr pkt_src_addr;
22402253
struct sockaddr local_addr;
22412254
struct sockaddr remote_addr;
2255+
enum net_verdict verdict = NET_DROP;
22422256

2243-
NET_ASSERT(context && context->tcp);
2257+
NET_ASSERT(context);
2258+
k_mutex_lock(&context->lock, K_FOREVER);
2259+
NET_ASSERT(context->tcp); /* protected by lock */
22442260

22452261
tcp = context->tcp;
22462262

@@ -2250,13 +2266,13 @@ NET_CONN_CB(tcp_syn_rcvd)
22502266
break;
22512267
case NET_TCP_SYN_RCVD:
22522268
if (net_pkt_iface(pkt) != net_context_get_iface(context)) {
2253-
return NET_DROP;
2269+
goto unlock;
22542270
}
22552271
break;
22562272
default:
22572273
NET_DBG("Context %p in wrong state %d",
22582274
context, tcp->state);
2259-
return NET_DROP;
2275+
goto unlock;
22602276
}
22612277

22622278
net_pkt_set_context(pkt, context);
@@ -2286,7 +2302,7 @@ NET_CONN_CB(tcp_syn_rcvd)
22862302
* so call unconditionally.
22872303
*/
22882304
if (net_tcp_parse_opts(pkt, opt_totlen, &tcp_opts) < 0) {
2289-
return NET_DROP;
2305+
goto unlock;
22902306
}
22912307

22922308
net_tcp_change_state(tcp, NET_TCP_SYN_RCVD);
@@ -2307,15 +2323,16 @@ NET_CONN_CB(tcp_syn_rcvd)
23072323
NET_DBG("No free TCP backlog entries");
23082324
}
23092325

2310-
return NET_DROP;
2326+
goto unlock;
23112327
}
23122328

23132329
get_sockaddr_ptr(ip_hdr, tcp_hdr,
23142330
net_context_get_family(context),
23152331
&pkt_src_addr);
23162332
send_syn_ack(context, &pkt_src_addr, &remote_addr);
23172333
net_pkt_unref(pkt);
2318-
return NET_OK;
2334+
verdict = NET_OK;
2335+
goto unlock;
23192336
}
23202337

23212338
/*
@@ -2326,14 +2343,14 @@ NET_CONN_CB(tcp_syn_rcvd)
23262343

23272344
if (tcp_backlog_rst(pkt, ip_hdr, tcp_hdr) < 0) {
23282345
net_stats_update_tcp_seg_rsterr(net_pkt_iface(pkt));
2329-
return NET_DROP;
2346+
goto unlock;
23302347
}
23312348

23322349
net_stats_update_tcp_seg_rst(net_pkt_iface(pkt));
23332350

23342351
net_tcp_print_recv_info("RST", pkt, tcp_hdr->src_port);
23352352

2336-
return NET_DROP;
2353+
goto unlock;
23372354
}
23382355

23392356
/*
@@ -2423,7 +2440,7 @@ NET_CONN_CB(tcp_syn_rcvd)
24232440
NET_ASSERT_INFO(false, "Invalid protocol family %d",
24242441
new_context->remote.sa_family);
24252442
net_context_unref(new_context);
2426-
return NET_DROP;
2443+
goto unlock;
24272444
}
24282445

24292446
context->tcp->accept_cb(new_context,
@@ -2432,18 +2449,20 @@ NET_CONN_CB(tcp_syn_rcvd)
24322449
0,
24332450
context->user_data);
24342451
net_pkt_unref(pkt);
2435-
return NET_OK;
2452+
verdict = NET_OK;
24362453
}
24372454

2438-
return NET_DROP;
2455+
goto unlock;
24392456

24402457
conndrop:
24412458
net_stats_update_tcp_seg_conndrop(net_pkt_iface(pkt));
24422459

24432460
reset:
24442461
send_reset(tcp->context, &local_addr, &remote_addr);
24452462

2446-
return NET_DROP;
2463+
unlock:
2464+
k_mutex_unlock(&context->lock);
2465+
return verdict;
24472466
}
24482467

24492468
int net_tcp_accept(struct net_context *context,
@@ -2563,12 +2582,16 @@ int net_tcp_connect(struct net_context *context,
25632582

25642583
send_syn(context, addr);
25652584

2585+
k_mutex_unlock(&context->lock);
25662586
/* in tcp_synack_received() we give back this semaphore */
25672587
if (timeout != 0 && k_sem_take(&context->tcp->connect_wait, timeout)) {
2568-
return -ETIMEDOUT;
2588+
ret = -ETIMEDOUT;
2589+
} else {
2590+
ret = 0;
25692591
}
2592+
k_mutex_lock(&context->lock, K_FOREVER);
25702593

2571-
return 0;
2594+
return ret;
25722595
}
25732596

25742597
struct net_tcp_hdr *net_tcp_input(struct net_pkt *pkt,

0 commit comments

Comments
 (0)