tcp: annotate races around tp->urg_data
authorEric Dumazet <edumazet@google.com>
Mon, 15 Nov 2021 19:02:43 +0000 (11:02 -0800)
committerDavid S. Miller <davem@davemloft.net>
Tue, 16 Nov 2021 13:10:34 +0000 (13:10 +0000)
tcp_poll() and tcp_ioctl() are reading tp->urg_data without socket lock
owned.

Also, it is faster to first check tp->urg_data in tcp_poll(),
then tp->urg_seq == tp->copied_seq, because tp->urg_seq is
located in a different/cold cache line.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/tcp.c
net/ipv4/tcp_input.c

index 267b2b18f048c4df4cabd819433a99bf8b3f2678..313cf648c349a24ab7a04729180ec9b76b2f6aa2 100644 (file)
@@ -545,10 +545,11 @@ __poll_t tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
        if (state != TCP_SYN_SENT &&
            (state != TCP_SYN_RECV || rcu_access_pointer(tp->fastopen_rsk))) {
                int target = sock_rcvlowat(sk, 0, INT_MAX);
+               u16 urg_data = READ_ONCE(tp->urg_data);
 
-               if (READ_ONCE(tp->urg_seq) == READ_ONCE(tp->copied_seq) &&
-                   !sock_flag(sk, SOCK_URGINLINE) &&
-                   tp->urg_data)
+               if (urg_data &&
+                   READ_ONCE(tp->urg_seq) == READ_ONCE(tp->copied_seq) &&
+                   !sock_flag(sk, SOCK_URGINLINE))
                        target++;
 
                if (tcp_stream_is_readable(sk, target))
@@ -573,7 +574,7 @@ __poll_t tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
                } else
                        mask |= EPOLLOUT | EPOLLWRNORM;
 
-               if (tp->urg_data & TCP_URG_VALID)
+               if (urg_data & TCP_URG_VALID)
                        mask |= EPOLLPRI;
        } else if (state == TCP_SYN_SENT && inet_sk(sk)->defer_connect) {
                /* Active TCP fastopen socket with defer_connect
@@ -607,7 +608,7 @@ int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
                unlock_sock_fast(sk, slow);
                break;
        case SIOCATMARK:
-               answ = tp->urg_data &&
+               answ = READ_ONCE(tp->urg_data) &&
                       READ_ONCE(tp->urg_seq) == READ_ONCE(tp->copied_seq);
                break;
        case SIOCOUTQ:
@@ -1465,7 +1466,7 @@ static int tcp_recv_urg(struct sock *sk, struct msghdr *msg, int len, int flags)
                char c = tp->urg_data;
 
                if (!(flags & MSG_PEEK))
-                       tp->urg_data = TCP_URG_READ;
+                       WRITE_ONCE(tp->urg_data, TCP_URG_READ);
 
                /* Read urgent data. */
                msg->msg_flags |= MSG_OOB;
@@ -2465,7 +2466,7 @@ found_ok_skb:
 
 skip_copy:
                if (tp->urg_data && after(tp->copied_seq, tp->urg_seq)) {
-                       tp->urg_data = 0;
+                       WRITE_ONCE(tp->urg_data, 0);
                        tcp_fast_path_check(sk);
                }
 
@@ -2959,7 +2960,7 @@ int tcp_disconnect(struct sock *sk, int flags)
        tcp_clear_xmit_timers(sk);
        __skb_queue_purge(&sk->sk_receive_queue);
        WRITE_ONCE(tp->copied_seq, tp->rcv_nxt);
-       tp->urg_data = 0;
+       WRITE_ONCE(tp->urg_data, 0);
        tcp_write_queue_purge(sk);
        tcp_fastopen_active_disable_ofo_check(sk);
        skb_rbtree_purge(&tp->out_of_order_queue);
index 246ab7b5e857eb9e802c4805075e89c98cf00636..5ee07a337652696bdebb1117334ff39d88fd0276 100644 (file)
@@ -5591,7 +5591,7 @@ static void tcp_check_urg(struct sock *sk, const struct tcphdr *th)
                }
        }
 
-       tp->urg_data = TCP_URG_NOTYET;
+       WRITE_ONCE(tp->urg_data, TCP_URG_NOTYET);
        WRITE_ONCE(tp->urg_seq, ptr);
 
        /* Disable header prediction. */
@@ -5617,7 +5617,7 @@ static void tcp_urg(struct sock *sk, struct sk_buff *skb, const struct tcphdr *t
                        u8 tmp;
                        if (skb_copy_bits(skb, ptr, &tmp, 1))
                                BUG();
-                       tp->urg_data = TCP_URG_VALID | tmp;
+                       WRITE_ONCE(tp->urg_data, TCP_URG_VALID | tmp);
                        if (!sock_flag(sk, SOCK_DEAD))
                                sk->sk_data_ready(sk);
                }