kernel: update kernel 4.1 to version 4.1.20
[openwrt.git] / target / linux / generic / patches-4.1 / 080-ipv6-ip6_fragment-fix-headroom-tests-and-skb-leak.patch
1 From: Florian Westphal <fw@strlen.de>
2 Date: Thu, 17 Sep 2015 11:24:48 +0100
3 Subject: [PATCH] ipv6: ip6_fragment: fix headroom tests and skb leak
4
5 David Woodhouse reports skb_under_panic when we try to push ethernet
6 header to fragmented ipv6 skbs:
7
8  skbuff: skb_under_panic: text:c1277f1e len:1294 put:14 head:dec98000
9  data:dec97ffc tail:0xdec9850a end:0xdec98f40 dev:br-lan
10 [..]
11 ip6_finish_output2+0x196/0x4da
12
13 David further debugged this:
14   [..] offending fragments were arriving here with skb_headroom(skb)==10.
15   Which is reasonable, being the Solos ADSL card's header of 8 bytes
16   followed by 2 bytes of PPP frame type.
17
18 The problem is that if netfilter ipv6 defragmentation is used, skb_cow()
19 in ip6_forward will only see reassembled skb.
20
21 Therefore, headroom is overestimated by 8 bytes (we pulled fragment
22 header) and we don't check the skbs in the frag_list either.
23
24 We can't do these checks in netfilter defrag since outdev isn't known yet.
25
26 Furthermore, existing tests in ip6_fragment did not consider the fragment
27 or ipv6 header size when checking headroom of the fraglist skbs.
28
29 While at it, also fix a skb leak on memory allocation -- ip6_fragment
30 must consume the skb.
31
32 I tested this e1000 driver hacked to not allocate additional headroom
33 (we end up in slowpath, since LL_RESERVED_SPACE is 16).
34
35 If 2 bytes of headroom are allocated, fastpath is taken (14 byte
36 ethernet header was pulled, so 16 byte headroom available in all
37 fragments).
38
39 Reported-by: David Woodhouse <dwmw2@infradead.org>
40 Diagnosed-by: David Woodhouse <dwmw2@infradead.org>
41 Signed-off-by: Florian Westphal <fw@strlen.de>
42 Closes 20532
43 ---
44
45 --- a/net/ipv6/ip6_output.c
46 +++ b/net/ipv6/ip6_output.c
47 @@ -587,20 +587,22 @@ int ip6_fragment(struct sock *sk, struct
48         }
49         mtu -= hlen + sizeof(struct frag_hdr);
50  
51 +       hroom = LL_RESERVED_SPACE(rt->dst.dev);
52         if (skb_has_frag_list(skb)) {
53                 int first_len = skb_pagelen(skb);
54                 struct sk_buff *frag2;
55  
56                 if (first_len - hlen > mtu ||
57                     ((first_len - hlen) & 7) ||
58 -                   skb_cloned(skb))
59 +                   skb_cloned(skb) ||
60 +                   skb_headroom(skb) < (hroom + sizeof(struct frag_hdr)))
61                         goto slow_path;
62  
63                 skb_walk_frags(skb, frag) {
64                         /* Correct geometry. */
65                         if (frag->len > mtu ||
66                             ((frag->len & 7) && frag->next) ||
67 -                           skb_headroom(frag) < hlen)
68 +                           skb_headroom(frag) < (hlen + hroom + sizeof(struct frag_hdr)))
69                                 goto slow_path_clean;
70  
71                         /* Partially cloned skb? */
72 @@ -617,8 +619,6 @@ int ip6_fragment(struct sock *sk, struct
73  
74                 err = 0;
75                 offset = 0;
76 -               frag = skb_shinfo(skb)->frag_list;
77 -               skb_frag_list_init(skb);
78                 /* BUILD HEADER */
79  
80                 *prevhdr = NEXTHDR_FRAGMENT;
81 @@ -626,8 +626,11 @@ int ip6_fragment(struct sock *sk, struct
82                 if (!tmp_hdr) {
83                         IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
84                                       IPSTATS_MIB_FRAGFAILS);
85 -                       return -ENOMEM;
86 +                       err = -ENOMEM;
87 +                       goto fail;
88                 }
89 +               frag = skb_shinfo(skb)->frag_list;
90 +               skb_frag_list_init(skb);
91  
92                 __skb_pull(skb, hlen);
93                 fh = (struct frag_hdr *)__skb_push(skb, sizeof(struct frag_hdr));
94 @@ -725,7 +728,6 @@ slow_path:
95          */
96  
97         *prevhdr = NEXTHDR_FRAGMENT;
98 -       hroom = LL_RESERVED_SPACE(rt->dst.dev);
99         troom = rt->dst.dev->needed_tailroom;
100  
101         /*