mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	inet: move inet->recverr to inet->inet_flags
IP_RECVERR socket option can now be set/get without locking the socket. This patch potentially avoid data-races around inet->recverr. Signed-off-by: Eric Dumazet <edumazet@google.com> Acked-by: Soheil Hassas Yeganeh <soheil@google.com> Reviewed-by: Simon Horman <horms@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									b4d84bce4c
								
							
						
					
					
						commit
						6b5f43ea08
					
				
					 9 changed files with 30 additions and 32 deletions
				
			
		| 
						 | 
				
			
			@ -230,8 +230,7 @@ struct inet_sock {
 | 
			
		|||
	__u8			min_ttl;
 | 
			
		||||
	__u8			mc_ttl;
 | 
			
		||||
	__u8			pmtudisc;
 | 
			
		||||
	__u8			recverr:1,
 | 
			
		||||
				is_icsk:1,
 | 
			
		||||
	__u8			is_icsk:1,
 | 
			
		||||
				freebind:1,
 | 
			
		||||
				hdrincl:1,
 | 
			
		||||
				mc_loop:1,
 | 
			
		||||
| 
						 | 
				
			
			@ -270,6 +269,8 @@ enum {
 | 
			
		|||
	INET_FLAGS_ORIGDSTADDR	= 6,
 | 
			
		||||
	INET_FLAGS_CHECKSUM	= 7,
 | 
			
		||||
	INET_FLAGS_RECVFRAGSIZE	= 8,
 | 
			
		||||
 | 
			
		||||
	INET_FLAGS_RECVERR	= 9,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* cmsg flags for inet */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -247,7 +247,6 @@ static int dccp_v4_err(struct sk_buff *skb, u32 info)
 | 
			
		|||
	const u8 offset = iph->ihl << 2;
 | 
			
		||||
	const struct dccp_hdr *dh;
 | 
			
		||||
	struct dccp_sock *dp;
 | 
			
		||||
	struct inet_sock *inet;
 | 
			
		||||
	const int type = icmp_hdr(skb)->type;
 | 
			
		||||
	const int code = icmp_hdr(skb)->code;
 | 
			
		||||
	struct sock *sk;
 | 
			
		||||
| 
						 | 
				
			
			@ -361,8 +360,7 @@ static int dccp_v4_err(struct sk_buff *skb, u32 info)
 | 
			
		|||
	 *							--ANK (980905)
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	inet = inet_sk(sk);
 | 
			
		||||
	if (!sock_owned_by_user(sk) && inet->recverr) {
 | 
			
		||||
	if (!sock_owned_by_user(sk) && inet_test_bit(RECVERR, sk)) {
 | 
			
		||||
		sk->sk_err = err;
 | 
			
		||||
		sk_error_report(sk);
 | 
			
		||||
	} else { /* Only an error on timeout */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -182,7 +182,7 @@ int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb,
 | 
			
		|||
	r->idiag_inode = sock_i_ino(sk);
 | 
			
		||||
 | 
			
		||||
	memset(&inet_sockopt, 0, sizeof(inet_sockopt));
 | 
			
		||||
	inet_sockopt.recverr	= inet->recverr;
 | 
			
		||||
	inet_sockopt.recverr	= inet_test_bit(RECVERR, sk);
 | 
			
		||||
	inet_sockopt.is_icsk	= inet->is_icsk;
 | 
			
		||||
	inet_sockopt.freebind	= inet->freebind;
 | 
			
		||||
	inet_sockopt.hdrincl	= inet->hdrincl;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -446,12 +446,11 @@ EXPORT_SYMBOL_GPL(ip_icmp_error);
 | 
			
		|||
 | 
			
		||||
void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 info)
 | 
			
		||||
{
 | 
			
		||||
	struct inet_sock *inet = inet_sk(sk);
 | 
			
		||||
	struct sock_exterr_skb *serr;
 | 
			
		||||
	struct iphdr *iph;
 | 
			
		||||
	struct sk_buff *skb;
 | 
			
		||||
 | 
			
		||||
	if (!inet->recverr)
 | 
			
		||||
	if (!inet_test_bit(RECVERR, sk))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	skb = alloc_skb(sizeof(struct iphdr), GFP_ATOMIC);
 | 
			
		||||
| 
						 | 
				
			
			@ -617,9 +616,7 @@ EXPORT_SYMBOL(ip_sock_set_freebind);
 | 
			
		|||
 | 
			
		||||
void ip_sock_set_recverr(struct sock *sk)
 | 
			
		||||
{
 | 
			
		||||
	lock_sock(sk);
 | 
			
		||||
	inet_sk(sk)->recverr = true;
 | 
			
		||||
	release_sock(sk);
 | 
			
		||||
	inet_set_bit(RECVERR, sk);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(ip_sock_set_recverr);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -978,6 +975,11 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname,
 | 
			
		|||
			return -EINVAL;
 | 
			
		||||
		inet_assign_bit(RECVFRAGSIZE, sk, val);
 | 
			
		||||
		return 0;
 | 
			
		||||
	case IP_RECVERR:
 | 
			
		||||
		inet_assign_bit(RECVERR, sk, val);
 | 
			
		||||
		if (!val)
 | 
			
		||||
			skb_queue_purge(&sk->sk_error_queue);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -1064,11 +1066,6 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname,
 | 
			
		|||
			goto e_inval;
 | 
			
		||||
		inet->pmtudisc = val;
 | 
			
		||||
		break;
 | 
			
		||||
	case IP_RECVERR:
 | 
			
		||||
		inet->recverr = !!val;
 | 
			
		||||
		if (!val)
 | 
			
		||||
			skb_queue_purge(&sk->sk_error_queue);
 | 
			
		||||
		break;
 | 
			
		||||
	case IP_RECVERR_RFC4884:
 | 
			
		||||
		if (val < 0 || val > 1)
 | 
			
		||||
			goto e_inval;
 | 
			
		||||
| 
						 | 
				
			
			@ -1575,6 +1572,9 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname,
 | 
			
		|||
	case IP_RECVFRAGSIZE:
 | 
			
		||||
		val = inet_test_bit(RECVFRAGSIZE, sk);
 | 
			
		||||
		goto copyval;
 | 
			
		||||
	case IP_RECVERR:
 | 
			
		||||
		val = inet_test_bit(RECVERR, sk);
 | 
			
		||||
		goto copyval;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (needs_rtnl)
 | 
			
		||||
| 
						 | 
				
			
			@ -1649,9 +1649,6 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname,
 | 
			
		|||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	case IP_RECVERR:
 | 
			
		||||
		val = inet->recverr;
 | 
			
		||||
		break;
 | 
			
		||||
	case IP_RECVERR_RFC4884:
 | 
			
		||||
		val = inet->recverr_rfc4884;
 | 
			
		||||
		break;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -580,7 +580,7 @@ void ping_err(struct sk_buff *skb, int offset, u32 info)
 | 
			
		|||
	 *      RFC1122: OK.  Passes ICMP errors back to application, as per
 | 
			
		||||
	 *	4.1.3.3.
 | 
			
		||||
	 */
 | 
			
		||||
	if ((family == AF_INET && !inet_sock->recverr) ||
 | 
			
		||||
	if ((family == AF_INET && !inet_test_bit(RECVERR, sk)) ||
 | 
			
		||||
	    (family == AF_INET6 && !inet6_sk(sk)->recverr)) {
 | 
			
		||||
		if (!harderr || sk->sk_state != TCP_ESTABLISHED)
 | 
			
		||||
			goto out;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -203,8 +203,9 @@ static void raw_err(struct sock *sk, struct sk_buff *skb, u32 info)
 | 
			
		|||
	struct inet_sock *inet = inet_sk(sk);
 | 
			
		||||
	const int type = icmp_hdr(skb)->type;
 | 
			
		||||
	const int code = icmp_hdr(skb)->code;
 | 
			
		||||
	int err = 0;
 | 
			
		||||
	int harderr = 0;
 | 
			
		||||
	bool recverr;
 | 
			
		||||
	int err = 0;
 | 
			
		||||
 | 
			
		||||
	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
 | 
			
		||||
		ipv4_sk_update_pmtu(skb, sk, info);
 | 
			
		||||
| 
						 | 
				
			
			@ -218,7 +219,8 @@ static void raw_err(struct sock *sk, struct sk_buff *skb, u32 info)
 | 
			
		|||
	   2. Socket is connected (otherwise the error indication
 | 
			
		||||
	      is useless without ip_recverr and error is hard.
 | 
			
		||||
	 */
 | 
			
		||||
	if (!inet->recverr && sk->sk_state != TCP_ESTABLISHED)
 | 
			
		||||
	recverr = inet_test_bit(RECVERR, sk);
 | 
			
		||||
	if (!recverr && sk->sk_state != TCP_ESTABLISHED)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	switch (type) {
 | 
			
		||||
| 
						 | 
				
			
			@ -245,7 +247,7 @@ static void raw_err(struct sock *sk, struct sk_buff *skb, u32 info)
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (inet->recverr) {
 | 
			
		||||
	if (recverr) {
 | 
			
		||||
		const struct iphdr *iph = (const struct iphdr *)skb->data;
 | 
			
		||||
		u8 *payload = skb->data + (iph->ihl << 2);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -254,7 +256,7 @@ static void raw_err(struct sock *sk, struct sk_buff *skb, u32 info)
 | 
			
		|||
		ip_icmp_error(sk, skb, err, 0, info, payload);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (inet->recverr || harderr) {
 | 
			
		||||
	if (recverr || harderr) {
 | 
			
		||||
		sk->sk_err = err;
 | 
			
		||||
		sk_error_report(sk);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -413,7 +415,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
 | 
			
		|||
	kfree_skb(skb);
 | 
			
		||||
error:
 | 
			
		||||
	IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS);
 | 
			
		||||
	if (err == -ENOBUFS && !inet->recverr)
 | 
			
		||||
	if (err == -ENOBUFS && !inet_test_bit(RECVERR, sk))
 | 
			
		||||
		err = 0;
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -645,7 +647,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 | 
			
		|||
			ip_flush_pending_frames(sk);
 | 
			
		||||
		else if (!(msg->msg_flags & MSG_MORE)) {
 | 
			
		||||
			err = ip_push_pending_frames(sk, &fl4);
 | 
			
		||||
			if (err == -ENOBUFS && !inet->recverr)
 | 
			
		||||
			if (err == -ENOBUFS && !inet_test_bit(RECVERR, sk))
 | 
			
		||||
				err = 0;
 | 
			
		||||
		}
 | 
			
		||||
		release_sock(sk);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -477,7 +477,6 @@ int tcp_v4_err(struct sk_buff *skb, u32 info)
 | 
			
		|||
	const struct iphdr *iph = (const struct iphdr *)skb->data;
 | 
			
		||||
	struct tcphdr *th = (struct tcphdr *)(skb->data + (iph->ihl << 2));
 | 
			
		||||
	struct tcp_sock *tp;
 | 
			
		||||
	struct inet_sock *inet;
 | 
			
		||||
	const int type = icmp_hdr(skb)->type;
 | 
			
		||||
	const int code = icmp_hdr(skb)->code;
 | 
			
		||||
	struct sock *sk;
 | 
			
		||||
| 
						 | 
				
			
			@ -625,8 +624,8 @@ int tcp_v4_err(struct sk_buff *skb, u32 info)
 | 
			
		|||
	 *							--ANK (980905)
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	inet = inet_sk(sk);
 | 
			
		||||
	if (!sock_owned_by_user(sk) && inet->recverr) {
 | 
			
		||||
	if (!sock_owned_by_user(sk) &&
 | 
			
		||||
	    inet_test_bit(RECVERR, sk)) {
 | 
			
		||||
		WRITE_ONCE(sk->sk_err, err);
 | 
			
		||||
		sk_error_report(sk);
 | 
			
		||||
	} else	{ /* Only an error on timeout */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -779,7 +779,7 @@ int __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
 | 
			
		|||
						  (u8 *)(uh+1));
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
	if (!inet->recverr) {
 | 
			
		||||
	if (!inet_test_bit(RECVERR, sk)) {
 | 
			
		||||
		if (!harderr || sk->sk_state != TCP_ESTABLISHED)
 | 
			
		||||
			goto out;
 | 
			
		||||
	} else
 | 
			
		||||
| 
						 | 
				
			
			@ -962,7 +962,8 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4,
 | 
			
		|||
send:
 | 
			
		||||
	err = ip_send_skb(sock_net(sk), skb);
 | 
			
		||||
	if (err) {
 | 
			
		||||
		if (err == -ENOBUFS && !inet->recverr) {
 | 
			
		||||
		if (err == -ENOBUFS &&
 | 
			
		||||
		    !inet_test_bit(RECVERR, sk)) {
 | 
			
		||||
			UDP_INC_STATS(sock_net(sk),
 | 
			
		||||
				      UDP_MIB_SNDBUFERRORS, is_udplite);
 | 
			
		||||
			err = 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -581,7 +581,7 @@ static void sctp_v4_err_handle(struct sctp_transport *t, struct sk_buff *skb,
 | 
			
		|||
	default:
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	if (!sock_owned_by_user(sk) && inet_sk(sk)->recverr) {
 | 
			
		||||
	if (!sock_owned_by_user(sk) && inet_test_bit(RECVERR, sk)) {
 | 
			
		||||
		sk->sk_err = err;
 | 
			
		||||
		sk_error_report(sk);
 | 
			
		||||
	} else {  /* Only an error on timeout */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue