kernel: 5.10: wireguard: backport 5.12-rc1 changes in net.git
[openwrt/staging/hauke.git] / target / linux / generic / backport-5.10 / 070-net-icmp-pass-zeroed-opts-from-icmp-v6-_ndo_send-bef.patch
1 From 4a25324891a32d080589a6e3a4dec2be2d9e3d60 Mon Sep 17 00:00:00 2001
2 From: "Jason A. Donenfeld" <Jason@zx2c4.com>
3 Date: Tue, 23 Feb 2021 14:18:58 +0100
4 Subject: [PATCH] net: icmp: pass zeroed opts from icmp{,v6}_ndo_send before
5 sending
6
7 commit ee576c47db60432c37e54b1e2b43a8ca6d3a8dca upstream.
8
9 The icmp{,v6}_send functions make all sorts of use of skb->cb, casting
10 it with IPCB or IP6CB, assuming the skb to have come directly from the
11 inet layer. But when the packet comes from the ndo layer, especially
12 when forwarded, there's no telling what might be in skb->cb at that
13 point. As a result, the icmp sending code risks reading bogus memory
14 contents, which can result in nasty stack overflows such as this one
15 reported by a user:
16
17 panic+0x108/0x2ea
18 __stack_chk_fail+0x14/0x20
19 __icmp_send+0x5bd/0x5c0
20 icmp_ndo_send+0x148/0x160
21
22 In icmp_send, skb->cb is cast with IPCB and an ip_options struct is read
23 from it. The optlen parameter there is of particular note, as it can
24 induce writes beyond bounds. There are quite a few ways that can happen
25 in __ip_options_echo. For example:
26
27 // sptr/skb are attacker-controlled skb bytes
28 sptr = skb_network_header(skb);
29 // dptr/dopt points to stack memory allocated by __icmp_send
30 dptr = dopt->__data;
31 // sopt is the corrupt skb->cb in question
32 if (sopt->rr) {
33 optlen = sptr[sopt->rr+1]; // corrupt skb->cb + skb->data
34 soffset = sptr[sopt->rr+2]; // corrupt skb->cb + skb->data
35 // this now writes potentially attacker-controlled data, over
36 // flowing the stack:
37 memcpy(dptr, sptr+sopt->rr, optlen);
38 }
39
40 In the icmpv6_send case, the story is similar, but not as dire, as only
41 IP6CB(skb)->iif and IP6CB(skb)->dsthao are used. The dsthao case is
42 worse than the iif case, but it is passed to ipv6_find_tlv, which does
43 a bit of bounds checking on the value.
44
45 This is easy to simulate by doing a `memset(skb->cb, 0x41,
46 sizeof(skb->cb));` before calling icmp{,v6}_ndo_send, and it's only by
47 good fortune and the rarity of icmp sending from that context that we've
48 avoided reports like this until now. For example, in KASAN:
49
50 BUG: KASAN: stack-out-of-bounds in __ip_options_echo+0xa0e/0x12b0
51 Write of size 38 at addr ffff888006f1f80e by task ping/89
52 CPU: 2 PID: 89 Comm: ping Not tainted 5.10.0-rc7-debug+ #5
53 Call Trace:
54 dump_stack+0x9a/0xcc
55 print_address_description.constprop.0+0x1a/0x160
56 __kasan_report.cold+0x20/0x38
57 kasan_report+0x32/0x40
58 check_memory_region+0x145/0x1a0
59 memcpy+0x39/0x60
60 __ip_options_echo+0xa0e/0x12b0
61 __icmp_send+0x744/0x1700
62
63 Actually, out of the 4 drivers that do this, only gtp zeroed the cb for
64 the v4 case, while the rest did not. So this commit actually removes the
65 gtp-specific zeroing, while putting the code where it belongs in the
66 shared infrastructure of icmp{,v6}_ndo_send.
67
68 This commit fixes the issue by passing an empty IPCB or IP6CB along to
69 the functions that actually do the work. For the icmp_send, this was
70 already trivial, thanks to __icmp_send providing the plumbing function.
71 For icmpv6_send, this required a tiny bit of refactoring to make it
72 behave like the v4 case, after which it was straight forward.
73
74 Fixes: a2b78e9b2cac ("sunvnet: generate ICMP PTMUD messages for smaller port MTUs")
75 Reported-by: SinYu <liuxyon@gmail.com>
76 Reviewed-by: Willem de Bruijn <willemb@google.com>
77 Link: https://lore.kernel.org/netdev/CAF=yD-LOF116aHub6RMe8vB8ZpnrrnoTdqhobEx+bvoA8AsP0w@mail.gmail.com/T/
78 Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
79 Link: https://lore.kernel.org/r/20210223131858.72082-1-Jason@zx2c4.com
80 Signed-off-by: Jakub Kicinski <kuba@kernel.org>
81 [Jason: backported to 5.10]
82 Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
83 ---
84 include/linux/icmpv6.h | 17 ++++++++++++++---
85 include/linux/ipv6.h | 1 -
86 include/net/icmp.h | 6 +++++-
87 net/ipv4/icmp.c | 5 +++--
88 net/ipv6/icmp.c | 16 ++++++++--------
89 net/ipv6/ip6_icmp.c | 12 +++++++-----
90 6 files changed, 37 insertions(+), 20 deletions(-)
91
92 --- a/drivers/net/gtp.c
93 +++ b/drivers/net/gtp.c
94 @@ -539,7 +539,6 @@ static int gtp_build_skb_ip4(struct sk_b
95 if (!skb_is_gso(skb) && (iph->frag_off & htons(IP_DF)) &&
96 mtu < ntohs(iph->tot_len)) {
97 netdev_dbg(dev, "packet too big, fragmentation needed\n");
98 - memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
99 icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
100 htonl(mtu));
101 goto err_rt;
102 --- a/include/linux/icmpv6.h
103 +++ b/include/linux/icmpv6.h
104 @@ -3,6 +3,7 @@
105 #define _LINUX_ICMPV6_H
106
107 #include <linux/skbuff.h>
108 +#include <linux/ipv6.h>
109 #include <uapi/linux/icmpv6.h>
110
111 static inline struct icmp6hdr *icmp6_hdr(const struct sk_buff *skb)
112 @@ -15,13 +16,16 @@ static inline struct icmp6hdr *icmp6_hdr
113 #if IS_ENABLED(CONFIG_IPV6)
114
115 typedef void ip6_icmp_send_t(struct sk_buff *skb, u8 type, u8 code, __u32 info,
116 - const struct in6_addr *force_saddr);
117 + const struct in6_addr *force_saddr,
118 + const struct inet6_skb_parm *parm);
119 #if IS_BUILTIN(CONFIG_IPV6)
120 void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
121 - const struct in6_addr *force_saddr);
122 -static inline void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
123 + const struct in6_addr *force_saddr,
124 + const struct inet6_skb_parm *parm);
125 +static inline void __icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
126 + const struct inet6_skb_parm *parm)
127 {
128 - icmp6_send(skb, type, code, info, NULL);
129 + icmp6_send(skb, type, code, info, NULL, parm);
130 }
131 static inline int inet6_register_icmp_sender(ip6_icmp_send_t *fn)
132 {
133 @@ -34,18 +38,28 @@ static inline int inet6_unregister_icmp_
134 return 0;
135 }
136 #else
137 -extern void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info);
138 +extern void __icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
139 + const struct inet6_skb_parm *parm);
140 extern int inet6_register_icmp_sender(ip6_icmp_send_t *fn);
141 extern int inet6_unregister_icmp_sender(ip6_icmp_send_t *fn);
142 #endif
143
144 +static inline void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
145 +{
146 + __icmpv6_send(skb, type, code, info, IP6CB(skb));
147 +}
148 +
149 int ip6_err_gen_icmpv6_unreach(struct sk_buff *skb, int nhs, int type,
150 unsigned int data_len);
151
152 #if IS_ENABLED(CONFIG_NF_NAT)
153 void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info);
154 #else
155 -#define icmpv6_ndo_send icmpv6_send
156 +static inline void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info)
157 +{
158 + struct inet6_skb_parm parm = { 0 };
159 + __icmpv6_send(skb_in, type, code, info, &parm);
160 +}
161 #endif
162
163 #else
164 --- a/include/linux/ipv6.h
165 +++ b/include/linux/ipv6.h
166 @@ -84,7 +84,6 @@ struct ipv6_params {
167 __s32 autoconf;
168 };
169 extern struct ipv6_params ipv6_defaults;
170 -#include <linux/icmpv6.h>
171 #include <linux/tcp.h>
172 #include <linux/udp.h>
173
174 --- a/include/net/icmp.h
175 +++ b/include/net/icmp.h
176 @@ -46,7 +46,11 @@ static inline void icmp_send(struct sk_b
177 #if IS_ENABLED(CONFIG_NF_NAT)
178 void icmp_ndo_send(struct sk_buff *skb_in, int type, int code, __be32 info);
179 #else
180 -#define icmp_ndo_send icmp_send
181 +static inline void icmp_ndo_send(struct sk_buff *skb_in, int type, int code, __be32 info)
182 +{
183 + struct ip_options opts = { 0 };
184 + __icmp_send(skb_in, type, code, info, &opts);
185 +}
186 #endif
187
188 int icmp_rcv(struct sk_buff *skb);
189 --- a/net/ipv4/icmp.c
190 +++ b/net/ipv4/icmp.c
191 @@ -775,13 +775,14 @@ EXPORT_SYMBOL(__icmp_send);
192 void icmp_ndo_send(struct sk_buff *skb_in, int type, int code, __be32 info)
193 {
194 struct sk_buff *cloned_skb = NULL;
195 + struct ip_options opts = { 0 };
196 enum ip_conntrack_info ctinfo;
197 struct nf_conn *ct;
198 __be32 orig_ip;
199
200 ct = nf_ct_get(skb_in, &ctinfo);
201 if (!ct || !(ct->status & IPS_SRC_NAT)) {
202 - icmp_send(skb_in, type, code, info);
203 + __icmp_send(skb_in, type, code, info, &opts);
204 return;
205 }
206
207 @@ -796,7 +797,7 @@ void icmp_ndo_send(struct sk_buff *skb_i
208
209 orig_ip = ip_hdr(skb_in)->saddr;
210 ip_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.ip;
211 - icmp_send(skb_in, type, code, info);
212 + __icmp_send(skb_in, type, code, info, &opts);
213 ip_hdr(skb_in)->saddr = orig_ip;
214 out:
215 consume_skb(cloned_skb);
216 --- a/net/ipv6/icmp.c
217 +++ b/net/ipv6/icmp.c
218 @@ -331,10 +331,9 @@ static int icmpv6_getfrag(void *from, ch
219 }
220
221 #if IS_ENABLED(CONFIG_IPV6_MIP6)
222 -static void mip6_addr_swap(struct sk_buff *skb)
223 +static void mip6_addr_swap(struct sk_buff *skb, const struct inet6_skb_parm *opt)
224 {
225 struct ipv6hdr *iph = ipv6_hdr(skb);
226 - struct inet6_skb_parm *opt = IP6CB(skb);
227 struct ipv6_destopt_hao *hao;
228 struct in6_addr tmp;
229 int off;
230 @@ -351,7 +350,7 @@ static void mip6_addr_swap(struct sk_buf
231 }
232 }
233 #else
234 -static inline void mip6_addr_swap(struct sk_buff *skb) {}
235 +static inline void mip6_addr_swap(struct sk_buff *skb, const struct inet6_skb_parm *opt) {}
236 #endif
237
238 static struct dst_entry *icmpv6_route_lookup(struct net *net,
239 @@ -446,7 +445,8 @@ static int icmp6_iif(const struct sk_buf
240 * Send an ICMP message in response to a packet in error
241 */
242 void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
243 - const struct in6_addr *force_saddr)
244 + const struct in6_addr *force_saddr,
245 + const struct inet6_skb_parm *parm)
246 {
247 struct inet6_dev *idev = NULL;
248 struct ipv6hdr *hdr = ipv6_hdr(skb);
249 @@ -542,7 +542,7 @@ void icmp6_send(struct sk_buff *skb, u8
250 if (!(skb->dev->flags & IFF_LOOPBACK) && !icmpv6_global_allow(net, type))
251 goto out_bh_enable;
252
253 - mip6_addr_swap(skb);
254 + mip6_addr_swap(skb, parm);
255
256 sk = icmpv6_xmit_lock(net);
257 if (!sk)
258 @@ -559,7 +559,7 @@ void icmp6_send(struct sk_buff *skb, u8
259 /* select a more meaningful saddr from input if */
260 struct net_device *in_netdev;
261
262 - in_netdev = dev_get_by_index(net, IP6CB(skb)->iif);
263 + in_netdev = dev_get_by_index(net, parm->iif);
264 if (in_netdev) {
265 ipv6_dev_get_saddr(net, in_netdev, &fl6.daddr,
266 inet6_sk(sk)->srcprefs,
267 @@ -640,7 +640,7 @@ EXPORT_SYMBOL(icmp6_send);
268 */
269 void icmpv6_param_prob(struct sk_buff *skb, u8 code, int pos)
270 {
271 - icmp6_send(skb, ICMPV6_PARAMPROB, code, pos, NULL);
272 + icmp6_send(skb, ICMPV6_PARAMPROB, code, pos, NULL, IP6CB(skb));
273 kfree_skb(skb);
274 }
275
276 @@ -697,10 +697,10 @@ int ip6_err_gen_icmpv6_unreach(struct sk
277 }
278 if (type == ICMP_TIME_EXCEEDED)
279 icmp6_send(skb2, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
280 - info, &temp_saddr);
281 + info, &temp_saddr, IP6CB(skb2));
282 else
283 icmp6_send(skb2, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH,
284 - info, &temp_saddr);
285 + info, &temp_saddr, IP6CB(skb2));
286 if (rt)
287 ip6_rt_put(rt);
288
289 --- a/net/ipv6/ip6_icmp.c
290 +++ b/net/ipv6/ip6_icmp.c
291 @@ -33,23 +33,25 @@ int inet6_unregister_icmp_sender(ip6_icm
292 }
293 EXPORT_SYMBOL(inet6_unregister_icmp_sender);
294
295 -void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
296 +void __icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
297 + const struct inet6_skb_parm *parm)
298 {
299 ip6_icmp_send_t *send;
300
301 rcu_read_lock();
302 send = rcu_dereference(ip6_icmp_send);
303 if (send)
304 - send(skb, type, code, info, NULL);
305 + send(skb, type, code, info, NULL, parm);
306 rcu_read_unlock();
307 }
308 -EXPORT_SYMBOL(icmpv6_send);
309 +EXPORT_SYMBOL(__icmpv6_send);
310 #endif
311
312 #if IS_ENABLED(CONFIG_NF_NAT)
313 #include <net/netfilter/nf_conntrack.h>
314 void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info)
315 {
316 + struct inet6_skb_parm parm = { 0 };
317 struct sk_buff *cloned_skb = NULL;
318 enum ip_conntrack_info ctinfo;
319 struct in6_addr orig_ip;
320 @@ -57,7 +59,7 @@ void icmpv6_ndo_send(struct sk_buff *skb
321
322 ct = nf_ct_get(skb_in, &ctinfo);
323 if (!ct || !(ct->status & IPS_SRC_NAT)) {
324 - icmpv6_send(skb_in, type, code, info);
325 + __icmpv6_send(skb_in, type, code, info, &parm);
326 return;
327 }
328
329 @@ -72,7 +74,7 @@ void icmpv6_ndo_send(struct sk_buff *skb
330
331 orig_ip = ipv6_hdr(skb_in)->saddr;
332 ipv6_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.in6;
333 - icmpv6_send(skb_in, type, code, info);
334 + __icmpv6_send(skb_in, type, code, info, &parm);
335 ipv6_hdr(skb_in)->saddr = orig_ip;
336 out:
337 consume_skb(cloned_skb);