Commit d38afeec authored by Kuniyuki Iwashima's avatar Kuniyuki Iwashima Committed by Jakub Kicinski

tcp/udp: Call inet6_destroy_sock() in IPv6 sk->sk_destruct().

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, commit 03485f2a ("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 commit e2732600 ("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: 03485f2a ("udpv6: Add lockless sendmsg() support")
Signed-off-by: default avatarKuniyuki Iwashima <kuniyu@amazon.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 21985f43
...@@ -1183,6 +1183,7 @@ void ipv6_local_error(struct sock *sk, int err, struct flowi6 *fl6, u32 info); ...@@ -1183,6 +1183,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 ipv6_local_rxpmtu(struct sock *sk, struct flowi6 *fl6, u32 mtu);
void inet6_cleanup_sock(struct sock *sk); void inet6_cleanup_sock(struct sock *sk);
void inet6_sock_destruct(struct sock *sk);
int inet6_release(struct socket *sock); int inet6_release(struct socket *sock);
int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len); int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len);
int inet6_getname(struct socket *sock, struct sockaddr *uaddr, int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
......
...@@ -247,7 +247,7 @@ static inline bool udp_sk_bound_dev_eq(struct net *net, int bound_dev_if, ...@@ -247,7 +247,7 @@ static inline bool udp_sk_bound_dev_eq(struct net *net, int bound_dev_if,
} }
/* net/ipv4/udp.c */ /* 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); 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); int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb);
void udp_skb_destructor(struct sock *sk, struct sk_buff *skb); void udp_skb_destructor(struct sock *sk, struct sk_buff *skb);
......
...@@ -25,14 +25,6 @@ static __inline__ int udplite_getfrag(void *from, char *to, int offset, ...@@ -25,14 +25,6 @@ static __inline__ int udplite_getfrag(void *from, char *to, int offset,
return copy_from_iter_full(to, len, &msg->msg_iter) ? 0 : -EFAULT; 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 * Checksumming routines
*/ */
......
...@@ -1598,7 +1598,7 @@ int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb) ...@@ -1598,7 +1598,7 @@ int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *skb)
} }
EXPORT_SYMBOL_GPL(__udp_enqueue_schedule_skb); 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 */ /* reclaim completely the forward allocated memory */
struct udp_sock *up = udp_sk(sk); struct udp_sock *up = udp_sk(sk);
...@@ -1611,10 +1611,14 @@ void udp_destruct_sock(struct sock *sk) ...@@ -1611,10 +1611,14 @@ void udp_destruct_sock(struct sock *sk)
kfree_skb(skb); kfree_skb(skb);
} }
udp_rmem_release(sk, total, 0, true); 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); inet_sock_destruct(sk);
} }
EXPORT_SYMBOL_GPL(udp_destruct_sock);
int udp_init_sock(struct sock *sk) int udp_init_sock(struct sock *sk)
{ {
...@@ -1622,7 +1626,6 @@ int udp_init_sock(struct sock *sk) ...@@ -1622,7 +1626,6 @@ int udp_init_sock(struct sock *sk)
sk->sk_destruct = udp_destruct_sock; sk->sk_destruct = udp_destruct_sock;
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(udp_init_sock);
void skb_consume_udp(struct sock *sk, struct sk_buff *skb, int len) void skb_consume_udp(struct sock *sk, struct sk_buff *skb, int len)
{ {
......
...@@ -17,6 +17,14 @@ ...@@ -17,6 +17,14 @@
struct udp_table udplite_table __read_mostly; struct udp_table udplite_table __read_mostly;
EXPORT_SYMBOL(udplite_table); 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) static int udplite_rcv(struct sk_buff *skb)
{ {
return __udp4_lib_rcv(skb, &udplite_table, IPPROTO_UDPLITE); return __udp4_lib_rcv(skb, &udplite_table, IPPROTO_UDPLITE);
......
...@@ -109,6 +109,12 @@ static __inline__ struct ipv6_pinfo *inet6_sk_generic(struct sock *sk) ...@@ -109,6 +109,12 @@ static __inline__ struct ipv6_pinfo *inet6_sk_generic(struct sock *sk)
return (struct ipv6_pinfo *)(((u8 *)sk) + offset); 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, static int inet6_create(struct net *net, struct socket *sock, int protocol,
int kern) int kern)
{ {
...@@ -201,7 +207,7 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol, ...@@ -201,7 +207,7 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol,
inet->hdrincl = 1; inet->hdrincl = 1;
} }
sk->sk_destruct = inet_sock_destruct; sk->sk_destruct = inet6_sock_destruct;
sk->sk_family = PF_INET6; sk->sk_family = PF_INET6;
sk->sk_protocol = protocol; sk->sk_protocol = protocol;
......
...@@ -56,6 +56,19 @@ ...@@ -56,6 +56,19 @@
#include <trace/events/skb.h> #include <trace/events/skb.h>
#include "udp_impl.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, static u32 udp6_ehashfn(const struct net *net,
const struct in6_addr *laddr, const struct in6_addr *laddr,
const u16 lport, const u16 lport,
...@@ -1733,7 +1746,7 @@ struct proto udpv6_prot = { ...@@ -1733,7 +1746,7 @@ struct proto udpv6_prot = {
.connect = ip6_datagram_connect, .connect = ip6_datagram_connect,
.disconnect = udp_disconnect, .disconnect = udp_disconnect,
.ioctl = udp_ioctl, .ioctl = udp_ioctl,
.init = udp_init_sock, .init = udpv6_init_sock,
.destroy = udpv6_destroy_sock, .destroy = udpv6_destroy_sock,
.setsockopt = udpv6_setsockopt, .setsockopt = udpv6_setsockopt,
.getsockopt = udpv6_getsockopt, .getsockopt = udpv6_getsockopt,
......
...@@ -12,6 +12,7 @@ int __udp6_lib_rcv(struct sk_buff *, struct udp_table *, int); ...@@ -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, int __udp6_lib_err(struct sk_buff *, struct inet6_skb_parm *, u8, u8, int,
__be32, struct udp_table *); __be32, struct udp_table *);
int udpv6_init_sock(struct sock *sk);
int udp_v6_get_port(struct sock *sk, unsigned short snum); int udp_v6_get_port(struct sock *sk, unsigned short snum);
void udp_v6_rehash(struct sock *sk); void udp_v6_rehash(struct sock *sk);
......
...@@ -12,6 +12,13 @@ ...@@ -12,6 +12,13 @@
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include "udp_impl.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) static int udplitev6_rcv(struct sk_buff *skb)
{ {
return __udp6_lib_rcv(skb, &udplite_table, IPPROTO_UDPLITE); return __udp6_lib_rcv(skb, &udplite_table, IPPROTO_UDPLITE);
...@@ -38,7 +45,7 @@ struct proto udplitev6_prot = { ...@@ -38,7 +45,7 @@ struct proto udplitev6_prot = {
.connect = ip6_datagram_connect, .connect = ip6_datagram_connect,
.disconnect = udp_disconnect, .disconnect = udp_disconnect,
.ioctl = udp_ioctl, .ioctl = udp_ioctl,
.init = udplite_sk_init, .init = udplitev6_sk_init,
.destroy = udpv6_destroy_sock, .destroy = udpv6_destroy_sock,
.setsockopt = udpv6_setsockopt, .setsockopt = udpv6_setsockopt,
.getsockopt = udpv6_getsockopt, .getsockopt = udpv6_getsockopt,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment