| From 0bd961d3bf9f4346ed4aa1a574d608a768bda63f Mon Sep 17 00:00:00 2001 |
| From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com> |
| Date: Sat, 6 Apr 2019 17:16:53 +0200 |
| Subject: net: ip6_gre: fix possible use-after-free in ip6erspan_rcv |
| |
| [ Upstream commit 2a3cabae4536edbcb21d344e7aa8be7a584d2afb ] |
| |
| erspan_v6 tunnels run __iptunnel_pull_header on received skbs to remove |
| erspan header. This can determine a possible use-after-free accessing |
| pkt_md pointer in ip6erspan_rcv since the packet will be 'uncloned' |
| running pskb_expand_head if it is a cloned gso skb (e.g if the packet has |
| been sent though a veth device). Fix it resetting pkt_md pointer after |
| __iptunnel_pull_header |
| |
| Fixes: 1d7e2ed22f8d ("net: erspan: refactor existing erspan code") |
| Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| net/ipv6/ip6_gre.c | 21 +++++++++++++-------- |
| 1 file changed, 13 insertions(+), 8 deletions(-) |
| |
| diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c |
| index 26f25b6e2833..438f1a5fd19a 100644 |
| --- a/net/ipv6/ip6_gre.c |
| +++ b/net/ipv6/ip6_gre.c |
| @@ -524,11 +524,10 @@ static int ip6gre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi) |
| return PACKET_REJECT; |
| } |
| |
| -static int ip6erspan_rcv(struct sk_buff *skb, int gre_hdr_len, |
| - struct tnl_ptk_info *tpi) |
| +static int ip6erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi, |
| + int gre_hdr_len) |
| { |
| struct erspan_base_hdr *ershdr; |
| - struct erspan_metadata *pkt_md; |
| const struct ipv6hdr *ipv6h; |
| struct erspan_md2 *md2; |
| struct ip6_tnl *tunnel; |
| @@ -547,18 +546,16 @@ static int ip6erspan_rcv(struct sk_buff *skb, int gre_hdr_len, |
| if (unlikely(!pskb_may_pull(skb, len))) |
| return PACKET_REJECT; |
| |
| - ershdr = (struct erspan_base_hdr *)skb->data; |
| - pkt_md = (struct erspan_metadata *)(ershdr + 1); |
| - |
| if (__iptunnel_pull_header(skb, len, |
| htons(ETH_P_TEB), |
| false, false) < 0) |
| return PACKET_REJECT; |
| |
| if (tunnel->parms.collect_md) { |
| + struct erspan_metadata *pkt_md, *md; |
| struct metadata_dst *tun_dst; |
| struct ip_tunnel_info *info; |
| - struct erspan_metadata *md; |
| + unsigned char *gh; |
| __be64 tun_id; |
| __be16 flags; |
| |
| @@ -571,6 +568,14 @@ static int ip6erspan_rcv(struct sk_buff *skb, int gre_hdr_len, |
| if (!tun_dst) |
| return PACKET_REJECT; |
| |
| + /* skb can be uncloned in __iptunnel_pull_header, so |
| + * old pkt_md is no longer valid and we need to reset |
| + * it |
| + */ |
| + gh = skb_network_header(skb) + |
| + skb_network_header_len(skb); |
| + pkt_md = (struct erspan_metadata *)(gh + gre_hdr_len + |
| + sizeof(*ershdr)); |
| info = &tun_dst->u.tun_info; |
| md = ip_tunnel_info_opts(info); |
| md->version = ver; |
| @@ -607,7 +612,7 @@ static int gre_rcv(struct sk_buff *skb) |
| |
| if (unlikely(tpi.proto == htons(ETH_P_ERSPAN) || |
| tpi.proto == htons(ETH_P_ERSPAN2))) { |
| - if (ip6erspan_rcv(skb, hdr_len, &tpi) == PACKET_RCVD) |
| + if (ip6erspan_rcv(skb, &tpi, hdr_len) == PACKET_RCVD) |
| return 0; |
| goto out; |
| } |
| -- |
| 2.19.1 |
| |