diff options
| author | Eric Dumazet <edumazet@google.com> | 2019-02-07 15:27:38 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2019-02-11 15:39:51 -0500 |
| commit | 4179cb5a4c924cd233eaadd081882425bc98f44e (patch) | |
| tree | 83dc255e437ace38c17db611fd8052cb8db1dfd8 | |
| parent | 989723b00b7fecad9acad55b151c73ccf2eac3ca (diff) | |
vxlan: test dev->flags & IFF_UP before calling netif_rx()
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>
| -rw-r--r-- | drivers/net/vxlan.c | 14 |
1 files changed, 11 insertions, 3 deletions
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 5209ee9aac47..2aae11feff0c 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c | |||
| @@ -2219,7 +2219,7 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, | |||
| 2219 | struct pcpu_sw_netstats *tx_stats, *rx_stats; | 2219 | struct pcpu_sw_netstats *tx_stats, *rx_stats; |
| 2220 | union vxlan_addr loopback; | 2220 | union vxlan_addr loopback; |
| 2221 | union vxlan_addr *remote_ip = &dst_vxlan->default_dst.remote_ip; | 2221 | union vxlan_addr *remote_ip = &dst_vxlan->default_dst.remote_ip; |
| 2222 | struct net_device *dev = skb->dev; | 2222 | struct net_device *dev; |
| 2223 | int len = skb->len; | 2223 | int len = skb->len; |
| 2224 | 2224 | ||
| 2225 | tx_stats = this_cpu_ptr(src_vxlan->dev->tstats); | 2225 | tx_stats = this_cpu_ptr(src_vxlan->dev->tstats); |
| @@ -2239,9 +2239,15 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, | |||
| 2239 | #endif | 2239 | #endif |
| 2240 | } | 2240 | } |
| 2241 | 2241 | ||
| 2242 | rcu_read_lock(); | ||
| 2243 | dev = skb->dev; | ||
| 2244 | if (unlikely(!(dev->flags & IFF_UP))) { | ||
| 2245 | kfree_skb(skb); | ||
| 2246 | goto drop; | ||
| 2247 | } | ||
| 2248 | |||
| 2242 | if (dst_vxlan->cfg.flags & VXLAN_F_LEARN) | 2249 | if (dst_vxlan->cfg.flags & VXLAN_F_LEARN) |
| 2243 | vxlan_snoop(skb->dev, &loopback, eth_hdr(skb)->h_source, 0, | 2250 | vxlan_snoop(dev, &loopback, eth_hdr(skb)->h_source, 0, vni); |
| 2244 | vni); | ||
| 2245 | 2251 | ||
| 2246 | u64_stats_update_begin(&tx_stats->syncp); | 2252 | u64_stats_update_begin(&tx_stats->syncp); |
| 2247 | tx_stats->tx_packets++; | 2253 | tx_stats->tx_packets++; |
| @@ -2254,8 +2260,10 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan, | |||
| 2254 | rx_stats->rx_bytes += len; | 2260 | rx_stats->rx_bytes += len; |
| 2255 | u64_stats_update_end(&rx_stats->syncp); | 2261 | u64_stats_update_end(&rx_stats->syncp); |
| 2256 | } else { | 2262 | } else { |
| 2263 | drop: | ||
| 2257 | dev->stats.rx_dropped++; | 2264 | dev->stats.rx_dropped++; |
| 2258 | } | 2265 | } |
| 2266 | rcu_read_unlock(); | ||
| 2259 | } | 2267 | } |
| 2260 | 2268 | ||
| 2261 | static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev, | 2269 | static int encap_bypass_if_local(struct sk_buff *skb, struct net_device *dev, |
