]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/3.0.79/ipv6-do-not-clear-pinet6-field.patch
5.1-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 3.0.79 / ipv6-do-not-clear-pinet6-field.patch
1 From 26b563a0f926b18c7d7a75cc9502f786d7bbcfdf Mon Sep 17 00:00:00 2001
2 From: Eric Dumazet <edumazet@google.com>
3 Date: Thu, 9 May 2013 10:28:16 +0000
4 Subject: ipv6: do not clear pinet6 field
5
6
7 From: Eric Dumazet <edumazet@google.com>
8
9 [ Upstream commit f77d602124d865c38705df7fa25c03de9c284ad2 ]
10
11 We have seen multiple NULL dereferences in __inet6_lookup_established()
12
13 After analysis, I found that inet6_sk() could be NULL while the
14 check for sk_family == AF_INET6 was true.
15
16 Bug was added in linux-2.6.29 when RCU lookups were introduced in UDP
17 and TCP stacks.
18
19 Once an IPv6 socket, using SLAB_DESTROY_BY_RCU is inserted in a hash
20 table, we no longer can clear pinet6 field.
21
22 This patch extends logic used in commit fcbdf09d9652c891
23 ("net: fix nulls list corruptions in sk_prot_alloc")
24
25 TCP/UDP/UDPLite IPv6 protocols provide their own .clear_sk() method
26 to make sure we do not clear pinet6 field.
27
28 At socket clone phase, we do not really care, as cloning the parent (non
29 NULL) pinet6 is not adding a fatal race.
30
31 Signed-off-by: Eric Dumazet <edumazet@google.com>
32 Signed-off-by: David S. Miller <davem@davemloft.net>
33 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
34 ---
35 include/net/sock.h | 12 ++++++++++++
36 net/core/sock.c | 12 ------------
37 net/ipv6/tcp_ipv6.c | 12 ++++++++++++
38 net/ipv6/udp.c | 13 ++++++++++++-
39 net/ipv6/udp_impl.h | 2 ++
40 net/ipv6/udplite.c | 2 +-
41 6 files changed, 39 insertions(+), 14 deletions(-)
42
43 --- a/include/net/sock.h
44 +++ b/include/net/sock.h
45 @@ -721,6 +721,18 @@ struct timewait_sock_ops;
46 struct inet_hashinfo;
47 struct raw_hashinfo;
48
49 +/*
50 + * caches using SLAB_DESTROY_BY_RCU should let .next pointer from nulls nodes
51 + * un-modified. Special care is taken when initializing object to zero.
52 + */
53 +static inline void sk_prot_clear_nulls(struct sock *sk, int size)
54 +{
55 + if (offsetof(struct sock, sk_node.next) != 0)
56 + memset(sk, 0, offsetof(struct sock, sk_node.next));
57 + memset(&sk->sk_node.pprev, 0,
58 + size - offsetof(struct sock, sk_node.pprev));
59 +}
60 +
61 /* Networking protocol blocks we attach to sockets.
62 * socket layer -> transport layer interface
63 * transport -> network interface is defined by struct inet_proto
64 --- a/net/core/sock.c
65 +++ b/net/core/sock.c
66 @@ -1017,18 +1017,6 @@ static void sock_copy(struct sock *nsk,
67 #endif
68 }
69
70 -/*
71 - * caches using SLAB_DESTROY_BY_RCU should let .next pointer from nulls nodes
72 - * un-modified. Special care is taken when initializing object to zero.
73 - */
74 -static inline void sk_prot_clear_nulls(struct sock *sk, int size)
75 -{
76 - if (offsetof(struct sock, sk_node.next) != 0)
77 - memset(sk, 0, offsetof(struct sock, sk_node.next));
78 - memset(&sk->sk_node.pprev, 0,
79 - size - offsetof(struct sock, sk_node.pprev));
80 -}
81 -
82 void sk_prot_clear_portaddr_nulls(struct sock *sk, int size)
83 {
84 unsigned long nulls1, nulls2;
85 --- a/net/ipv6/tcp_ipv6.c
86 +++ b/net/ipv6/tcp_ipv6.c
87 @@ -2205,6 +2205,17 @@ void tcp6_proc_exit(struct net *net)
88 }
89 #endif
90
91 +static void tcp_v6_clear_sk(struct sock *sk, int size)
92 +{
93 + struct inet_sock *inet = inet_sk(sk);
94 +
95 + /* we do not want to clear pinet6 field, because of RCU lookups */
96 + sk_prot_clear_nulls(sk, offsetof(struct inet_sock, pinet6));
97 +
98 + size -= offsetof(struct inet_sock, pinet6) + sizeof(inet->pinet6);
99 + memset(&inet->pinet6 + 1, 0, size);
100 +}
101 +
102 struct proto tcpv6_prot = {
103 .name = "TCPv6",
104 .owner = THIS_MODULE,
105 @@ -2244,6 +2255,7 @@ struct proto tcpv6_prot = {
106 .compat_setsockopt = compat_tcp_setsockopt,
107 .compat_getsockopt = compat_tcp_getsockopt,
108 #endif
109 + .clear_sk = tcp_v6_clear_sk,
110 };
111
112 static const struct inet6_protocol tcpv6_protocol = {
113 --- a/net/ipv6/udp.c
114 +++ b/net/ipv6/udp.c
115 @@ -1448,6 +1448,17 @@ void udp6_proc_exit(struct net *net) {
116 }
117 #endif /* CONFIG_PROC_FS */
118
119 +void udp_v6_clear_sk(struct sock *sk, int size)
120 +{
121 + struct inet_sock *inet = inet_sk(sk);
122 +
123 + /* we do not want to clear pinet6 field, because of RCU lookups */
124 + sk_prot_clear_portaddr_nulls(sk, offsetof(struct inet_sock, pinet6));
125 +
126 + size -= offsetof(struct inet_sock, pinet6) + sizeof(inet->pinet6);
127 + memset(&inet->pinet6 + 1, 0, size);
128 +}
129 +
130 /* ------------------------------------------------------------------------ */
131
132 struct proto udpv6_prot = {
133 @@ -1478,7 +1489,7 @@ struct proto udpv6_prot = {
134 .compat_setsockopt = compat_udpv6_setsockopt,
135 .compat_getsockopt = compat_udpv6_getsockopt,
136 #endif
137 - .clear_sk = sk_prot_clear_portaddr_nulls,
138 + .clear_sk = udp_v6_clear_sk,
139 };
140
141 static struct inet_protosw udpv6_protosw = {
142 --- a/net/ipv6/udp_impl.h
143 +++ b/net/ipv6/udp_impl.h
144 @@ -31,6 +31,8 @@ extern int udpv6_recvmsg(struct kiocb *i
145 extern int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb);
146 extern void udpv6_destroy_sock(struct sock *sk);
147
148 +extern void udp_v6_clear_sk(struct sock *sk, int size);
149 +
150 #ifdef CONFIG_PROC_FS
151 extern int udp6_seq_show(struct seq_file *seq, void *v);
152 #endif
153 --- a/net/ipv6/udplite.c
154 +++ b/net/ipv6/udplite.c
155 @@ -55,7 +55,7 @@ struct proto udplitev6_prot = {
156 .compat_setsockopt = compat_udpv6_setsockopt,
157 .compat_getsockopt = compat_udpv6_getsockopt,
158 #endif
159 - .clear_sk = sk_prot_clear_portaddr_nulls,
160 + .clear_sk = udp_v6_clear_sk,
161 };
162
163 static struct inet_protosw udplite6_protosw = {