Re: [PATCH net] ip6_gre: fix ip6gre_err() invalid reads

2017-02-05 Thread David Miller
From: Eric Dumazet 
Date: Sat, 04 Feb 2017 23:18:55 -0800

> From: Eric Dumazet 
> 
> 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 
> Reported-by: Andrey Konovalov 

So the bug is that we include offset twice in the calculation.

Applied and queued up for -stable, thanks.



[PATCH net] ip6_gre: fix ip6gre_err() invalid reads

2017-02-04 Thread Eric Dumazet
From: Eric Dumazet 

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 
Reported-by: Andrey Konovalov 
---
 net/ipv6/ip6_gre.c |   40 +---
 1 file changed, 21 insertions(+), 19 deletions(-)

diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 
558631860d91bbcb1321a4b566b6e92ddcaf7163..630b73be599977599c0021849fc6eb689cfefad7
 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)
 
 
 static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
-   u8 type, u8 code, int offset, __be32 info)
+  u8 type, u8 code, int offset, __be32 info)
 {
-   const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)skb->data;
-   __be16 *p = (__be16 *)(skb->data + offset);
-   int grehlen = offset + 4;
+   const struct gre_base_hdr *greh;
+   const struct ipv6hdr *ipv6h;
+   int grehlen = sizeof(*greh);
struct ip6_tnl *t;
+   int key_off = 0;
__be16 flags;
+   __be32 key;
 
-   flags = p[0];
-   if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) {
-   if (flags&(GRE_VERSION|GRE_ROUTING))
-   return;
-   if (flags_KEY) {
-   grehlen += 4;
-   if (flags_CSUM)
-   grehlen += 4;
-   }
+   if (!pskb_may_pull(skb, offset + grehlen))
+   return;
+   greh = (const struct gre_base_hdr *)(skb->data + offset);
+   flags = greh->flags;
+   if (flags & (GRE_VERSION | GRE_ROUTING))
+   return;
+   if (flags & GRE_CSUM)
+   grehlen += 4;
+   if (flags & GRE_KEY) {
+   key_off = grehlen + offset;
+   grehlen += 4;
}
 
-   /* If only 8 bytes returned, keyed message will be dropped here */
-   if (!pskb_may_pull(skb, grehlen))
+   if (!pskb_may_pull(skb, offset + grehlen))
return;
ipv6h = (const struct ipv6hdr *)skb->data;
-   p = (__be16 *)(skb->data + offset);
+   greh = (const struct gre_base_hdr *)(skb->data + offset);
+   key = key_off ? *(__be32 *)(skb->data + key_off) : 0;
 
t = ip6gre_tunnel_lookup(skb->dev, >daddr, >saddr,
-   flags & GRE_KEY ?
-   *(((__be32 *)p) + (grehlen / 4) - 1) : 0,
-   p[1]);
+key, greh->protocol);
if (!t)
return;