mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	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, 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>
This commit is contained in:
		
							parent
							
								
									21985f4337
								
							
						
					
					
						commit
						d38afeec26
					
				
					 9 changed files with 46 additions and 15 deletions
				
			
		|  | @ -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 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, | ||||
|  |  | |||
|  | @ -247,7 +247,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); | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
| 
 | ||||
| /* 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 | ||||
|  */ | ||||
|  |  | |||
|  | @ -1598,7 +1598,7 @@ int __udp_enqueue_schedule_skb(struct sock *sk, struct sk_buff *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 */ | ||||
| 	struct udp_sock *up = udp_sk(sk); | ||||
|  | @ -1611,10 +1611,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) | ||||
| { | ||||
|  | @ -1622,7 +1626,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); | ||||
|  |  | |||
|  | @ -109,6 +109,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) | ||||
| { | ||||
|  | @ -201,7 +207,7 @@ static int inet6_create(struct net *net, struct socket *sock, int 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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -56,6 +56,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, | ||||
|  | @ -1733,7 +1746,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, | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Kuniyuki Iwashima
						Kuniyuki Iwashima