Skip to content

Commit d983ea6

Browse files
Eric Dumazetdavem330
Eric Dumazet
authored andcommitted
tcp: add rcu protection around tp->fastopen_rsk
Both tcp_v4_err() and tcp_v6_err() do the following operations while they do not own the socket lock : fastopen = tp->fastopen_rsk; snd_una = fastopen ? tcp_rsk(fastopen)->snt_isn : tp->snd_una; The problem is that without appropriate barrier, the compiler might reload tp->fastopen_rsk and trigger a NULL deref. request sockets are protected by RCU, we can simply add the missing annotations and barriers to solve the issue. Fixes: 168a8f5 ("tcp: TCP Fast Open Server - main code path") Signed-off-by: Eric Dumazet <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 8caf8a9 commit d983ea6

File tree

11 files changed

+35
-24
lines changed

11 files changed

+35
-24
lines changed

include/linux/tcp.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ struct tcp_sock {
393393
/* fastopen_rsk points to request_sock that resulted in this big
394394
* socket. Used to retransmit SYNACKs etc.
395395
*/
396-
struct request_sock *fastopen_rsk;
396+
struct request_sock __rcu *fastopen_rsk;
397397
u32 *saved_syn;
398398
};
399399

@@ -447,8 +447,8 @@ static inline struct tcp_timewait_sock *tcp_twsk(const struct sock *sk)
447447

448448
static inline bool tcp_passive_fastopen(const struct sock *sk)
449449
{
450-
return (sk->sk_state == TCP_SYN_RECV &&
451-
tcp_sk(sk)->fastopen_rsk != NULL);
450+
return sk->sk_state == TCP_SYN_RECV &&
451+
rcu_access_pointer(tcp_sk(sk)->fastopen_rsk) != NULL;
452452
}
453453

454454
static inline void fastopen_queue_tune(struct sock *sk, int backlog)

net/core/request_sock.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ void reqsk_fastopen_remove(struct sock *sk, struct request_sock *req,
9696

9797
fastopenq = &inet_csk(lsk)->icsk_accept_queue.fastopenq;
9898

99-
tcp_sk(sk)->fastopen_rsk = NULL;
99+
RCU_INIT_POINTER(tcp_sk(sk)->fastopen_rsk, NULL);
100100
spin_lock_bh(&fastopenq->lock);
101101
fastopenq->qlen--;
102102
tcp_rsk(req)->tfo_listener = false;

net/ipv4/inet_connection_sock.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -906,7 +906,7 @@ static void inet_child_forget(struct sock *sk, struct request_sock *req,
906906
percpu_counter_inc(sk->sk_prot->orphan_count);
907907

908908
if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(req)->tfo_listener) {
909-
BUG_ON(tcp_sk(child)->fastopen_rsk != req);
909+
BUG_ON(rcu_access_pointer(tcp_sk(child)->fastopen_rsk) != req);
910910
BUG_ON(sk != req->rsk_listener);
911911

912912
/* Paranoid, to prevent race condition if
@@ -915,7 +915,7 @@ static void inet_child_forget(struct sock *sk, struct request_sock *req,
915915
* Also to satisfy an assertion in
916916
* tcp_v4_destroy_sock().
917917
*/
918-
tcp_sk(child)->fastopen_rsk = NULL;
918+
RCU_INIT_POINTER(tcp_sk(child)->fastopen_rsk, NULL);
919919
}
920920
inet_csk_destroy_sock(child);
921921
}

net/ipv4/tcp.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ __poll_t tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
543543

544544
/* Connected or passive Fast Open socket? */
545545
if (state != TCP_SYN_SENT &&
546-
(state != TCP_SYN_RECV || tp->fastopen_rsk)) {
546+
(state != TCP_SYN_RECV || rcu_access_pointer(tp->fastopen_rsk))) {
547547
int target = sock_rcvlowat(sk, 0, INT_MAX);
548548

549549
if (tp->urg_seq == tp->copied_seq &&
@@ -2487,7 +2487,10 @@ void tcp_close(struct sock *sk, long timeout)
24872487
}
24882488

24892489
if (sk->sk_state == TCP_CLOSE) {
2490-
struct request_sock *req = tcp_sk(sk)->fastopen_rsk;
2490+
struct request_sock *req;
2491+
2492+
req = rcu_dereference_protected(tcp_sk(sk)->fastopen_rsk,
2493+
lockdep_sock_is_held(sk));
24912494
/* We could get here with a non-NULL req if the socket is
24922495
* aborted (e.g., closed with unread data) before 3WHS
24932496
* finishes.
@@ -3831,8 +3834,10 @@ EXPORT_SYMBOL(tcp_md5_hash_key);
38313834

38323835
void tcp_done(struct sock *sk)
38333836
{
3834-
struct request_sock *req = tcp_sk(sk)->fastopen_rsk;
3837+
struct request_sock *req;
38353838

3839+
req = rcu_dereference_protected(tcp_sk(sk)->fastopen_rsk,
3840+
lockdep_sock_is_held(sk));
38363841
if (sk->sk_state == TCP_SYN_SENT || sk->sk_state == TCP_SYN_RECV)
38373842
TCP_INC_STATS(sock_net(sk), TCP_MIB_ATTEMPTFAILS);
38383843

net/ipv4/tcp_fastopen.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ static struct sock *tcp_fastopen_create_child(struct sock *sk,
253253
*/
254254
tp = tcp_sk(child);
255255

256-
tp->fastopen_rsk = req;
256+
rcu_assign_pointer(tp->fastopen_rsk, req);
257257
tcp_rsk(req)->tfo_listener = true;
258258

259259
/* RFC1323: The window in SYN & SYN/ACK segments is never

net/ipv4/tcp_input.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2666,7 +2666,7 @@ static void tcp_process_loss(struct sock *sk, int flag, int num_dupack,
26662666
struct tcp_sock *tp = tcp_sk(sk);
26672667
bool recovered = !before(tp->snd_una, tp->high_seq);
26682668

2669-
if ((flag & FLAG_SND_UNA_ADVANCED || tp->fastopen_rsk) &&
2669+
if ((flag & FLAG_SND_UNA_ADVANCED || rcu_access_pointer(tp->fastopen_rsk)) &&
26702670
tcp_try_undo_loss(sk, false))
26712671
return;
26722672

@@ -2990,7 +2990,7 @@ void tcp_rearm_rto(struct sock *sk)
29902990
/* If the retrans timer is currently being used by Fast Open
29912991
* for SYN-ACK retrans purpose, stay put.
29922992
*/
2993-
if (tp->fastopen_rsk)
2993+
if (rcu_access_pointer(tp->fastopen_rsk))
29942994
return;
29952995

29962996
if (!tp->packets_out) {
@@ -6087,6 +6087,8 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
60876087

60886088
static void tcp_rcv_synrecv_state_fastopen(struct sock *sk)
60896089
{
6090+
struct request_sock *req;
6091+
60906092
tcp_try_undo_loss(sk, false);
60916093

60926094
/* Reset rtx states to prevent spurious retransmits_timed_out() */
@@ -6096,7 +6098,9 @@ static void tcp_rcv_synrecv_state_fastopen(struct sock *sk)
60966098
/* Once we leave TCP_SYN_RECV or TCP_FIN_WAIT_1,
60976099
* we no longer need req so release it.
60986100
*/
6099-
reqsk_fastopen_remove(sk, tcp_sk(sk)->fastopen_rsk, false);
6101+
req = rcu_dereference_protected(tcp_sk(sk)->fastopen_rsk,
6102+
lockdep_sock_is_held(sk));
6103+
reqsk_fastopen_remove(sk, req, false);
61006104

61016105
/* Re-arm the timer because data may have been sent out.
61026106
* This is similar to the regular data transmission case
@@ -6171,7 +6175,8 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb)
61716175

61726176
tcp_mstamp_refresh(tp);
61736177
tp->rx_opt.saw_tstamp = 0;
6174-
req = tp->fastopen_rsk;
6178+
req = rcu_dereference_protected(tp->fastopen_rsk,
6179+
lockdep_sock_is_held(sk));
61756180
if (req) {
61766181
bool req_stolen;
61776182

net/ipv4/tcp_ipv4.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ int tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
478478
icsk = inet_csk(sk);
479479
tp = tcp_sk(sk);
480480
/* XXX (TFO) - tp->snd_una should be ISN (tcp_create_openreq_child() */
481-
fastopen = tp->fastopen_rsk;
481+
fastopen = rcu_dereference(tp->fastopen_rsk);
482482
snd_una = fastopen ? tcp_rsk(fastopen)->snt_isn : tp->snd_una;
483483
if (sk->sk_state != TCP_LISTEN &&
484484
!between(seq, snd_una, tp->snd_nxt)) {
@@ -2121,7 +2121,7 @@ void tcp_v4_destroy_sock(struct sock *sk)
21212121
if (inet_csk(sk)->icsk_bind_hash)
21222122
inet_put_port(sk);
21232123

2124-
BUG_ON(tp->fastopen_rsk);
2124+
BUG_ON(rcu_access_pointer(tp->fastopen_rsk));
21252125

21262126
/* If socket is aborted during connect operation */
21272127
tcp_free_fastopen_req(tp);

net/ipv4/tcp_minisocks.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,7 @@ struct sock *tcp_create_openreq_child(const struct sock *sk,
541541
newtp->rx_opt.mss_clamp = req->mss;
542542
tcp_ecn_openreq_child(newtp, req);
543543
newtp->fastopen_req = NULL;
544-
newtp->fastopen_rsk = NULL;
544+
RCU_INIT_POINTER(newtp->fastopen_rsk, NULL);
545545

546546
__TCP_INC_STATS(sock_net(sk), TCP_MIB_PASSIVEOPENS);
547547

net/ipv4/tcp_output.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2482,7 +2482,7 @@ bool tcp_schedule_loss_probe(struct sock *sk, bool advancing_rto)
24822482
/* Don't do any loss probe on a Fast Open connection before 3WHS
24832483
* finishes.
24842484
*/
2485-
if (tp->fastopen_rsk)
2485+
if (rcu_access_pointer(tp->fastopen_rsk))
24862486
return false;
24872487

24882488
early_retrans = sock_net(sk)->ipv4.sysctl_tcp_early_retrans;

net/ipv4/tcp_timer.c

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -386,15 +386,13 @@ abort: tcp_write_err(sk);
386386
* Timer for Fast Open socket to retransmit SYNACK. Note that the
387387
* sk here is the child socket, not the parent (listener) socket.
388388
*/
389-
static void tcp_fastopen_synack_timer(struct sock *sk)
389+
static void tcp_fastopen_synack_timer(struct sock *sk, struct request_sock *req)
390390
{
391391
struct inet_connection_sock *icsk = inet_csk(sk);
392392
int max_retries = icsk->icsk_syn_retries ? :
393393
sock_net(sk)->ipv4.sysctl_tcp_synack_retries + 1; /* add one more retry for fastopen */
394394
struct tcp_sock *tp = tcp_sk(sk);
395-
struct request_sock *req;
396395

397-
req = tcp_sk(sk)->fastopen_rsk;
398396
req->rsk_ops->syn_ack_timeout(req);
399397

400398
if (req->num_timeout >= max_retries) {
@@ -435,11 +433,14 @@ void tcp_retransmit_timer(struct sock *sk)
435433
struct tcp_sock *tp = tcp_sk(sk);
436434
struct net *net = sock_net(sk);
437435
struct inet_connection_sock *icsk = inet_csk(sk);
436+
struct request_sock *req;
438437

439-
if (tp->fastopen_rsk) {
438+
req = rcu_dereference_protected(tp->fastopen_rsk,
439+
lockdep_sock_is_held(sk));
440+
if (req) {
440441
WARN_ON_ONCE(sk->sk_state != TCP_SYN_RECV &&
441442
sk->sk_state != TCP_FIN_WAIT1);
442-
tcp_fastopen_synack_timer(sk);
443+
tcp_fastopen_synack_timer(sk, req);
443444
/* Before we receive ACK to our SYN-ACK don't retransmit
444445
* anything else (e.g., data or FIN segments).
445446
*/

net/ipv6/tcp_ipv6.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ static int tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
406406

407407
tp = tcp_sk(sk);
408408
/* XXX (TFO) - tp->snd_una should be ISN (tcp_create_openreq_child() */
409-
fastopen = tp->fastopen_rsk;
409+
fastopen = rcu_dereference(tp->fastopen_rsk);
410410
snd_una = fastopen ? tcp_rsk(fastopen)->snt_isn : tp->snd_una;
411411
if (sk->sk_state != TCP_LISTEN &&
412412
!between(seq, snd_una, tp->snd_nxt)) {

0 commit comments

Comments
 (0)