Skip to content

Commit fcbdf09

Browse files
Octavian Purdiladavem330
authored andcommitted
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>
1 parent 2984961 commit fcbdf09

File tree

6 files changed

+42
-12
lines changed

6 files changed

+42
-12
lines changed

include/net/sock.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,7 @@ struct proto {
754754
void (*unhash)(struct sock *sk);
755755
void (*rehash)(struct sock *sk);
756756
int (*get_port)(struct sock *sk, unsigned short snum);
757+
void (*clear_sk)(struct sock *sk, int size);
757758

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

856+
void sk_prot_clear_portaddr_nulls(struct sock *sk, int size);
857+
855858
/* About 10 seconds */
856859
#define SOCK_DESTROY_TIME (10*HZ)
857860

net/core/sock.c

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,36 @@ static void sock_copy(struct sock *nsk, const struct sock *osk)
10091009
#endif
10101010
}
10111011

1012+
/*
1013+
* caches using SLAB_DESTROY_BY_RCU should let .next pointer from nulls nodes
1014+
* un-modified. Special care is taken when initializing object to zero.
1015+
*/
1016+
static inline void sk_prot_clear_nulls(struct sock *sk, int size)
1017+
{
1018+
if (offsetof(struct sock, sk_node.next) != 0)
1019+
memset(sk, 0, offsetof(struct sock, sk_node.next));
1020+
memset(&sk->sk_node.pprev, 0,
1021+
size - offsetof(struct sock, sk_node.pprev));
1022+
}
1023+
1024+
void sk_prot_clear_portaddr_nulls(struct sock *sk, int size)
1025+
{
1026+
unsigned long nulls1, nulls2;
1027+
1028+
nulls1 = offsetof(struct sock, __sk_common.skc_node.next);
1029+
nulls2 = offsetof(struct sock, __sk_common.skc_portaddr_node.next);
1030+
if (nulls1 > nulls2)
1031+
swap(nulls1, nulls2);
1032+
1033+
if (nulls1 != 0)
1034+
memset((char *)sk, 0, nulls1);
1035+
memset((char *)sk + nulls1 + sizeof(void *), 0,
1036+
nulls2 - nulls1 - sizeof(void *));
1037+
memset((char *)sk + nulls2 + sizeof(void *), 0,
1038+
size - nulls2 - sizeof(void *));
1039+
}
1040+
EXPORT_SYMBOL(sk_prot_clear_portaddr_nulls);
1041+
10121042
static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority,
10131043
int family)
10141044
{
@@ -1021,19 +1051,12 @@ static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority,
10211051
if (!sk)
10221052
return sk;
10231053
if (priority & __GFP_ZERO) {
1024-
/*
1025-
* caches using SLAB_DESTROY_BY_RCU should let
1026-
* sk_node.next un-modified. Special care is taken
1027-
* when initializing object to zero.
1028-
*/
1029-
if (offsetof(struct sock, sk_node.next) != 0)
1030-
memset(sk, 0, offsetof(struct sock, sk_node.next));
1031-
memset(&sk->sk_node.pprev, 0,
1032-
prot->obj_size - offsetof(struct sock,
1033-
sk_node.pprev));
1054+
if (prot->clear_sk)
1055+
prot->clear_sk(sk, prot->obj_size);
1056+
else
1057+
sk_prot_clear_nulls(sk, prot->obj_size);
10341058
}
1035-
}
1036-
else
1059+
} else
10371060
sk = kmalloc(prot->obj_size, priority);
10381061

10391062
if (sk != NULL) {

net/ipv4/udp.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1899,6 +1899,7 @@ struct proto udp_prot = {
18991899
.compat_setsockopt = compat_udp_setsockopt,
19001900
.compat_getsockopt = compat_udp_getsockopt,
19011901
#endif
1902+
.clear_sk = sk_prot_clear_portaddr_nulls,
19021903
};
19031904
EXPORT_SYMBOL(udp_prot);
19041905

net/ipv4/udplite.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ struct proto udplite_prot = {
5757
.compat_setsockopt = compat_udp_setsockopt,
5858
.compat_getsockopt = compat_udp_getsockopt,
5959
#endif
60+
.clear_sk = sk_prot_clear_portaddr_nulls,
6061
};
6162
EXPORT_SYMBOL(udplite_prot);
6263

net/ipv6/udp.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1477,6 +1477,7 @@ struct proto udpv6_prot = {
14771477
.compat_setsockopt = compat_udpv6_setsockopt,
14781478
.compat_getsockopt = compat_udpv6_getsockopt,
14791479
#endif
1480+
.clear_sk = sk_prot_clear_portaddr_nulls,
14801481
};
14811482

14821483
static struct inet_protosw udpv6_protosw = {

net/ipv6/udplite.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ struct proto udplitev6_prot = {
5555
.compat_setsockopt = compat_udpv6_setsockopt,
5656
.compat_getsockopt = compat_udpv6_getsockopt,
5757
#endif
58+
.clear_sk = sk_prot_clear_portaddr_nulls,
5859
};
5960

6061
static struct inet_protosw udplite6_protosw = {

0 commit comments

Comments
 (0)