diff options
Diffstat (limited to 'net/ipv4/tcp.c')
| -rw-r--r-- | net/ipv4/tcp.c | 100 | 
1 files changed, 37 insertions, 63 deletions
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 810cc164f795..31f3b858db81 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2691,6 +2691,9 @@ int tcp_disconnect(struct sock *sk, int flags)  	tp->window_clamp = 0;  	tp->delivered = 0;  	tp->delivered_ce = 0; +	if (icsk->icsk_ca_ops->release) +		icsk->icsk_ca_ops->release(sk); +	memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv));  	tcp_set_ca_state(sk, TCP_CA_Open);  	tp->is_sack_reneg = 0;  	tcp_clear_retrans(tp); @@ -2761,7 +2764,7 @@ static inline bool tcp_can_repair_sock(const struct sock *sk)  		(sk->sk_state != TCP_LISTEN);  } -static int tcp_repair_set_window(struct tcp_sock *tp, char __user *optbuf, int len) +static int tcp_repair_set_window(struct tcp_sock *tp, sockptr_t optbuf, int len)  {  	struct tcp_repair_window opt; @@ -2771,7 +2774,7 @@ static int tcp_repair_set_window(struct tcp_sock *tp, char __user *optbuf, int l  	if (len != sizeof(opt))  		return -EINVAL; -	if (copy_from_user(&opt, optbuf, sizeof(opt))) +	if (copy_from_sockptr(&opt, optbuf, sizeof(opt)))  		return -EFAULT;  	if (opt.max_window < opt.snd_wnd) @@ -2793,17 +2796,18 @@ static int tcp_repair_set_window(struct tcp_sock *tp, char __user *optbuf, int l  	return 0;  } -static int tcp_repair_options_est(struct sock *sk, -		struct tcp_repair_opt __user *optbuf, unsigned int len) +static int tcp_repair_options_est(struct sock *sk, sockptr_t optbuf, +		unsigned int len)  {  	struct tcp_sock *tp = tcp_sk(sk);  	struct tcp_repair_opt opt; +	size_t offset = 0;  	while (len >= sizeof(opt)) { -		if (copy_from_user(&opt, optbuf, sizeof(opt))) +		if (copy_from_sockptr_offset(&opt, optbuf, offset, sizeof(opt)))  			return -EFAULT; -		optbuf++; +		offset += sizeof(opt);  		len -= sizeof(opt);  		switch (opt.opt_code) { @@ -2957,7 +2961,7 @@ void tcp_sock_set_user_timeout(struct sock *sk, u32 val)  }  EXPORT_SYMBOL(tcp_sock_set_user_timeout); -static int __tcp_sock_set_keepidle(struct sock *sk, int val) +int tcp_sock_set_keepidle_locked(struct sock *sk, int val)  {  	struct tcp_sock *tp = tcp_sk(sk); @@ -2984,7 +2988,7 @@ int tcp_sock_set_keepidle(struct sock *sk, int val)  	int err;  	lock_sock(sk); -	err = __tcp_sock_set_keepidle(sk, val); +	err = tcp_sock_set_keepidle_locked(sk, val);  	release_sock(sk);  	return err;  } @@ -3017,8 +3021,8 @@ EXPORT_SYMBOL(tcp_sock_set_keepcnt);  /*   *	Socket option code for TCP.   */ -static int do_tcp_setsockopt(struct sock *sk, int level, -		int optname, char __user *optval, unsigned int optlen) +static int do_tcp_setsockopt(struct sock *sk, int level, int optname, +		sockptr_t optval, unsigned int optlen)  {  	struct tcp_sock *tp = tcp_sk(sk);  	struct inet_connection_sock *icsk = inet_csk(sk); @@ -3034,7 +3038,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level,  		if (optlen < 1)  			return -EINVAL; -		val = strncpy_from_user(name, optval, +		val = strncpy_from_sockptr(name, optval,  					min_t(long, TCP_CA_NAME_MAX-1, optlen));  		if (val < 0)  			return -EFAULT; @@ -3053,7 +3057,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level,  		if (optlen < 1)  			return -EINVAL; -		val = strncpy_from_user(name, optval, +		val = strncpy_from_sockptr(name, optval,  					min_t(long, TCP_ULP_NAME_MAX - 1,  					      optlen));  		if (val < 0) @@ -3076,7 +3080,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level,  		    optlen != TCP_FASTOPEN_KEY_BUF_LENGTH)  			return -EINVAL; -		if (copy_from_user(key, optval, optlen)) +		if (copy_from_sockptr(key, optval, optlen))  			return -EFAULT;  		if (optlen == TCP_FASTOPEN_KEY_BUF_LENGTH) @@ -3092,7 +3096,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level,  	if (optlen < sizeof(int))  		return -EINVAL; -	if (get_user(val, (int __user *)optval)) +	if (copy_from_sockptr(&val, optval, sizeof(val)))  		return -EFAULT;  	lock_sock(sk); @@ -3171,9 +3175,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level,  		if (!tp->repair)  			err = -EINVAL;  		else if (sk->sk_state == TCP_ESTABLISHED) -			err = tcp_repair_options_est(sk, -					(struct tcp_repair_opt __user *)optval, -					optlen); +			err = tcp_repair_options_est(sk, optval, optlen);  		else  			err = -EPERM;  		break; @@ -3183,7 +3185,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level,  		break;  	case TCP_KEEPIDLE: -		err = __tcp_sock_set_keepidle(sk, val); +		err = tcp_sock_set_keepidle_locked(sk, val);  		break;  	case TCP_KEEPINTVL:  		if (val < 1 || val > MAX_TCP_KEEPINTVL) @@ -3246,10 +3248,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level,  #ifdef CONFIG_TCP_MD5SIG  	case TCP_MD5SIG:  	case TCP_MD5SIG_EXT: -		if ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN)) -			err = tp->af_specific->md5_parse(sk, optname, optval, optlen); -		else -			err = -EINVAL; +		err = tp->af_specific->md5_parse(sk, optname, optval, optlen);  		break;  #endif  	case TCP_USER_TIMEOUT: @@ -3325,7 +3324,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level,  	return err;  } -int tcp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, +int tcp_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,  		   unsigned int optlen)  {  	const struct inet_connection_sock *icsk = inet_csk(sk); @@ -3337,18 +3336,6 @@ int tcp_setsockopt(struct sock *sk, int level, int optname, char __user *optval,  }  EXPORT_SYMBOL(tcp_setsockopt); -#ifdef CONFIG_COMPAT -int compat_tcp_setsockopt(struct sock *sk, int level, int optname, -			  char __user *optval, unsigned int optlen) -{ -	if (level != SOL_TCP) -		return inet_csk_compat_setsockopt(sk, level, optname, -						  optval, optlen); -	return do_tcp_setsockopt(sk, level, optname, optval, optlen); -} -EXPORT_SYMBOL(compat_tcp_setsockopt); -#endif -  static void tcp_get_info_chrono_stats(const struct tcp_sock *tp,  				      struct tcp_info *info)  { @@ -3514,10 +3501,12 @@ static size_t tcp_opt_stats_get_size(void)  		nla_total_size(sizeof(u32)) + /* TCP_NLA_SRTT */  		nla_total_size(sizeof(u16)) + /* TCP_NLA_TIMEOUT_REHASH */  		nla_total_size(sizeof(u32)) + /* TCP_NLA_BYTES_NOTSENT */ +		nla_total_size_64bit(sizeof(u64)) + /* TCP_NLA_EDT */  		0;  } -struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk) +struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk, +					       const struct sk_buff *orig_skb)  {  	const struct tcp_sock *tp = tcp_sk(sk);  	struct sk_buff *stats; @@ -3571,6 +3560,8 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk)  	nla_put_u16(stats, TCP_NLA_TIMEOUT_REHASH, tp->timeout_rehash);  	nla_put_u32(stats, TCP_NLA_BYTES_NOTSENT,  		    max_t(int, 0, tp->write_seq - tp->snd_nxt)); +	nla_put_u64_64bit(stats, TCP_NLA_EDT, orig_skb->skb_mstamp_ns, +			  TCP_NLA_PAD);  	return stats;  } @@ -3694,22 +3685,14 @@ static int do_tcp_getsockopt(struct sock *sk, int level,  		return 0;  	case TCP_FASTOPEN_KEY: { -		__u8 key[TCP_FASTOPEN_KEY_BUF_LENGTH]; -		struct tcp_fastopen_context *ctx; -		unsigned int key_len = 0; +		u64 key[TCP_FASTOPEN_KEY_BUF_LENGTH / sizeof(u64)]; +		unsigned int key_len;  		if (get_user(len, optlen))  			return -EFAULT; -		rcu_read_lock(); -		ctx = rcu_dereference(icsk->icsk_accept_queue.fastopenq.ctx); -		if (ctx) { -			key_len = tcp_fastopen_context_len(ctx) * -					TCP_FASTOPEN_KEY_LENGTH; -			memcpy(&key[0], &ctx->key[0], key_len); -		} -		rcu_read_unlock(); - +		key_len = tcp_fastopen_get_cipher(net, icsk, key) * +				TCP_FASTOPEN_KEY_LENGTH;  		len = min_t(unsigned int, len, key_len);  		if (put_user(len, optlen))  			return -EFAULT; @@ -3896,18 +3879,6 @@ int tcp_getsockopt(struct sock *sk, int level, int optname, char __user *optval,  }  EXPORT_SYMBOL(tcp_getsockopt); -#ifdef CONFIG_COMPAT -int compat_tcp_getsockopt(struct sock *sk, int level, int optname, -			  char __user *optval, int __user *optlen) -{ -	if (level != SOL_TCP) -		return inet_csk_compat_getsockopt(sk, level, optname, -						  optval, optlen); -	return do_tcp_getsockopt(sk, level, optname, optval, optlen); -} -EXPORT_SYMBOL(compat_tcp_getsockopt); -#endif -  #ifdef CONFIG_TCP_MD5SIG  static DEFINE_PER_CPU(struct tcp_md5sig_pool, tcp_md5sig_pool);  static DEFINE_MUTEX(tcp_md5sig_mutex); @@ -4033,11 +4004,14 @@ EXPORT_SYMBOL(tcp_md5_hash_skb_data);  int tcp_md5_hash_key(struct tcp_md5sig_pool *hp, const struct tcp_md5sig_key *key)  { +	u8 keylen = READ_ONCE(key->keylen); /* paired with WRITE_ONCE() in tcp_md5_do_add */  	struct scatterlist sg; -	sg_init_one(&sg, key->key, key->keylen); -	ahash_request_set_crypt(hp->md5_req, &sg, NULL, key->keylen); -	return crypto_ahash_update(hp->md5_req); +	sg_init_one(&sg, key->key, keylen); +	ahash_request_set_crypt(hp->md5_req, &sg, NULL, keylen); + +	/* We use data_race() because tcp_md5_do_add() might change key->key under us */ +	return data_race(crypto_ahash_update(hp->md5_req));  }  EXPORT_SYMBOL(tcp_md5_hash_key);  | 
