Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 286c72de authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller
Browse files

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: default avatarEric Dumazet <edumazet@google.com>
Reported-by: default avatarBaozeng Ding <sploving1@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 2399d614
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -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,
+1 −1
Original line number Original line Diff line number Diff line
@@ -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,
+1 −1
Original line number Original line Diff line number Diff line
@@ -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,
+11 −2
Original line number Original line Diff line number Diff line
@@ -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);


+1 −1
Original line number Original line Diff line number Diff line
@@ -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,
Loading