| From 5b59843e1b2d043667748e6fea8edb7302548a63 Mon Sep 17 00:00:00 2001 |
| From: Junwei Hu <hujunwei4@huawei.com> |
| Date: Tue, 2 Apr 2019 19:38:04 +0800 |
| Subject: ipv6: Fix dangling pointer when ipv6 fragment |
| |
| [ Upstream commit ef0efcd3bd3fd0589732b67fb586ffd3c8705806 ] |
| |
| At the beginning of ip6_fragment func, the prevhdr pointer is |
| obtained in the ip6_find_1stfragopt func. |
| However, all the pointers pointing into skb header may change |
| when calling skb_checksum_help func with |
| skb->ip_summed = CHECKSUM_PARTIAL condition. |
| The prevhdr pointe will be dangling if it is not reloaded after |
| calling __skb_linearize func in skb_checksum_help func. |
| |
| Here, I add a variable, nexthdr_offset, to evaluate the offset, |
| which does not changes even after calling __skb_linearize func. |
| |
| Fixes: 405c92f7a541 ("ipv6: add defensive check for CHECKSUM_PARTIAL skbs in ip_fragment") |
| Signed-off-by: Junwei Hu <hujunwei4@huawei.com> |
| Reported-by: Wenhao Zhang <zhangwenhao8@huawei.com> |
| Reported-by: syzbot+e8ce541d095e486074fc@syzkaller.appspotmail.com |
| Reviewed-by: Zhiqiang Liu <liuzhiqiang26@huawei.com> |
| Acked-by: Martin KaFai Lau <kafai@fb.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| net/ipv6/ip6_output.c | 4 +++- |
| 1 file changed, 3 insertions(+), 1 deletion(-) |
| |
| diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c |
| index 5f9fa0302b5a..e71227390bec 100644 |
| --- a/net/ipv6/ip6_output.c |
| +++ b/net/ipv6/ip6_output.c |
| @@ -595,7 +595,7 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, |
| inet6_sk(skb->sk) : NULL; |
| struct ipv6hdr *tmp_hdr; |
| struct frag_hdr *fh; |
| - unsigned int mtu, hlen, left, len; |
| + unsigned int mtu, hlen, left, len, nexthdr_offset; |
| int hroom, troom; |
| __be32 frag_id; |
| int ptr, offset = 0, err = 0; |
| @@ -606,6 +606,7 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, |
| goto fail; |
| hlen = err; |
| nexthdr = *prevhdr; |
| + nexthdr_offset = prevhdr - skb_network_header(skb); |
| |
| mtu = ip6_skb_dst_mtu(skb); |
| |
| @@ -640,6 +641,7 @@ int ip6_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, |
| (err = skb_checksum_help(skb))) |
| goto fail; |
| |
| + prevhdr = skb_network_header(skb) + nexthdr_offset; |
| hroom = LL_RESERVED_SPACE(rt->dst.dev); |
| if (skb_has_frag_list(skb)) { |
| unsigned int first_len = skb_pagelen(skb); |
| -- |
| 2.19.1 |
| |