diff options
| author | Eric Dumazet <edumazet@google.com> | 2017-02-05 02:18:55 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2017-02-05 17:23:04 -0500 |
| commit | 7892032cfe67f4bde6fc2ee967e45a8fbaf33756 (patch) | |
| tree | cbadc1cb0484fbdcd3d5d68d5189c1d022d0d0d4 /net/ipv6/ip6_gre.c | |
| parent | d71b7896886345c53ef1d84bda2bc758554f5d61 (diff) | |
ip6_gre: fix ip6gre_err() invalid reads
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>
Diffstat (limited to 'net/ipv6/ip6_gre.c')
| -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 558631860d91..630b73be5999 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 | ||
