diff options
author | Eric Dumazet <edumazet@google.com> | 2017-02-05 02:18:55 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-02-18 09:11:41 -0500 |
commit | ae1768bbbc469b75662c6714957fe5886cc960c4 (patch) | |
tree | dec13ff13f970cea0195745d5423012e9f839e30 | |
parent | 66cdd4347573027f95b4c7b50a7b20079ce66919 (diff) |
ip6_gre: fix ip6gre_err() invalid reads
[ Upstream commit 7892032cfe67f4bde6fc2ee967e45a8fbaf33756 ]
Andrey Konovalov reported out of bound accesses in ip6gre_err()
If GRE flags contains GRE_KEY, the following expression
*(((__be32 *)p) + (grehlen / 4) - 1)
accesses data ~40 bytes after the expected point, since
grehlen includes the size of IPv6 headers.
Let's use a "struct gre_base_hdr *greh" pointer to make this
code more readable.
p[1] becomes greh->protocol.
grhlen is the GRE header length.
Fixes: c12b395a4664 ("gre: Support GRE over IPv6")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Andrey Konovalov <andreyknvl@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | net/ipv6/ip6_gre.c | 40 |
1 files changed, 21 insertions, 19 deletions
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index d7d6d3ae0b3b..0a5922055da2 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c | |||
@@ -367,35 +367,37 @@ static void ip6gre_tunnel_uninit(struct net_device *dev) | |||
367 | 367 | ||
368 | 368 | ||
369 | static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt, | 369 | static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt, |
370 | u8 type, u8 code, int offset, __be32 info) | 370 | u8 type, u8 code, int offset, __be32 info) |
371 | { | 371 | { |
372 | const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)skb->data; | 372 | const struct gre_base_hdr *greh; |
373 | __be16 *p = (__be16 *)(skb->data + offset); | 373 | const struct ipv6hdr *ipv6h; |
374 | int grehlen = offset + 4; | 374 | int grehlen = sizeof(*greh); |
375 | struct ip6_tnl *t; | 375 | struct ip6_tnl *t; |
376 | int key_off = 0; | ||
376 | __be16 flags; | 377 | __be16 flags; |
378 | __be32 key; | ||
377 | 379 | ||
378 | flags = p[0]; | 380 | if (!pskb_may_pull(skb, offset + grehlen)) |
379 | if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) { | 381 | return; |
380 | if (flags&(GRE_VERSION|GRE_ROUTING)) | 382 | greh = (const struct gre_base_hdr *)(skb->data + offset); |
381 | return; | 383 | flags = greh->flags; |
382 | if (flags&GRE_KEY) { | 384 | if (flags & (GRE_VERSION | GRE_ROUTING)) |
383 | grehlen += 4; | 385 | return; |
384 | if (flags&GRE_CSUM) | 386 | if (flags & GRE_CSUM) |
385 | grehlen += 4; | 387 | grehlen += 4; |
386 | } | 388 | if (flags & GRE_KEY) { |
389 | key_off = grehlen + offset; | ||
390 | grehlen += 4; | ||
387 | } | 391 | } |
388 | 392 | ||
389 | /* If only 8 bytes returned, keyed message will be dropped here */ | 393 | if (!pskb_may_pull(skb, offset + grehlen)) |
390 | if (!pskb_may_pull(skb, grehlen)) | ||
391 | return; | 394 | return; |
392 | ipv6h = (const struct ipv6hdr *)skb->data; | 395 | ipv6h = (const struct ipv6hdr *)skb->data; |
393 | p = (__be16 *)(skb->data + offset); | 396 | greh = (const struct gre_base_hdr *)(skb->data + offset); |
397 | key = key_off ? *(__be32 *)(skb->data + key_off) : 0; | ||
394 | 398 | ||
395 | t = ip6gre_tunnel_lookup(skb->dev, &ipv6h->daddr, &ipv6h->saddr, | 399 | t = ip6gre_tunnel_lookup(skb->dev, &ipv6h->daddr, &ipv6h->saddr, |
396 | flags & GRE_KEY ? | 400 | key, greh->protocol); |
397 | *(((__be32 *)p) + (grehlen / 4) - 1) : 0, | ||
398 | p[1]); | ||
399 | if (!t) | 401 | if (!t) |
400 | return; | 402 | return; |
401 | 403 | ||