Commit 1227c177 authored by Kuniyuki Iwashima's avatar Kuniyuki Iwashima Committed by David S. Miller
Browse files

net: Fix data-races around sysctl_[rw]mem_(max|default).



While reading sysctl_[rw]mem_(max|default), they can be changed
concurrently.  Thus, we need to add READ_ONCE() to its readers.

Fixes: 1da177e4 ("Linux-2.6.12-rc2")
Signed-off-by: default avatarKuniyuki Iwashima <kuniyu@amazon.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c624c58e
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -5034,14 +5034,14 @@ static int __bpf_setsockopt(struct sock *sk, int level, int optname,
		/* Only some socketops are supported */
		switch (optname) {
		case SO_RCVBUF:
			val = min_t(u32, val, sysctl_rmem_max);
			val = min_t(u32, val, READ_ONCE(sysctl_rmem_max));
			val = min_t(int, val, INT_MAX / 2);
			sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
			WRITE_ONCE(sk->sk_rcvbuf,
				   max_t(int, val * 2, SOCK_MIN_RCVBUF));
			break;
		case SO_SNDBUF:
			val = min_t(u32, val, sysctl_wmem_max);
			val = min_t(u32, val, READ_ONCE(sysctl_wmem_max));
			val = min_t(int, val, INT_MAX / 2);
			sk->sk_userlocks |= SOCK_SNDBUF_LOCK;
			WRITE_ONCE(sk->sk_sndbuf,
+4 −4
Original line number Diff line number Diff line
@@ -1101,7 +1101,7 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
		 * play 'guess the biggest size' games. RCVBUF/SNDBUF
		 * are treated in BSD as hints
		 */
		val = min_t(u32, val, sysctl_wmem_max);
		val = min_t(u32, val, READ_ONCE(sysctl_wmem_max));
set_sndbuf:
		/* Ensure val * 2 fits into an int, to prevent max_t()
		 * from treating it as a negative value.
@@ -1133,7 +1133,7 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
		 * play 'guess the biggest size' games. RCVBUF/SNDBUF
		 * are treated in BSD as hints
		 */
		__sock_set_rcvbuf(sk, min_t(u32, val, sysctl_rmem_max));
		__sock_set_rcvbuf(sk, min_t(u32, val, READ_ONCE(sysctl_rmem_max)));
		break;

	case SO_RCVBUFFORCE:
@@ -3309,8 +3309,8 @@ void sock_init_data(struct socket *sock, struct sock *sk)
	timer_setup(&sk->sk_timer, NULL, 0);

	sk->sk_allocation	=	GFP_KERNEL;
	sk->sk_rcvbuf		=	sysctl_rmem_default;
	sk->sk_sndbuf		=	sysctl_wmem_default;
	sk->sk_rcvbuf		=	READ_ONCE(sysctl_rmem_default);
	sk->sk_sndbuf		=	READ_ONCE(sysctl_wmem_default);
	sk->sk_state		=	TCP_CLOSE;
	sk_set_socket(sk, sock);

+1 −1
Original line number Diff line number Diff line
@@ -1730,7 +1730,7 @@ void ip_send_unicast_reply(struct sock *sk, struct sk_buff *skb,

	sk->sk_protocol = ip_hdr(skb)->protocol;
	sk->sk_bound_dev_if = arg->bound_dev_if;
	sk->sk_sndbuf = sysctl_wmem_default;
	sk->sk_sndbuf = READ_ONCE(sysctl_wmem_default);
	ipc.sockc.mark = fl4.flowi4_mark;
	err = ip_append_data(sk, &fl4, ip_reply_glue_bits, arg->iov->iov_base,
			     len, 0, &ipc, &rt, MSG_DONTWAIT);
+1 −1
Original line number Diff line number Diff line
@@ -239,7 +239,7 @@ void tcp_select_initial_window(const struct sock *sk, int __space, __u32 mss,
	if (wscale_ok) {
		/* Set window scaling on max possible window */
		space = max_t(u32, space, READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_rmem[2]));
		space = max_t(u32, space, sysctl_rmem_max);
		space = max_t(u32, space, READ_ONCE(sysctl_rmem_max));
		space = min_t(u32, space, *window_clamp);
		*rcv_wscale = clamp_t(int, ilog2(space) - 15,
				      0, TCP_MAX_WSCALE);
+2 −2
Original line number Diff line number Diff line
@@ -1280,12 +1280,12 @@ static void set_sock_size(struct sock *sk, int mode, int val)
	lock_sock(sk);
	if (mode) {
		val = clamp_t(int, val, (SOCK_MIN_SNDBUF + 1) / 2,
			      sysctl_wmem_max);
			      READ_ONCE(sysctl_wmem_max));
		sk->sk_sndbuf = val * 2;
		sk->sk_userlocks |= SOCK_SNDBUF_LOCK;
	} else {
		val = clamp_t(int, val, (SOCK_MIN_RCVBUF + 1) / 2,
			      sysctl_rmem_max);
			      READ_ONCE(sysctl_rmem_max));
		sk->sk_rcvbuf = val * 2;
		sk->sk_userlocks |= SOCK_RCVBUF_LOCK;
	}