Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tcp: Fix data-races around sysctl_tcp_syncookies.

While reading sysctl_tcp_syncookies, it can be changed concurrently.
Thus, we need to add READ_ONCE() to its readers.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Kuniyuki Iwashima and committed by
David S. Miller
f2e383b5 20a3b1c0

+18 -12
+2 -2
net/core/filter.c
··· 7041 7041 if (sk->sk_protocol != IPPROTO_TCP || sk->sk_state != TCP_LISTEN) 7042 7042 return -EINVAL; 7043 7043 7044 - if (!sock_net(sk)->ipv4.sysctl_tcp_syncookies) 7044 + if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies)) 7045 7045 return -EINVAL; 7046 7046 7047 7047 if (!th->ack || th->rst || th->syn) ··· 7116 7116 if (sk->sk_protocol != IPPROTO_TCP || sk->sk_state != TCP_LISTEN) 7117 7117 return -EINVAL; 7118 7118 7119 - if (!sock_net(sk)->ipv4.sysctl_tcp_syncookies) 7119 + if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies)) 7120 7120 return -ENOENT; 7121 7121 7122 7122 if (!th->syn || th->ack || th->fin || th->rst)
+2 -1
net/ipv4/syncookies.c
··· 340 340 struct flowi4 fl4; 341 341 u32 tsoff = 0; 342 342 343 - if (!sock_net(sk)->ipv4.sysctl_tcp_syncookies || !th->ack || th->rst) 343 + if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies) || 344 + !th->ack || th->rst) 344 345 goto out; 345 346 346 347 if (tcp_synq_no_recent_overflow(sk))
+12 -8
net/ipv4/tcp_input.c
··· 6797 6797 { 6798 6798 struct request_sock_queue *queue = &inet_csk(sk)->icsk_accept_queue; 6799 6799 const char *msg = "Dropping request"; 6800 - bool want_cookie = false; 6801 6800 struct net *net = sock_net(sk); 6801 + bool want_cookie = false; 6802 + u8 syncookies; 6803 + 6804 + syncookies = READ_ONCE(net->ipv4.sysctl_tcp_syncookies); 6802 6805 6803 6806 #ifdef CONFIG_SYN_COOKIES 6804 - if (net->ipv4.sysctl_tcp_syncookies) { 6807 + if (syncookies) { 6805 6808 msg = "Sending cookies"; 6806 6809 want_cookie = true; 6807 6810 __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPREQQFULLDOCOOKIES); ··· 6812 6809 #endif 6813 6810 __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPREQQFULLDROP); 6814 6811 6815 - if (!queue->synflood_warned && 6816 - net->ipv4.sysctl_tcp_syncookies != 2 && 6812 + if (!queue->synflood_warned && syncookies != 2 && 6817 6813 xchg(&queue->synflood_warned, 1) == 0) 6818 6814 net_info_ratelimited("%s: Possible SYN flooding on port %d. %s. Check SNMP counters.\n", 6819 6815 proto, sk->sk_num, msg); ··· 6861 6859 struct tcp_sock *tp = tcp_sk(sk); 6862 6860 u16 mss; 6863 6861 6864 - if (sock_net(sk)->ipv4.sysctl_tcp_syncookies != 2 && 6862 + if (READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies) != 2 && 6865 6863 !inet_csk_reqsk_queue_is_full(sk)) 6866 6864 return 0; 6867 6865 ··· 6895 6893 bool want_cookie = false; 6896 6894 struct dst_entry *dst; 6897 6895 struct flowi fl; 6896 + u8 syncookies; 6897 + 6898 + syncookies = READ_ONCE(net->ipv4.sysctl_tcp_syncookies); 6898 6899 6899 6900 /* TW buckets are converted to open requests without 6900 6901 * limitations, they conserve resources and peer is 6901 6902 * evidently real one. 6902 6903 */ 6903 - if ((net->ipv4.sysctl_tcp_syncookies == 2 || 6904 - inet_csk_reqsk_queue_is_full(sk)) && !isn) { 6904 + if ((syncookies == 2 || inet_csk_reqsk_queue_is_full(sk)) && !isn) { 6905 6905 want_cookie = tcp_syn_flood_action(sk, rsk_ops->slab_name); 6906 6906 if (!want_cookie) 6907 6907 goto drop; ··· 6953 6949 6954 6950 if (!want_cookie && !isn) { 6955 6951 /* Kill the following clause, if you dislike this way. */ 6956 - if (!net->ipv4.sysctl_tcp_syncookies && 6952 + if (!syncookies && 6957 6953 (net->ipv4.sysctl_max_syn_backlog - inet_csk_reqsk_queue_len(sk) < 6958 6954 (net->ipv4.sysctl_max_syn_backlog >> 2)) && 6959 6955 !tcp_peer_is_proven(req, dst)) {
+2 -1
net/ipv6/syncookies.c
··· 141 141 __u8 rcv_wscale; 142 142 u32 tsoff = 0; 143 143 144 - if (!sock_net(sk)->ipv4.sysctl_tcp_syncookies || !th->ack || th->rst) 144 + if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies) || 145 + !th->ack || th->rst) 145 146 goto out; 146 147 147 148 if (tcp_synq_no_recent_overflow(sk))