Skip to content

Commit 7656d84

Browse files
Eric Dumazetdavem330
authored andcommitted
tcp: fix fastopen races vs lockless listener
There are multiple races that need fixes : 1) skb_get() + queue skb + kfree_skb() is racy An accept() can be done on another cpu, data consumed immediately. tcp_recvmsg() uses __kfree_skb() as it is assumed all skb found in socket receive queue are private. Then the kfree_skb() in tcp_rcv_state_process() uses an already freed skb 2) tcp_reqsk_record_syn() needs to be done before tcp_try_fastopen() for the same reasons. 3) We want to send the SYNACK before queueing child into accept queue, otherwise we might reintroduce the ooo issue fixed in commit 7c85af8 ("tcp: avoid reorders for TFO passive connections") Signed-off-by: Eric Dumazet <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 3e087ca commit 7656d84

File tree

2 files changed

+12
-20
lines changed

2 files changed

+12
-20
lines changed

net/ipv4/tcp_fastopen.c

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,6 @@ static struct sock *tcp_fastopen_create_child(struct sock *sk,
168168
TCP_TIMEOUT_INIT, TCP_RTO_MAX);
169169

170170
atomic_set(&req->rsk_refcnt, 2);
171-
/* Add the child socket directly into the accept queue */
172-
inet_csk_reqsk_queue_add(sk, req, child);
173171

174172
/* Now finish processing the fastopen child socket. */
175173
inet_csk(child)->icsk_af_ops->rebuild_header(child);
@@ -178,25 +176,18 @@ static struct sock *tcp_fastopen_create_child(struct sock *sk,
178176
tcp_init_metrics(child);
179177
tcp_init_buffer_space(child);
180178

181-
/* Queue the data carried in the SYN packet. We need to first
182-
* bump skb's refcnt because the caller will attempt to free it.
183-
* Note that IPv6 might also have used skb_get() trick
184-
* in tcp_v6_conn_request() to keep this SYN around (treq->pktopts)
185-
* So we need to eventually get a clone of the packet,
186-
* before inserting it in sk_receive_queue.
179+
/* Queue the data carried in the SYN packet.
180+
* We used to play tricky games with skb_get().
181+
* With lockless listener, it is a dead end.
182+
* Do not think about it.
187183
*
188184
* XXX (TFO) - we honor a zero-payload TFO request for now,
189185
* (any reason not to?) but no need to queue the skb since
190186
* there is no data. How about SYN+FIN?
191187
*/
192188
end_seq = TCP_SKB_CB(skb)->end_seq;
193189
if (end_seq != TCP_SKB_CB(skb)->seq + 1) {
194-
struct sk_buff *skb2;
195-
196-
if (unlikely(skb_shared(skb)))
197-
skb2 = skb_clone(skb, GFP_ATOMIC);
198-
else
199-
skb2 = skb_get(skb);
190+
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
200191

201192
if (likely(skb2)) {
202193
skb_dst_drop(skb2);
@@ -214,12 +205,9 @@ static struct sock *tcp_fastopen_create_child(struct sock *sk,
214205
}
215206
}
216207
tcp_rsk(req)->rcv_nxt = tp->rcv_nxt = end_seq;
217-
sk->sk_data_ready(sk);
218-
bh_unlock_sock(child);
219-
/* Note: sock_put(child) will be done by tcp_conn_request()
220-
* after SYNACK packet is sent.
208+
/* tcp_conn_request() is sending the SYNACK,
209+
* and queues the child into listener accept queue.
221210
*/
222-
WARN_ON(!req->sk);
223211
return child;
224212
}
225213

net/ipv4/tcp_input.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6229,12 +6229,16 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
62296229
tcp_rsk(req)->txhash = net_tx_rndhash();
62306230
tcp_openreq_init_rwin(req, sk, dst);
62316231
if (!want_cookie) {
6232-
fastopen_sk = tcp_try_fastopen(sk, skb, req, &foc, dst);
62336232
tcp_reqsk_record_syn(sk, req, skb);
6233+
fastopen_sk = tcp_try_fastopen(sk, skb, req, &foc, dst);
62346234
}
62356235
if (fastopen_sk) {
62366236
af_ops->send_synack(fastopen_sk, dst, &fl, req,
62376237
skb_get_queue_mapping(skb), &foc, false);
6238+
/* Add the child socket directly into the accept queue */
6239+
inet_csk_reqsk_queue_add(sk, req, fastopen_sk);
6240+
sk->sk_data_ready(sk);
6241+
bh_unlock_sock(fastopen_sk);
62386242
sock_put(fastopen_sk);
62396243
} else {
62406244
tcp_rsk(req)->tfo_listener = false;

0 commit comments

Comments
 (0)