Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fcbdf09d authored by Octavian Purdila's avatar Octavian Purdila Committed by David S. Miller
Browse files

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: default avatarLeonard Crestez <lcrestez@ixiacom.com>
Signed-off-by: default avatarOctavian Purdila <opurdila@ixiacom.com>
Acked-by: default avatarEric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 2984961c
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -754,6 +754,7 @@ struct proto {
	void			(*unhash)(struct sock *sk);
	void			(*rehash)(struct sock *sk);
	int			(*get_port)(struct sock *sk, unsigned short snum);
	void			(*clear_sk)(struct sock *sk, int size);

	/* Keeping track of sockets in use */
#ifdef CONFIG_PROC_FS
@@ -852,6 +853,8 @@ static inline void __sk_prot_rehash(struct sock *sk)
	sk->sk_prot->hash(sk);
}

void sk_prot_clear_portaddr_nulls(struct sock *sk, int size);

/* About 10 seconds */
#define SOCK_DESTROY_TIME (10*HZ)

+35 −12
Original line number Diff line number Diff line
@@ -1009,6 +1009,36 @@ static void sock_copy(struct sock *nsk, const struct sock *osk)
#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,
		int family)
{
@@ -1021,19 +1051,12 @@ static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority,
		if (!sk)
			return sk;
		if (priority & __GFP_ZERO) {
			/*
			 * caches using SLAB_DESTROY_BY_RCU should let
			 * 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));
		}
	}
			if (prot->clear_sk)
				prot->clear_sk(sk, prot->obj_size);
			else
				sk_prot_clear_nulls(sk, prot->obj_size);
		}
	} else
		sk = kmalloc(prot->obj_size, priority);

	if (sk != NULL) {
+1 −0
Original line number Diff line number Diff line
@@ -1899,6 +1899,7 @@ struct proto udp_prot = {
	.compat_setsockopt = compat_udp_setsockopt,
	.compat_getsockopt = compat_udp_getsockopt,
#endif
	.clear_sk	   = sk_prot_clear_portaddr_nulls,
};
EXPORT_SYMBOL(udp_prot);

+1 −0
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ struct proto udplite_prot = {
	.compat_setsockopt = compat_udp_setsockopt,
	.compat_getsockopt = compat_udp_getsockopt,
#endif
	.clear_sk	   = sk_prot_clear_portaddr_nulls,
};
EXPORT_SYMBOL(udplite_prot);

+1 −0
Original line number Diff line number Diff line
@@ -1477,6 +1477,7 @@ struct proto udpv6_prot = {
	.compat_setsockopt = compat_udpv6_setsockopt,
	.compat_getsockopt = compat_udpv6_getsockopt,
#endif
	.clear_sk	   = sk_prot_clear_portaddr_nulls,
};

static struct inet_protosw udpv6_protosw = {
Loading