Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

udp: Call inet6_destroy_sock() in setsockopt(IPV6_ADDRFORM).

Commit 4b340ae20d0e ("IPv6: Complete IPV6_DONTFRAG support") forgot
to add a change to free inet6_sk(sk)->rxpmtu while converting an IPv6
socket into IPv4 with IPV6_ADDRFORM. After conversion, sk_prot is
changed to udp_prot and ->destroy() never cleans it up, resulting in
a memory leak.

This is due to the discrepancy between inet6_destroy_sock() and
IPV6_ADDRFORM, so let's call inet6_destroy_sock() from IPV6_ADDRFORM
to remove the difference.

However, this is not enough for now because rxpmtu can be changed
without lock_sock() after commit 03485f2adcde ("udpv6: Add lockless
sendmsg() support"). We will fix this case in the following patch.

Note we will rename inet6_destroy_sock() to inet6_cleanup_sock() and
remove unnecessary inet6_destroy_sock() calls in sk_prot->destroy()
in the future.

Fixes: 4b340ae20d0e ("IPv6: Complete IPV6_DONTFRAG support")
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Kuniyuki Iwashima and committed by
Jakub Kicinski
21985f43 3c52c6bb

+15 -12
+1
include/net/ipv6.h
··· 1182 1182 void ipv6_local_error(struct sock *sk, int err, struct flowi6 *fl6, u32 info); 1183 1183 void ipv6_local_rxpmtu(struct sock *sk, struct flowi6 *fl6, u32 mtu); 1184 1184 1185 + void inet6_cleanup_sock(struct sock *sk); 1185 1186 int inet6_release(struct socket *sock); 1186 1187 int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len); 1187 1188 int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
+6
net/ipv6/af_inet6.c
··· 510 510 } 511 511 EXPORT_SYMBOL_GPL(inet6_destroy_sock); 512 512 513 + void inet6_cleanup_sock(struct sock *sk) 514 + { 515 + inet6_destroy_sock(sk); 516 + } 517 + EXPORT_SYMBOL_GPL(inet6_cleanup_sock); 518 + 513 519 /* 514 520 * This does both peername and sockname. 515 521 */
+8 -12
net/ipv6/ipv6_sockglue.c
··· 431 431 if (optlen < sizeof(int)) 432 432 goto e_inval; 433 433 if (val == PF_INET) { 434 - struct ipv6_txoptions *opt; 435 - struct sk_buff *pktopt; 436 - 437 434 if (sk->sk_type == SOCK_RAW) 438 435 break; 439 436 ··· 461 464 break; 462 465 } 463 466 464 - fl6_free_socklist(sk); 465 467 __ipv6_sock_mc_close(sk); 466 468 __ipv6_sock_ac_close(sk); 467 469 ··· 497 501 sk->sk_socket->ops = &inet_dgram_ops; 498 502 sk->sk_family = PF_INET; 499 503 } 500 - opt = xchg((__force struct ipv6_txoptions **)&np->opt, 501 - NULL); 502 - if (opt) { 503 - atomic_sub(opt->tot_len, &sk->sk_omem_alloc); 504 - txopt_put(opt); 505 - } 506 - pktopt = xchg(&np->pktoptions, NULL); 507 - kfree_skb(pktopt); 504 + 505 + /* Disable all options not to allocate memory anymore, 506 + * but there is still a race. See the lockless path 507 + * in udpv6_sendmsg() and ipv6_local_rxpmtu(). 508 + */ 509 + np->rxopt.all = 0; 510 + 511 + inet6_cleanup_sock(sk); 508 512 509 513 /* 510 514 * ... and add it to the refcnt debug socks count