]>
Commit | Line | Data |
---|---|---|
c1ebd610 GKH |
1 | From foo@baz Tue Feb 14 17:03:08 PST 2017 |
2 | From: Eric Dumazet <edumazet@google.com> | |
3 | Date: Mon, 23 Jan 2017 16:43:06 -0800 | |
4 | Subject: ipv6: fix ip6_tnl_parse_tlv_enc_lim() | |
5 | ||
6 | From: Eric Dumazet <edumazet@google.com> | |
7 | ||
8 | ||
9 | [ Upstream commit fbfa743a9d2a0ffa24251764f10afc13eb21e739 ] | |
10 | ||
11 | This function suffers from multiple issues. | |
12 | ||
13 | First one is that pskb_may_pull() may reallocate skb->head, | |
14 | so the 'raw' pointer needs either to be reloaded or not used at all. | |
15 | ||
16 | Second issue is that NEXTHDR_DEST handling does not validate | |
17 | that the options are present in skb->data, so we might read | |
18 | garbage or access non existent memory. | |
19 | ||
20 | With help from Willem de Bruijn. | |
21 | ||
22 | Signed-off-by: Eric Dumazet <edumazet@google.com> | |
23 | Reported-by: Dmitry Vyukov <dvyukov@google.com> | |
24 | Cc: Willem de Bruijn <willemb@google.com> | |
25 | Signed-off-by: David S. Miller <davem@davemloft.net> | |
26 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
27 | --- | |
28 | net/ipv6/ip6_tunnel.c | 34 ++++++++++++++++++++++------------ | |
29 | 1 file changed, 22 insertions(+), 12 deletions(-) | |
30 | ||
31 | --- a/net/ipv6/ip6_tunnel.c | |
32 | +++ b/net/ipv6/ip6_tunnel.c | |
33 | @@ -400,18 +400,19 @@ ip6_tnl_dev_uninit(struct net_device *de | |
34 | ||
35 | __u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw) | |
36 | { | |
37 | - const struct ipv6hdr *ipv6h = (const struct ipv6hdr *) raw; | |
38 | - __u8 nexthdr = ipv6h->nexthdr; | |
39 | - __u16 off = sizeof(*ipv6h); | |
40 | + const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)raw; | |
41 | + unsigned int nhoff = raw - skb->data; | |
42 | + unsigned int off = nhoff + sizeof(*ipv6h); | |
43 | + u8 next, nexthdr = ipv6h->nexthdr; | |
44 | ||
45 | while (ipv6_ext_hdr(nexthdr) && nexthdr != NEXTHDR_NONE) { | |
46 | - __u16 optlen = 0; | |
47 | struct ipv6_opt_hdr *hdr; | |
48 | - if (raw + off + sizeof(*hdr) > skb->data && | |
49 | - !pskb_may_pull(skb, raw - skb->data + off + sizeof (*hdr))) | |
50 | + u16 optlen; | |
51 | + | |
52 | + if (!pskb_may_pull(skb, off + sizeof(*hdr))) | |
53 | break; | |
54 | ||
55 | - hdr = (struct ipv6_opt_hdr *) (raw + off); | |
56 | + hdr = (struct ipv6_opt_hdr *)(skb->data + off); | |
57 | if (nexthdr == NEXTHDR_FRAGMENT) { | |
58 | struct frag_hdr *frag_hdr = (struct frag_hdr *) hdr; | |
59 | if (frag_hdr->frag_off) | |
60 | @@ -422,20 +423,29 @@ __u16 ip6_tnl_parse_tlv_enc_lim(struct s | |
61 | } else { | |
62 | optlen = ipv6_optlen(hdr); | |
63 | } | |
64 | + /* cache hdr->nexthdr, since pskb_may_pull() might | |
65 | + * invalidate hdr | |
66 | + */ | |
67 | + next = hdr->nexthdr; | |
68 | if (nexthdr == NEXTHDR_DEST) { | |
69 | - __u16 i = off + 2; | |
70 | + u16 i = 2; | |
71 | + | |
72 | + /* Remember : hdr is no longer valid at this point. */ | |
73 | + if (!pskb_may_pull(skb, off + optlen)) | |
74 | + break; | |
75 | + | |
76 | while (1) { | |
77 | struct ipv6_tlv_tnl_enc_lim *tel; | |
78 | ||
79 | /* No more room for encapsulation limit */ | |
80 | - if (i + sizeof (*tel) > off + optlen) | |
81 | + if (i + sizeof(*tel) > optlen) | |
82 | break; | |
83 | ||
84 | - tel = (struct ipv6_tlv_tnl_enc_lim *) &raw[i]; | |
85 | + tel = (struct ipv6_tlv_tnl_enc_lim *) skb->data + off + i; | |
86 | /* return index of option if found and valid */ | |
87 | if (tel->type == IPV6_TLV_TNL_ENCAP_LIMIT && | |
88 | tel->length == 1) | |
89 | - return i; | |
90 | + return i + off - nhoff; | |
91 | /* else jump to next option */ | |
92 | if (tel->type) | |
93 | i += tel->length + 2; | |
94 | @@ -443,7 +453,7 @@ __u16 ip6_tnl_parse_tlv_enc_lim(struct s | |
95 | i++; | |
96 | } | |
97 | } | |
98 | - nexthdr = hdr->nexthdr; | |
99 | + nexthdr = next; | |
100 | off += optlen; | |
101 | } | |
102 | return 0; |