| From 4d9e6204e082a9002e072089d151a985551af0ec Mon Sep 17 00:00:00 2001 |
| From: Eric Dumazet <edumazet@google.com> |
| Date: Thu, 7 Feb 2019 12:27:38 -0800 |
| Subject: vxlan: test dev->flags & IFF_UP before calling netif_rx() |
| |
| [ Upstream commit 4179cb5a4c924cd233eaadd081882425bc98f44e ] |
| |
| netif_rx() must be called under a strict contract. |
| |
| At device dismantle phase, core networking clears IFF_UP |
| and flush_all_backlogs() is called after rcu grace period |
| to make sure no incoming packet might be in a cpu backlog |
| and still referencing the device. |
| |
| Most drivers call netif_rx() from their interrupt handler, |
| and since the interrupts are disabled at device dismantle, |
| netif_rx() does not have to check dev->flags & IFF_UP |
| |
| Virtual drivers do not have this guarantee, and must |
| therefore make the check themselves. |
| |
| Otherwise we risk use-after-free and/or crashes. |
| |
| Note this patch also fixes a small issue that came |
| with commit ce6502a8f957 ("vxlan: fix a use after free |
| in vxlan_encap_bypass"), since the dev->stats.rx_dropped |
| change was done on the wrong device. |
| |
| Fixes: d342894c5d2f ("vxlan: virtual extensible lan") |
| Fixes: ce6502a8f957 ("vxlan: fix a use after free in vxlan_encap_bypass") |
| Signed-off-by: Eric Dumazet <edumazet@google.com> |
| Cc: Petr Machata <petrm@mellanox.com> |
| Cc: Ido Schimmel <idosch@mellanox.com> |
| Cc: Roopa Prabhu <roopa@cumulusnetworks.com> |
| Cc: Stefano Brivio <sbrivio@redhat.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/net/vxlan.c | 14 +++++++++++--- |
| 1 file changed, 11 insertions(+), 3 deletions(-) |
| |
| diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c |
| index 13d39a72fe0d0..a1b40b9c4906e 100644 |
| --- a/drivers/net/vxlan.c |
| +++ b/drivers/net/vxlan.c |
| @@ -2002,7 +2002,7 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, |
| struct pcpu_sw_netstats *tx_stats, *rx_stats; |
| union vxlan_addr loopback; |
| union vxlan_addr *remote_ip = &dst_vxlan->default_dst.remote_ip; |
| - struct net_device *dev = skb->dev; |
| + struct net_device *dev; |
| int len = skb->len; |
| |
| tx_stats = this_cpu_ptr(src_vxlan->dev->tstats); |
| @@ -2022,9 +2022,15 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, |
| #endif |
| } |
| |
| + rcu_read_lock(); |
| + dev = skb->dev; |
| + if (unlikely(!(dev->flags & IFF_UP))) { |
| + kfree_skb(skb); |
| + goto drop; |
| + } |
| + |
| if (dst_vxlan->cfg.flags & VXLAN_F_LEARN) |
| - vxlan_snoop(skb->dev, &loopback, eth_hdr(skb)->h_source, 0, |
| - vni); |
| + vxlan_snoop(dev, &loopback, eth_hdr(skb)->h_source, 0, vni); |
| |
| u64_stats_update_begin(&tx_stats->syncp); |
| tx_stats->tx_packets++; |
| @@ -2037,8 +2043,10 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, |
| rx_stats->rx_bytes += len; |
| u64_stats_update_end(&rx_stats->syncp); |
| } else { |
| +drop: |
| dev->stats.rx_dropped++; |
| } |
| + rcu_read_unlock(); |
| } |
| |
| static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev, |
| -- |
| 2.19.1 |
| |