| From bf8a9848c2a01e86b99c72a0a490eda1ece7ab06 Mon Sep 17 00:00:00 2001 |
| From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com> |
| Date: Sat, 6 Apr 2019 17:16:52 +0200 |
| Subject: net: ip_gre: fix possible use-after-free in erspan_rcv |
| |
| [ Upstream commit 492b67e28ee5f2a2522fb72e3d3bcb990e461514 ] |
| |
| erspan tunnels run __iptunnel_pull_header on received skbs to remove |
| gre and erspan headers. This can determine a possible use-after-free |
| accessing pkt_md pointer in erspan_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/ipv4/ip_gre.c | 15 ++++++++++----- |
| 1 file changed, 10 insertions(+), 5 deletions(-) |
| |
| diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c |
| index 6ae89f2b541b..2d5734079e6b 100644 |
| --- a/net/ipv4/ip_gre.c |
| +++ b/net/ipv4/ip_gre.c |
| @@ -259,7 +259,6 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi, |
| struct net *net = dev_net(skb->dev); |
| struct metadata_dst *tun_dst = NULL; |
| struct erspan_base_hdr *ershdr; |
| - struct erspan_metadata *pkt_md; |
| struct ip_tunnel_net *itn; |
| struct ip_tunnel *tunnel; |
| const struct iphdr *iph; |
| @@ -282,9 +281,6 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi, |
| if (unlikely(!pskb_may_pull(skb, len))) |
| return PACKET_REJECT; |
| |
| - ershdr = (struct erspan_base_hdr *)(skb->data + gre_hdr_len); |
| - pkt_md = (struct erspan_metadata *)(ershdr + 1); |
| - |
| if (__iptunnel_pull_header(skb, |
| len, |
| htons(ETH_P_TEB), |
| @@ -292,8 +288,9 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi, |
| goto drop; |
| |
| if (tunnel->collect_md) { |
| + struct erspan_metadata *pkt_md, *md; |
| struct ip_tunnel_info *info; |
| - struct erspan_metadata *md; |
| + unsigned char *gh; |
| __be64 tun_id; |
| __be16 flags; |
| |
| @@ -306,6 +303,14 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi, |
| 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)); |
| md = ip_tunnel_info_opts(&tun_dst->u.tun_info); |
| md->version = ver; |
| md2 = &md->u.md2; |
| -- |
| 2.19.1 |
| |