| From fbfa743a9d2a0ffa24251764f10afc13eb21e739 Mon Sep 17 00:00:00 2001 |
| From: Eric Dumazet <edumazet@google.com> |
| Date: Mon, 23 Jan 2017 16:43:06 -0800 |
| Subject: [PATCH] ipv6: fix ip6_tnl_parse_tlv_enc_lim() |
| |
| commit fbfa743a9d2a0ffa24251764f10afc13eb21e739 upstream. |
| |
| This function suffers from multiple issues. |
| |
| First one is that pskb_may_pull() may reallocate skb->head, |
| so the 'raw' pointer needs either to be reloaded or not used at all. |
| |
| Second issue is that NEXTHDR_DEST handling does not validate |
| that the options are present in skb->data, so we might read |
| garbage or access non existent memory. |
| |
| With help from Willem de Bruijn. |
| |
| Signed-off-by: Eric Dumazet <edumazet@google.com> |
| Reported-by: Dmitry Vyukov <dvyukov@google.com> |
| Cc: Willem de Bruijn <willemb@google.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| |
| diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c |
| index 02923f956ac8..ff8ee06491c3 100644 |
| --- a/net/ipv6/ip6_tunnel.c |
| +++ b/net/ipv6/ip6_tunnel.c |
| @@ -400,18 +400,19 @@ ip6_tnl_dev_uninit(struct net_device *dev) |
| |
| __u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw) |
| { |
| - const struct ipv6hdr *ipv6h = (const struct ipv6hdr *) raw; |
| - __u8 nexthdr = ipv6h->nexthdr; |
| - __u16 off = sizeof(*ipv6h); |
| + const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)raw; |
| + unsigned int nhoff = raw - skb->data; |
| + unsigned int off = nhoff + sizeof(*ipv6h); |
| + u8 next, nexthdr = ipv6h->nexthdr; |
| |
| while (ipv6_ext_hdr(nexthdr) && nexthdr != NEXTHDR_NONE) { |
| - __u16 optlen = 0; |
| struct ipv6_opt_hdr *hdr; |
| - if (raw + off + sizeof(*hdr) > skb->data && |
| - !pskb_may_pull(skb, raw - skb->data + off + sizeof (*hdr))) |
| + u16 optlen; |
| + |
| + if (!pskb_may_pull(skb, off + sizeof(*hdr))) |
| break; |
| |
| - hdr = (struct ipv6_opt_hdr *) (raw + off); |
| + hdr = (struct ipv6_opt_hdr *)(skb->data + off); |
| if (nexthdr == NEXTHDR_FRAGMENT) { |
| struct frag_hdr *frag_hdr = (struct frag_hdr *) hdr; |
| if (frag_hdr->frag_off) |
| @@ -422,20 +423,29 @@ __u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw) |
| } else { |
| optlen = ipv6_optlen(hdr); |
| } |
| + /* cache hdr->nexthdr, since pskb_may_pull() might |
| + * invalidate hdr |
| + */ |
| + next = hdr->nexthdr; |
| if (nexthdr == NEXTHDR_DEST) { |
| - __u16 i = off + 2; |
| + u16 i = 2; |
| + |
| + /* Remember : hdr is no longer valid at this point. */ |
| + if (!pskb_may_pull(skb, off + optlen)) |
| + break; |
| + |
| while (1) { |
| struct ipv6_tlv_tnl_enc_lim *tel; |
| |
| /* No more room for encapsulation limit */ |
| - if (i + sizeof (*tel) > off + optlen) |
| + if (i + sizeof(*tel) > optlen) |
| break; |
| |
| - tel = (struct ipv6_tlv_tnl_enc_lim *) &raw[i]; |
| + tel = (struct ipv6_tlv_tnl_enc_lim *) skb->data + off + i; |
| /* return index of option if found and valid */ |
| if (tel->type == IPV6_TLV_TNL_ENCAP_LIMIT && |
| tel->length == 1) |
| - return i; |
| + return i + off - nhoff; |
| /* else jump to next option */ |
| if (tel->type) |
| i += tel->length + 2; |
| @@ -443,7 +453,7 @@ __u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw) |
| i++; |
| } |
| } |
| - nexthdr = hdr->nexthdr; |
| + nexthdr = next; |
| off += optlen; |
| } |
| return 0; |
| -- |
| 2.12.0 |
| |