]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
tcp: annotate data-races around tcptw->tw_rcv_nxt
authorEric Dumazet <edumazet@google.com>
Tue, 27 Aug 2024 01:52:50 +0000 (01:52 +0000)
committerJakub Kicinski <kuba@kernel.org>
Thu, 29 Aug 2024 00:08:17 +0000 (17:08 -0700)
No lock protects tcp tw fields.

tcptw->tw_rcv_nxt can be changed from twsk_rcv_nxt_update()
while other threads might read this field.

Add READ_ONCE()/WRITE_ONCE() annotations, and make sure
tcp_timewait_state_process() reads tcptw->tw_rcv_nxt only once.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Jason Xing <kerneljasonxing@gmail.com>
Link: https://patch.msgid.link/20240827015250.3509197-3-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_minisocks.c
net/ipv6/tcp_ipv6.c

index 7c29158e1abcde9049db4dbd65d9377627f61b96..eb631e66ee03cacd079da5adb36d0639bcf1d9ef 100644 (file)
@@ -1073,7 +1073,7 @@ static void tcp_v4_timewait_ack(struct sock *sk, struct sk_buff *skb)
        }
 
        tcp_v4_send_ack(sk, skb,
-                       tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
+                       tcptw->tw_snd_nxt, READ_ONCE(tcptw->tw_rcv_nxt),
                        tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale,
                        tcp_tw_tsval(tcptw),
                        READ_ONCE(tcptw->tw_ts_recent),
index b6d547d29f9a6a91f16c5886630598079bbb50fc..ad562272db2edd901231fdbe8f35e3a5bb077bec 100644 (file)
@@ -52,16 +52,17 @@ tcp_timewait_check_oow_rate_limit(struct inet_timewait_sock *tw,
        return TCP_TW_SUCCESS;
 }
 
-static void twsk_rcv_nxt_update(struct tcp_timewait_sock *tcptw, u32 seq)
+static void twsk_rcv_nxt_update(struct tcp_timewait_sock *tcptw, u32 seq,
+                               u32 rcv_nxt)
 {
 #ifdef CONFIG_TCP_AO
        struct tcp_ao_info *ao;
 
        ao = rcu_dereference(tcptw->ao_info);
-       if (unlikely(ao && seq < tcptw->tw_rcv_nxt))
+       if (unlikely(ao && seq < rcv_nxt))
                WRITE_ONCE(ao->rcv_sne, ao->rcv_sne + 1);
 #endif
-       tcptw->tw_rcv_nxt = seq;
+       WRITE_ONCE(tcptw->tw_rcv_nxt, seq);
 }
 
 /*
@@ -98,8 +99,9 @@ enum tcp_tw_status
 tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb,
                           const struct tcphdr *th, u32 *tw_isn)
 {
-       struct tcp_options_received tmp_opt;
        struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
+       u32 rcv_nxt = READ_ONCE(tcptw->tw_rcv_nxt);
+       struct tcp_options_received tmp_opt;
        bool paws_reject = false;
        int ts_recent_stamp;
 
@@ -123,20 +125,20 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb,
                /* Out of window, send ACK */
                if (paws_reject ||
                    !tcp_in_window(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq,
-                                  tcptw->tw_rcv_nxt,
-                                  tcptw->tw_rcv_nxt + tcptw->tw_rcv_wnd))
+                                  rcv_nxt,
+                                  rcv_nxt + tcptw->tw_rcv_wnd))
                        return tcp_timewait_check_oow_rate_limit(
                                tw, skb, LINUX_MIB_TCPACKSKIPPEDFINWAIT2);
 
                if (th->rst)
                        goto kill;
 
-               if (th->syn && !before(TCP_SKB_CB(skb)->seq, tcptw->tw_rcv_nxt))
+               if (th->syn && !before(TCP_SKB_CB(skb)->seq, rcv_nxt))
                        return TCP_TW_RST;
 
                /* Dup ACK? */
                if (!th->ack ||
-                   !after(TCP_SKB_CB(skb)->end_seq, tcptw->tw_rcv_nxt) ||
+                   !after(TCP_SKB_CB(skb)->end_seq, rcv_nxt) ||
                    TCP_SKB_CB(skb)->end_seq == TCP_SKB_CB(skb)->seq) {
                        inet_twsk_put(tw);
                        return TCP_TW_SUCCESS;
@@ -146,12 +148,13 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb,
                 * reset.
                 */
                if (!th->fin ||
-                   TCP_SKB_CB(skb)->end_seq != tcptw->tw_rcv_nxt + 1)
+                   TCP_SKB_CB(skb)->end_seq != rcv_nxt + 1)
                        return TCP_TW_RST;
 
                /* FIN arrived, enter true time-wait state. */
                WRITE_ONCE(tw->tw_substate, TCP_TIME_WAIT);
-               twsk_rcv_nxt_update(tcptw, TCP_SKB_CB(skb)->end_seq);
+               twsk_rcv_nxt_update(tcptw, TCP_SKB_CB(skb)->end_seq,
+                                   rcv_nxt);
 
                if (tmp_opt.saw_tstamp) {
                        WRITE_ONCE(tcptw->tw_ts_recent_stamp,
@@ -182,7 +185,7 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb,
         */
 
        if (!paws_reject &&
-           (TCP_SKB_CB(skb)->seq == tcptw->tw_rcv_nxt &&
+           (TCP_SKB_CB(skb)->seq == rcv_nxt &&
             (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq || th->rst))) {
                /* In window segment, it may be only reset or bare ack. */
 
@@ -229,7 +232,7 @@ kill:
         */
 
        if (th->syn && !th->rst && !th->ack && !paws_reject &&
-           (after(TCP_SKB_CB(skb)->seq, tcptw->tw_rcv_nxt) ||
+           (after(TCP_SKB_CB(skb)->seq, rcv_nxt) ||
             (tmp_opt.saw_tstamp &&
              (s32)(READ_ONCE(tcptw->tw_ts_recent) - tmp_opt.rcv_tsval) < 0))) {
                u32 isn = tcptw->tw_snd_nxt + 65535 + 2;
index fb2e64ce660f8f0b7fc7bf74fb88276d3e29b0be..d71ab4e1efe1c6598cf3d3e4334adf0881064ce9 100644 (file)
@@ -1193,7 +1193,8 @@ static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb)
 #endif
        }
 
-       tcp_v6_send_ack(sk, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
+       tcp_v6_send_ack(sk, skb, tcptw->tw_snd_nxt,
+                       READ_ONCE(tcptw->tw_rcv_nxt),
                        tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale,
                        tcp_tw_tsval(tcptw),
                        READ_ONCE(tcptw->tw_ts_recent), tw->tw_bound_dev_if,