tcp/udp: Call inet6_destroy_sock() in IPv6 sk->sk_destruct().
commitd38afeec26
upstream. Originally, inet6_sk(sk)->XXX were changed under lock_sock(), so we were able to clean them up by calling inet6_destroy_sock() during the IPv6 -> IPv4 conversion by IPV6_ADDRFORM. However, commit03485f2adc
("udpv6: Add lockless sendmsg() support") added a lockless memory allocation path, which could cause a memory leak: setsockopt(IPV6_ADDRFORM) sendmsg() +-----------------------+ +-------+ - do_ipv6_setsockopt(sk, ...) - udpv6_sendmsg(sk, ...) - sockopt_lock_sock(sk) ^._ called via udpv6_prot - lock_sock(sk) before WRITE_ONCE() - WRITE_ONCE(sk->sk_prot, &tcp_prot) - inet6_destroy_sock() - if (!corkreq) - sockopt_release_sock(sk) - ip6_make_skb(sk, ...) - release_sock(sk) ^._ lockless fast path for the non-corking case - __ip6_append_data(sk, ...) - ipv6_local_rxpmtu(sk, ...) - xchg(&np->rxpmtu, skb) ^._ rxpmtu is never freed. - goto out_no_dst; - lock_sock(sk) For now, rxpmtu is only the case, but not to miss the future change and a similar bug fixed in commite27326009a
("net: ping6: Fix memleak in ipv6_renew_options()."), let's set a new function to IPv6 sk->sk_destruct() and call inet6_cleanup_sock() there. Since the conversion does not change sk->sk_destruct(), we can guarantee that we can clean up IPv6 resources finally. We can now remove all inet6_destroy_sock() calls from IPv6 protocol specific ->destroy() functions, but such changes are invasive to backport. So they can be posted as a follow-up later for net-next. Fixes:03485f2adc
("udpv6: Add lockless sendmsg() support") Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com> Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Ziyang Xuan <william.xuanziyang@huawei.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Родитель
dabbe97f36
Коммит
dd04213138
|
@ -1119,6 +1119,7 @@ void ipv6_local_error(struct sock *sk, int err, struct flowi6 *fl6, u32 info);
|
|||
void ipv6_local_rxpmtu(struct sock *sk, struct flowi6 *fl6, u32 mtu);
|
||||
|
||||
void inet6_cleanup_sock(struct sock *sk);
|
||||
void inet6_sock_destruct(struct sock *sk);
|
||||
int inet6_release(struct socket *sock);
|
||||
int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len);
|
||||
int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
|
||||
|
|
|
@ -270,7 +270,7 @@ static inline bool udp_sk_bound_dev_eq(struct net *net, int bound_dev_if,
|
|||
}
|
||||
|
||||
/* net/ipv4/udp.c */
|
||||
void udp_destruct_sock(struct sock *sk);
|
||||
void udp_destruct_common(struct sock *sk);
|
||||
void skb_consume_udp(struct sock *sk, struct sk_buff *skb, int len);
|
||||
int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb);
|
||||
void udp_skb_destructor(struct sock *sk, struct sk_buff *skb);
|
||||
|
|
|
@ -24,14 +24,6 @@ static __inline__ int udplite_getfrag(void *from, char *to, int offset,
|
|||
return copy_from_iter_full(to, len, &msg->msg_iter) ? 0 : -EFAULT;
|
||||
}
|
||||
|
||||
/* Designate sk as UDP-Lite socket */
|
||||
static inline int udplite_sk_init(struct sock *sk)
|
||||
{
|
||||
udp_init_sock(sk);
|
||||
udp_sk(sk)->pcflag = UDPLITE_BIT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checksumming routines
|
||||
*/
|
||||
|
|
|
@ -1596,7 +1596,7 @@ drop:
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(__udp_enqueue_schedule_skb);
|
||||
|
||||
void udp_destruct_sock(struct sock *sk)
|
||||
void udp_destruct_common(struct sock *sk)
|
||||
{
|
||||
/* reclaim completely the forward allocated memory */
|
||||
struct udp_sock *up = udp_sk(sk);
|
||||
|
@ -1609,10 +1609,14 @@ void udp_destruct_sock(struct sock *sk)
|
|||
kfree_skb(skb);
|
||||
}
|
||||
udp_rmem_release(sk, total, 0, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(udp_destruct_common);
|
||||
|
||||
static void udp_destruct_sock(struct sock *sk)
|
||||
{
|
||||
udp_destruct_common(sk);
|
||||
inet_sock_destruct(sk);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(udp_destruct_sock);
|
||||
|
||||
int udp_init_sock(struct sock *sk)
|
||||
{
|
||||
|
@ -1620,7 +1624,6 @@ int udp_init_sock(struct sock *sk)
|
|||
sk->sk_destruct = udp_destruct_sock;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(udp_init_sock);
|
||||
|
||||
void skb_consume_udp(struct sock *sk, struct sk_buff *skb, int len)
|
||||
{
|
||||
|
|
|
@ -17,6 +17,14 @@
|
|||
struct udp_table udplite_table __read_mostly;
|
||||
EXPORT_SYMBOL(udplite_table);
|
||||
|
||||
/* Designate sk as UDP-Lite socket */
|
||||
static int udplite_sk_init(struct sock *sk)
|
||||
{
|
||||
udp_init_sock(sk);
|
||||
udp_sk(sk)->pcflag = UDPLITE_BIT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udplite_rcv(struct sk_buff *skb)
|
||||
{
|
||||
return __udp4_lib_rcv(skb, &udplite_table, IPPROTO_UDPLITE);
|
||||
|
|
|
@ -108,6 +108,12 @@ static __inline__ struct ipv6_pinfo *inet6_sk_generic(struct sock *sk)
|
|||
return (struct ipv6_pinfo *)(((u8 *)sk) + offset);
|
||||
}
|
||||
|
||||
void inet6_sock_destruct(struct sock *sk)
|
||||
{
|
||||
inet6_cleanup_sock(sk);
|
||||
inet_sock_destruct(sk);
|
||||
}
|
||||
|
||||
static int inet6_create(struct net *net, struct socket *sock, int protocol,
|
||||
int kern)
|
||||
{
|
||||
|
@ -200,7 +206,7 @@ lookup_protocol:
|
|||
inet->hdrincl = 1;
|
||||
}
|
||||
|
||||
sk->sk_destruct = inet_sock_destruct;
|
||||
sk->sk_destruct = inet6_sock_destruct;
|
||||
sk->sk_family = PF_INET6;
|
||||
sk->sk_protocol = protocol;
|
||||
|
||||
|
|
|
@ -55,6 +55,19 @@
|
|||
#include <trace/events/skb.h>
|
||||
#include "udp_impl.h"
|
||||
|
||||
static void udpv6_destruct_sock(struct sock *sk)
|
||||
{
|
||||
udp_destruct_common(sk);
|
||||
inet6_sock_destruct(sk);
|
||||
}
|
||||
|
||||
int udpv6_init_sock(struct sock *sk)
|
||||
{
|
||||
skb_queue_head_init(&udp_sk(sk)->reader_queue);
|
||||
sk->sk_destruct = udpv6_destruct_sock;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 udp6_ehashfn(const struct net *net,
|
||||
const struct in6_addr *laddr,
|
||||
const u16 lport,
|
||||
|
@ -1721,7 +1734,7 @@ struct proto udpv6_prot = {
|
|||
.connect = ip6_datagram_connect,
|
||||
.disconnect = udp_disconnect,
|
||||
.ioctl = udp_ioctl,
|
||||
.init = udp_init_sock,
|
||||
.init = udpv6_init_sock,
|
||||
.destroy = udpv6_destroy_sock,
|
||||
.setsockopt = udpv6_setsockopt,
|
||||
.getsockopt = udpv6_getsockopt,
|
||||
|
|
|
@ -12,6 +12,7 @@ int __udp6_lib_rcv(struct sk_buff *, struct udp_table *, int);
|
|||
int __udp6_lib_err(struct sk_buff *, struct inet6_skb_parm *, u8, u8, int,
|
||||
__be32, struct udp_table *);
|
||||
|
||||
int udpv6_init_sock(struct sock *sk);
|
||||
int udp_v6_get_port(struct sock *sk, unsigned short snum);
|
||||
void udp_v6_rehash(struct sock *sk);
|
||||
|
||||
|
|
|
@ -12,6 +12,13 @@
|
|||
#include <linux/proc_fs.h>
|
||||
#include "udp_impl.h"
|
||||
|
||||
static int udplitev6_sk_init(struct sock *sk)
|
||||
{
|
||||
udpv6_init_sock(sk);
|
||||
udp_sk(sk)->pcflag = UDPLITE_BIT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udplitev6_rcv(struct sk_buff *skb)
|
||||
{
|
||||
return __udp6_lib_rcv(skb, &udplite_table, IPPROTO_UDPLITE);
|
||||
|
@ -38,7 +45,7 @@ struct proto udplitev6_prot = {
|
|||
.connect = ip6_datagram_connect,
|
||||
.disconnect = udp_disconnect,
|
||||
.ioctl = udp_ioctl,
|
||||
.init = udplite_sk_init,
|
||||
.init = udplitev6_sk_init,
|
||||
.destroy = udpv6_destroy_sock,
|
||||
.setsockopt = udpv6_setsockopt,
|
||||
.getsockopt = udpv6_getsockopt,
|
||||
|
|
Загрузка…
Ссылка в новой задаче