Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os 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>

authored by

Eric Dumazet and committed by
David S. Miller
286c72de 2399d614

+18 -8
+1
include/net/udp.h
··· 258 258 void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst); 259 259 int udp_rcv(struct sk_buff *skb); 260 260 int udp_ioctl(struct sock *sk, int cmd, unsigned long arg); 261 + int __udp_disconnect(struct sock *sk, int flags); 261 262 int udp_disconnect(struct sock *sk, int flags); 262 263 unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait); 263 264 struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
+1 -1
net/ipv4/ping.c
··· 994 994 .init = ping_init_sock, 995 995 .close = ping_close, 996 996 .connect = ip4_datagram_connect, 997 - .disconnect = udp_disconnect, 997 + .disconnect = __udp_disconnect, 998 998 .setsockopt = ip_setsockopt, 999 999 .getsockopt = ip_getsockopt, 1000 1000 .sendmsg = ping_v4_sendmsg,
+1 -1
net/ipv4/raw.c
··· 918 918 .close = raw_close, 919 919 .destroy = raw_destroy, 920 920 .connect = ip4_datagram_connect, 921 - .disconnect = udp_disconnect, 921 + .disconnect = __udp_disconnect, 922 922 .ioctl = raw_ioctl, 923 923 .init = raw_init, 924 924 .setsockopt = raw_setsockopt,
+11 -2
net/ipv4/udp.c
··· 1345 1345 goto try_again; 1346 1346 } 1347 1347 1348 - int udp_disconnect(struct sock *sk, int flags) 1348 + int __udp_disconnect(struct sock *sk, int flags) 1349 1349 { 1350 1350 struct inet_sock *inet = inet_sk(sk); 1351 1351 /* ··· 1365 1365 inet->inet_sport = 0; 1366 1366 } 1367 1367 sk_dst_reset(sk); 1368 + return 0; 1369 + } 1370 + EXPORT_SYMBOL(__udp_disconnect); 1371 + 1372 + int udp_disconnect(struct sock *sk, int flags) 1373 + { 1374 + lock_sock(sk); 1375 + __udp_disconnect(sk, flags); 1376 + release_sock(sk); 1368 1377 return 0; 1369 1378 } 1370 1379 EXPORT_SYMBOL(udp_disconnect); ··· 2202 2193 2203 2194 sk->sk_err = err; 2204 2195 sk->sk_error_report(sk); 2205 - udp_disconnect(sk, 0); 2196 + __udp_disconnect(sk, 0); 2206 2197 2207 2198 release_sock(sk); 2208 2199
+1 -1
net/ipv6/ping.c
··· 180 180 .init = ping_init_sock, 181 181 .close = ping_close, 182 182 .connect = ip6_datagram_connect_v6_only, 183 - .disconnect = udp_disconnect, 183 + .disconnect = __udp_disconnect, 184 184 .setsockopt = ipv6_setsockopt, 185 185 .getsockopt = ipv6_getsockopt, 186 186 .sendmsg = ping_v6_sendmsg,
+1 -1
net/ipv6/raw.c
··· 1241 1241 .close = rawv6_close, 1242 1242 .destroy = raw6_destroy, 1243 1243 .connect = ip6_datagram_connect_v6_only, 1244 - .disconnect = udp_disconnect, 1244 + .disconnect = __udp_disconnect, 1245 1245 .ioctl = rawv6_ioctl, 1246 1246 .init = rawv6_init_sk, 1247 1247 .setsockopt = rawv6_setsockopt,
+1 -1
net/l2tp/l2tp_ip.c
··· 338 338 if (sock_flag(sk, SOCK_ZAPPED)) 339 339 return 0; 340 340 341 - return udp_disconnect(sk, flags); 341 + return __udp_disconnect(sk, flags); 342 342 } 343 343 344 344 static int l2tp_ip_getname(struct socket *sock, struct sockaddr *uaddr,
+1 -1
net/l2tp/l2tp_ip6.c
··· 410 410 if (sock_flag(sk, SOCK_ZAPPED)) 411 411 return 0; 412 412 413 - return udp_disconnect(sk, flags); 413 + return __udp_disconnect(sk, flags); 414 414 } 415 415 416 416 static int l2tp_ip6_getname(struct socket *sock, struct sockaddr *uaddr,