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
@ -258,6 +258,7 @@ void udp_flush_pending_frames(struct sock *sk);
|
||||
void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst);
|
||||
int udp_rcv(struct sk_buff *skb);
|
||||
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);
|
||||
unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait);
|
||||
struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
|
||||
|
@ -994,7 +994,7 @@ struct proto ping_prot = {
|
||||
.init = ping_init_sock,
|
||||
.close = ping_close,
|
||||
.connect = ip4_datagram_connect,
|
||||
.disconnect = udp_disconnect,
|
||||
.disconnect = __udp_disconnect,
|
||||
.setsockopt = ip_setsockopt,
|
||||
.getsockopt = ip_getsockopt,
|
||||
.sendmsg = ping_v4_sendmsg,
|
||||
|
@ -918,7 +918,7 @@ struct proto raw_prot = {
|
||||
.close = raw_close,
|
||||
.destroy = raw_destroy,
|
||||
.connect = ip4_datagram_connect,
|
||||
.disconnect = udp_disconnect,
|
||||
.disconnect = __udp_disconnect,
|
||||
.ioctl = raw_ioctl,
|
||||
.init = raw_init,
|
||||
.setsockopt = raw_setsockopt,
|
||||
|
@ -1345,7 +1345,7 @@ csum_copy_err:
|
||||
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);
|
||||
/*
|
||||
@ -1367,6 +1367,15 @@ int udp_disconnect(struct sock *sk, int flags)
|
||||
sk_dst_reset(sk);
|
||||
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);
|
||||
|
||||
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_error_report(sk);
|
||||
udp_disconnect(sk, 0);
|
||||
__udp_disconnect(sk, 0);
|
||||
|
||||
release_sock(sk);
|
||||
|
||||
|
@ -180,7 +180,7 @@ struct proto pingv6_prot = {
|
||||
.init = ping_init_sock,
|
||||
.close = ping_close,
|
||||
.connect = ip6_datagram_connect_v6_only,
|
||||
.disconnect = udp_disconnect,
|
||||
.disconnect = __udp_disconnect,
|
||||
.setsockopt = ipv6_setsockopt,
|
||||
.getsockopt = ipv6_getsockopt,
|
||||
.sendmsg = ping_v6_sendmsg,
|
||||
|
@ -1241,7 +1241,7 @@ struct proto rawv6_prot = {
|
||||
.close = rawv6_close,
|
||||
.destroy = raw6_destroy,
|
||||
.connect = ip6_datagram_connect_v6_only,
|
||||
.disconnect = udp_disconnect,
|
||||
.disconnect = __udp_disconnect,
|
||||
.ioctl = rawv6_ioctl,
|
||||
.init = rawv6_init_sk,
|
||||
.setsockopt = rawv6_setsockopt,
|
||||
|
@ -338,7 +338,7 @@ static int l2tp_ip_disconnect(struct sock *sk, int flags)
|
||||
if (sock_flag(sk, SOCK_ZAPPED))
|
||||
return 0;
|
||||
|
||||
return udp_disconnect(sk, flags);
|
||||
return __udp_disconnect(sk, flags);
|
||||
}
|
||||
|
||||
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))
|
||||
return 0;
|
||||
|
||||
return udp_disconnect(sk, flags);
|
||||
return __udp_disconnect(sk, flags);
|
||||
}
|
||||
|
||||
static int l2tp_ip6_getname(struct socket *sock, struct sockaddr *uaddr,
|
||||
|
Loading…
Reference in New Issue
Block a user