]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
tcp/udp: Call inet6_destroy_sock() in IPv6 sk->sk_destruct().
authorKuniyuki Iwashima <kuniyu@amazon.com>
Thu, 6 Oct 2022 18:53:47 +0000 (11:53 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 26 Apr 2023 09:18:57 +0000 (11:18 +0200)
commit d38afeec26ed4739c640bf286c270559aab2ba5f 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, commit 03485f2adcde ("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 e27326009a3d ("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: 03485f2adcde ("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>
include/net/ipv6.h
include/net/udp.h
include/net/udplite.h
net/ipv4/udp.c
net/ipv4/udplite.c
net/ipv6/af_inet6.c
net/ipv6/udp.c
net/ipv6/udp_impl.h
net/ipv6/udplite.c

index c1eea6cb5bb979dde478353554ebff1cc9efe3a7..7f7e90c1992fa42b71f18ca0147348182d5a6341 100644 (file)
@@ -943,6 +943,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, int *uaddr_len,
index 07135de001668e221e5a6deefbc840a1a3590cfa..d7bbb2e0c82c1716ce0dd61b68d4fd50c40cacb2 100644 (file)
@@ -251,7 +251,7 @@ static inline int udp_rqueue_get(struct sock *sk)
 }
 
 /* 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);
index 9185e45b997ff67ce2999c6b0665c8d693b44338..c59ba86668af0e5734195263a8080f3e1f44a0a6 100644 (file)
@@ -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
  */
index 16573afc30695c974196fae8315a29a3013a6321..21429419abb5294d790017a4f08150525ece24d0 100644 (file)
@@ -1380,7 +1380,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);
@@ -1393,10 +1393,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)
 {
@@ -1404,7 +1408,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)
 {
index 59f10fe9782e57b9d40facc369f412195639b84b..460379bf79890333e0321a1a8b9307e64723301d 100644 (file)
 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);
index f1a556d9e23b57a000aecabe142a1ed9811a8317..758462576e80138726228ed6265c35f8b2301568 100644 (file)
@@ -107,6 +107,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)
 {
@@ -199,7 +205,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;
 
index 0d4f82f9ebfd5f296582143a04e3f0a5c8fa626b..2b1f153ad6a93cdc6a288a552af7da552dd5f40c 100644 (file)
@@ -66,6 +66,19 @@ static bool udp6_lib_exact_dif_match(struct net *net, struct sk_buff *skb)
        return false;
 }
 
+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,
@@ -1552,7 +1565,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,
index 7903e21c178b94f1a58faf4362609908b64e5fa3..e5d067b09ccfae013f3f71bc501655daa6f631e5 100644 (file)
@@ -12,6 +12,7 @@ int __udp6_lib_rcv(struct sk_buff *, struct udp_table *, int);
 void __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);
 
 int udpv6_getsockopt(struct sock *sk, int level, int optname,
index 2784cc363f2b533df16effcec35ffcdc2b528885..1724db8bd4ff16f621ea75ea142dce8d2605d1a1 100644 (file)
 #include <linux/export.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);
@@ -39,7 +46,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,