forked from mirrors/linux
		
	udp: must lock the socket in udp_disconnect()
Baozeng Ding reported KASAN traces showing uses after free in
udp_lib_get_port() and other related UDP functions.
A CONFIG_DEBUG_PAGEALLOC=y kernel would eventually crash.
I could write a reproducer with two threads doing :
static int sock_fd;
static void *thr1(void *arg)
{
	for (;;) {
		connect(sock_fd, (const struct sockaddr *)arg,
			sizeof(struct sockaddr_in));
	}
}
static void *thr2(void *arg)
{
	struct sockaddr_in unspec;
	for (;;) {
		memset(&unspec, 0, sizeof(unspec));
	        connect(sock_fd, (const struct sockaddr *)&unspec,
			sizeof(unspec));
        }
}
Problem is that udp_disconnect() could run without holding socket lock,
and this was causing list corruptions.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Baozeng Ding <sploving1@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
							parent
							
								
									2399d6143f
								
							
						
					
					
						commit
						286c72deab
					
				
					 8 changed files with 18 additions and 8 deletions
				
			
		| 
						 | 
					@ -258,6 +258,7 @@ void udp_flush_pending_frames(struct sock *sk);
 | 
				
			||||||
void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst);
 | 
					void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst);
 | 
				
			||||||
int udp_rcv(struct sk_buff *skb);
 | 
					int udp_rcv(struct sk_buff *skb);
 | 
				
			||||||
int udp_ioctl(struct sock *sk, int cmd, unsigned long arg);
 | 
					int udp_ioctl(struct sock *sk, int cmd, unsigned long arg);
 | 
				
			||||||
 | 
					int __udp_disconnect(struct sock *sk, int flags);
 | 
				
			||||||
int udp_disconnect(struct sock *sk, int flags);
 | 
					int udp_disconnect(struct sock *sk, int flags);
 | 
				
			||||||
unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait);
 | 
					unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait);
 | 
				
			||||||
struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
 | 
					struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -994,7 +994,7 @@ struct proto ping_prot = {
 | 
				
			||||||
	.init =		ping_init_sock,
 | 
						.init =		ping_init_sock,
 | 
				
			||||||
	.close =	ping_close,
 | 
						.close =	ping_close,
 | 
				
			||||||
	.connect =	ip4_datagram_connect,
 | 
						.connect =	ip4_datagram_connect,
 | 
				
			||||||
	.disconnect =	udp_disconnect,
 | 
						.disconnect =	__udp_disconnect,
 | 
				
			||||||
	.setsockopt =	ip_setsockopt,
 | 
						.setsockopt =	ip_setsockopt,
 | 
				
			||||||
	.getsockopt =	ip_getsockopt,
 | 
						.getsockopt =	ip_getsockopt,
 | 
				
			||||||
	.sendmsg =	ping_v4_sendmsg,
 | 
						.sendmsg =	ping_v4_sendmsg,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -918,7 +918,7 @@ struct proto raw_prot = {
 | 
				
			||||||
	.close		   = raw_close,
 | 
						.close		   = raw_close,
 | 
				
			||||||
	.destroy	   = raw_destroy,
 | 
						.destroy	   = raw_destroy,
 | 
				
			||||||
	.connect	   = ip4_datagram_connect,
 | 
						.connect	   = ip4_datagram_connect,
 | 
				
			||||||
	.disconnect	   = udp_disconnect,
 | 
						.disconnect	   = __udp_disconnect,
 | 
				
			||||||
	.ioctl		   = raw_ioctl,
 | 
						.ioctl		   = raw_ioctl,
 | 
				
			||||||
	.init		   = raw_init,
 | 
						.init		   = raw_init,
 | 
				
			||||||
	.setsockopt	   = raw_setsockopt,
 | 
						.setsockopt	   = raw_setsockopt,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1345,7 +1345,7 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock,
 | 
				
			||||||
	goto try_again;
 | 
						goto try_again;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int udp_disconnect(struct sock *sk, int flags)
 | 
					int __udp_disconnect(struct sock *sk, int flags)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct inet_sock *inet = inet_sk(sk);
 | 
						struct inet_sock *inet = inet_sk(sk);
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
| 
						 | 
					@ -1367,6 +1367,15 @@ int udp_disconnect(struct sock *sk, int flags)
 | 
				
			||||||
	sk_dst_reset(sk);
 | 
						sk_dst_reset(sk);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL(__udp_disconnect);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int udp_disconnect(struct sock *sk, int flags)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						lock_sock(sk);
 | 
				
			||||||
 | 
						__udp_disconnect(sk, flags);
 | 
				
			||||||
 | 
						release_sock(sk);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(udp_disconnect);
 | 
					EXPORT_SYMBOL(udp_disconnect);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void udp_lib_unhash(struct sock *sk)
 | 
					void udp_lib_unhash(struct sock *sk)
 | 
				
			||||||
| 
						 | 
					@ -2193,7 +2202,7 @@ int udp_abort(struct sock *sk, int err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sk->sk_err = err;
 | 
						sk->sk_err = err;
 | 
				
			||||||
	sk->sk_error_report(sk);
 | 
						sk->sk_error_report(sk);
 | 
				
			||||||
	udp_disconnect(sk, 0);
 | 
						__udp_disconnect(sk, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	release_sock(sk);
 | 
						release_sock(sk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -180,7 +180,7 @@ struct proto pingv6_prot = {
 | 
				
			||||||
	.init =		ping_init_sock,
 | 
						.init =		ping_init_sock,
 | 
				
			||||||
	.close =	ping_close,
 | 
						.close =	ping_close,
 | 
				
			||||||
	.connect =	ip6_datagram_connect_v6_only,
 | 
						.connect =	ip6_datagram_connect_v6_only,
 | 
				
			||||||
	.disconnect =	udp_disconnect,
 | 
						.disconnect =	__udp_disconnect,
 | 
				
			||||||
	.setsockopt =	ipv6_setsockopt,
 | 
						.setsockopt =	ipv6_setsockopt,
 | 
				
			||||||
	.getsockopt =	ipv6_getsockopt,
 | 
						.getsockopt =	ipv6_getsockopt,
 | 
				
			||||||
	.sendmsg =	ping_v6_sendmsg,
 | 
						.sendmsg =	ping_v6_sendmsg,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1241,7 +1241,7 @@ struct proto rawv6_prot = {
 | 
				
			||||||
	.close		   = rawv6_close,
 | 
						.close		   = rawv6_close,
 | 
				
			||||||
	.destroy	   = raw6_destroy,
 | 
						.destroy	   = raw6_destroy,
 | 
				
			||||||
	.connect	   = ip6_datagram_connect_v6_only,
 | 
						.connect	   = ip6_datagram_connect_v6_only,
 | 
				
			||||||
	.disconnect	   = udp_disconnect,
 | 
						.disconnect	   = __udp_disconnect,
 | 
				
			||||||
	.ioctl		   = rawv6_ioctl,
 | 
						.ioctl		   = rawv6_ioctl,
 | 
				
			||||||
	.init		   = rawv6_init_sk,
 | 
						.init		   = rawv6_init_sk,
 | 
				
			||||||
	.setsockopt	   = rawv6_setsockopt,
 | 
						.setsockopt	   = rawv6_setsockopt,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -338,7 +338,7 @@ static int l2tp_ip_disconnect(struct sock *sk, int flags)
 | 
				
			||||||
	if (sock_flag(sk, SOCK_ZAPPED))
 | 
						if (sock_flag(sk, SOCK_ZAPPED))
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return udp_disconnect(sk, flags);
 | 
						return __udp_disconnect(sk, flags);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int l2tp_ip_getname(struct socket *sock, struct sockaddr *uaddr,
 | 
					static int l2tp_ip_getname(struct socket *sock, struct sockaddr *uaddr,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -410,7 +410,7 @@ static int l2tp_ip6_disconnect(struct sock *sk, int flags)
 | 
				
			||||||
	if (sock_flag(sk, SOCK_ZAPPED))
 | 
						if (sock_flag(sk, SOCK_ZAPPED))
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return udp_disconnect(sk, flags);
 | 
						return __udp_disconnect(sk, flags);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int l2tp_ip6_getname(struct socket *sock, struct sockaddr *uaddr,
 | 
					static int l2tp_ip6_getname(struct socket *sock, struct sockaddr *uaddr,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue