]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
vxlan: use neigh_ha_snapshot() in route_shortcircuit()
authorEric Dumazet <edumazet@google.com>
Thu, 23 Jul 2026 14:42:47 +0000 (14:42 +0000)
committerJakub Kicinski <kuba@kernel.org>
Mon, 27 Jul 2026 22:15:13 +0000 (15:15 -0700)
The neighbour hardware address n->ha can be updated asynchronously by the
neighbour subsystem, protected by n->ha_lock seqlock. Reading n->ha without
holding the seqlock loop can lead to torn reads or reading a partially updated
MAC address.

Use neigh_ha_snapshot() in route_shortcircuit() to safely copy n->ha under
read_seqbegin()/read_seqretry() lock protection before using it.

Note that arp_reduce() and neigh_reduce() seem to have the same issue
left for future patches.

Fixes: e4f67addf158 ("add DOVE extensions for VXLAN")
Cc: stable@vger.kernel.org
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
Link: https://patch.msgid.link/20260723144249.759100-4-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/vxlan/vxlan_core.c

index e831fe2034427a8c2c6539c484f42c0592d1e272..be3c2bc2cd9accec8d37194141fa31f60392c039 100644 (file)
@@ -2159,9 +2159,11 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
        }
 
        if (n) {
+               u8 haddr[ETH_ALEN];
                bool diff;
 
-               diff = !ether_addr_equal(eth_hdr(skb)->h_dest, n->ha);
+               neigh_ha_snapshot(haddr, n, dev);
+               diff = !ether_addr_equal_unaligned(eth_hdr(skb)->h_dest, haddr);
                if (diff) {
                        if (skb_cow_head(skb, 0)) {
                                neigh_release(n);
@@ -2169,7 +2171,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
                        }
                        memcpy(eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest,
                                dev->addr_len);
-                       memcpy(eth_hdr(skb)->h_dest, n->ha, dev->addr_len);
+                       memcpy(eth_hdr(skb)->h_dest, haddr, dev->addr_len);
                }
                neigh_release(n);
                return diff;