mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	net: fix nulls list corruptions in sk_prot_alloc
Special care is taken inside sk_port_alloc to avoid overwriting skc_node/skc_nulls_node. We should also avoid overwriting skc_bind_node/skc_portaddr_node. The patch fixes the following crash: BUG: unable to handle kernel paging request at fffffffffffffff0 IP: [<ffffffff812ec6dd>] udp4_lib_lookup2+0xad/0x370 [<ffffffff812ecc22>] __udp4_lib_lookup+0x282/0x360 [<ffffffff812ed63e>] __udp4_lib_rcv+0x31e/0x700 [<ffffffff812bba45>] ? ip_local_deliver_finish+0x65/0x190 [<ffffffff812bbbf8>] ? ip_local_deliver+0x88/0xa0 [<ffffffff812eda35>] udp_rcv+0x15/0x20 [<ffffffff812bba45>] ip_local_deliver_finish+0x65/0x190 [<ffffffff812bbbf8>] ip_local_deliver+0x88/0xa0 [<ffffffff812bb2cd>] ip_rcv_finish+0x32d/0x6f0 [<ffffffff8128c14c>] ? netif_receive_skb+0x99c/0x11c0 [<ffffffff812bb94b>] ip_rcv+0x2bb/0x350 [<ffffffff8128c14c>] netif_receive_skb+0x99c/0x11c0 Signed-off-by: Leonard Crestez <lcrestez@ixiacom.com> Signed-off-by: Octavian Purdila <opurdila@ixiacom.com> Acked-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									2984961c38
								
							
						
					
					
						commit
						fcbdf09d96
					
				
					 6 changed files with 42 additions and 12 deletions
				
			
		| 
						 | 
					@ -754,6 +754,7 @@ struct proto {
 | 
				
			||||||
	void			(*unhash)(struct sock *sk);
 | 
						void			(*unhash)(struct sock *sk);
 | 
				
			||||||
	void			(*rehash)(struct sock *sk);
 | 
						void			(*rehash)(struct sock *sk);
 | 
				
			||||||
	int			(*get_port)(struct sock *sk, unsigned short snum);
 | 
						int			(*get_port)(struct sock *sk, unsigned short snum);
 | 
				
			||||||
 | 
						void			(*clear_sk)(struct sock *sk, int size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Keeping track of sockets in use */
 | 
						/* Keeping track of sockets in use */
 | 
				
			||||||
#ifdef CONFIG_PROC_FS
 | 
					#ifdef CONFIG_PROC_FS
 | 
				
			||||||
| 
						 | 
					@ -852,6 +853,8 @@ static inline void __sk_prot_rehash(struct sock *sk)
 | 
				
			||||||
	sk->sk_prot->hash(sk);
 | 
						sk->sk_prot->hash(sk);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void sk_prot_clear_portaddr_nulls(struct sock *sk, int size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* About 10 seconds */
 | 
					/* About 10 seconds */
 | 
				
			||||||
#define SOCK_DESTROY_TIME (10*HZ)
 | 
					#define SOCK_DESTROY_TIME (10*HZ)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1009,6 +1009,36 @@ static void sock_copy(struct sock *nsk, const struct sock *osk)
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * caches using SLAB_DESTROY_BY_RCU should let .next pointer from nulls nodes
 | 
				
			||||||
 | 
					 * un-modified. Special care is taken when initializing object to zero.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static inline void sk_prot_clear_nulls(struct sock *sk, int size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (offsetof(struct sock, sk_node.next) != 0)
 | 
				
			||||||
 | 
							memset(sk, 0, offsetof(struct sock, sk_node.next));
 | 
				
			||||||
 | 
						memset(&sk->sk_node.pprev, 0,
 | 
				
			||||||
 | 
						       size - offsetof(struct sock, sk_node.pprev));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void sk_prot_clear_portaddr_nulls(struct sock *sk, int size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned long nulls1, nulls2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nulls1 = offsetof(struct sock, __sk_common.skc_node.next);
 | 
				
			||||||
 | 
						nulls2 = offsetof(struct sock, __sk_common.skc_portaddr_node.next);
 | 
				
			||||||
 | 
						if (nulls1 > nulls2)
 | 
				
			||||||
 | 
							swap(nulls1, nulls2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (nulls1 != 0)
 | 
				
			||||||
 | 
							memset((char *)sk, 0, nulls1);
 | 
				
			||||||
 | 
						memset((char *)sk + nulls1 + sizeof(void *), 0,
 | 
				
			||||||
 | 
						       nulls2 - nulls1 - sizeof(void *));
 | 
				
			||||||
 | 
						memset((char *)sk + nulls2 + sizeof(void *), 0,
 | 
				
			||||||
 | 
						       size - nulls2 - sizeof(void *));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					EXPORT_SYMBOL(sk_prot_clear_portaddr_nulls);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority,
 | 
					static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority,
 | 
				
			||||||
		int family)
 | 
							int family)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -1021,19 +1051,12 @@ static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority,
 | 
				
			||||||
		if (!sk)
 | 
							if (!sk)
 | 
				
			||||||
			return sk;
 | 
								return sk;
 | 
				
			||||||
		if (priority & __GFP_ZERO) {
 | 
							if (priority & __GFP_ZERO) {
 | 
				
			||||||
			/*
 | 
								if (prot->clear_sk)
 | 
				
			||||||
			 * caches using SLAB_DESTROY_BY_RCU should let
 | 
									prot->clear_sk(sk, prot->obj_size);
 | 
				
			||||||
			 * sk_node.next un-modified. Special care is taken
 | 
					 | 
				
			||||||
			 * when initializing object to zero.
 | 
					 | 
				
			||||||
			 */
 | 
					 | 
				
			||||||
			if (offsetof(struct sock, sk_node.next) != 0)
 | 
					 | 
				
			||||||
				memset(sk, 0, offsetof(struct sock, sk_node.next));
 | 
					 | 
				
			||||||
			memset(&sk->sk_node.pprev, 0,
 | 
					 | 
				
			||||||
			       prot->obj_size - offsetof(struct sock,
 | 
					 | 
				
			||||||
							 sk_node.pprev));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
			else
 | 
								else
 | 
				
			||||||
 | 
									sk_prot_clear_nulls(sk, prot->obj_size);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else
 | 
				
			||||||
		sk = kmalloc(prot->obj_size, priority);
 | 
							sk = kmalloc(prot->obj_size, priority);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (sk != NULL) {
 | 
						if (sk != NULL) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1899,6 +1899,7 @@ struct proto udp_prot = {
 | 
				
			||||||
	.compat_setsockopt = compat_udp_setsockopt,
 | 
						.compat_setsockopt = compat_udp_setsockopt,
 | 
				
			||||||
	.compat_getsockopt = compat_udp_getsockopt,
 | 
						.compat_getsockopt = compat_udp_getsockopt,
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
						.clear_sk	   = sk_prot_clear_portaddr_nulls,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
EXPORT_SYMBOL(udp_prot);
 | 
					EXPORT_SYMBOL(udp_prot);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -57,6 +57,7 @@ struct proto 	udplite_prot = {
 | 
				
			||||||
	.compat_setsockopt = compat_udp_setsockopt,
 | 
						.compat_setsockopt = compat_udp_setsockopt,
 | 
				
			||||||
	.compat_getsockopt = compat_udp_getsockopt,
 | 
						.compat_getsockopt = compat_udp_getsockopt,
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
						.clear_sk	   = sk_prot_clear_portaddr_nulls,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
EXPORT_SYMBOL(udplite_prot);
 | 
					EXPORT_SYMBOL(udplite_prot);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1477,6 +1477,7 @@ struct proto udpv6_prot = {
 | 
				
			||||||
	.compat_setsockopt = compat_udpv6_setsockopt,
 | 
						.compat_setsockopt = compat_udpv6_setsockopt,
 | 
				
			||||||
	.compat_getsockopt = compat_udpv6_getsockopt,
 | 
						.compat_getsockopt = compat_udpv6_getsockopt,
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
						.clear_sk	   = sk_prot_clear_portaddr_nulls,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct inet_protosw udpv6_protosw = {
 | 
					static struct inet_protosw udpv6_protosw = {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,6 +55,7 @@ struct proto udplitev6_prot = {
 | 
				
			||||||
	.compat_setsockopt = compat_udpv6_setsockopt,
 | 
						.compat_setsockopt = compat_udpv6_setsockopt,
 | 
				
			||||||
	.compat_getsockopt = compat_udpv6_getsockopt,
 | 
						.compat_getsockopt = compat_udpv6_getsockopt,
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
						.clear_sk	   = sk_prot_clear_portaddr_nulls,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct inet_protosw udplite6_protosw = {
 | 
					static struct inet_protosw udplite6_protosw = {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue