| From foo@baz Wed May 31 09:13:34 JST 2017 |
| From: Eric Dumazet <edumazet@google.com> |
| Date: Fri, 19 May 2017 14:17:48 -0700 |
| Subject: ipv6: fix out of bound writes in __ip6_append_data() |
| |
| From: Eric Dumazet <edumazet@google.com> |
| |
| |
| [ Upstream commit 232cd35d0804cc241eb887bb8d4d9b3b9881c64a ] |
| |
| Andrey Konovalov and idaifish@gmail.com reported crashes caused by |
| one skb shared_info being overwritten from __ip6_append_data() |
| |
| Andrey program lead to following state : |
| |
| copy -4200 datalen 2000 fraglen 2040 |
| maxfraglen 2040 alloclen 2048 transhdrlen 0 offset 0 fraggap 6200 |
| |
| The skb_copy_and_csum_bits(skb_prev, maxfraglen, data + transhdrlen, |
| fraggap, 0); is overwriting skb->head and skb_shared_info |
| |
| Since we apparently detect this rare condition too late, move the |
| code earlier to even avoid allocating skb and risking crashes. |
| |
| Once again, many thanks to Andrey and syzkaller team. |
| |
| Signed-off-by: Eric Dumazet <edumazet@google.com> |
| Reported-by: Andrey Konovalov <andreyknvl@google.com> |
| Tested-by: Andrey Konovalov <andreyknvl@google.com> |
| Reported-by: <idaifish@gmail.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/ipv6/ip6_output.c | 15 ++++++++------- |
| 1 file changed, 8 insertions(+), 7 deletions(-) |
| |
| --- a/net/ipv6/ip6_output.c |
| +++ b/net/ipv6/ip6_output.c |
| @@ -1447,6 +1447,11 @@ alloc_new_skb: |
| */ |
| alloclen += sizeof(struct frag_hdr); |
| |
| + copy = datalen - transhdrlen - fraggap; |
| + if (copy < 0) { |
| + err = -EINVAL; |
| + goto error; |
| + } |
| if (transhdrlen) { |
| skb = sock_alloc_send_skb(sk, |
| alloclen + hh_len, |
| @@ -1496,13 +1501,9 @@ alloc_new_skb: |
| data += fraggap; |
| pskb_trim_unique(skb_prev, maxfraglen); |
| } |
| - copy = datalen - transhdrlen - fraggap; |
| - |
| - if (copy < 0) { |
| - err = -EINVAL; |
| - kfree_skb(skb); |
| - goto error; |
| - } else if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) < 0) { |
| + if (copy > 0 && |
| + getfrag(from, data + transhdrlen, offset, |
| + copy, fraggap, skb) < 0) { |
| err = -EFAULT; |
| kfree_skb(skb); |
| goto error; |