mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-29 19:48:36 -04:00
Merge branch 'vxlan-fixes-for-skb-header-pulling-cloning-and-concurrency-in-tx-path'
Eric Dumazet says:
====================
vxlan: fixes for skb header pulling, cloning, and concurrency in TX path
While working on RTNL-less fill_info for vxlan, Sashiko found annoying
pre-existing issues, adding noise to an already complex work.
This series addresses some of them in VXLAN transmit path,
primarily within route_shortcircuit(), header validation, and neighbour
lookup.
Patch 1 fixes a potential use-after-free in vxlan_xmit() caused by caching
the Ethernet header pointer ('eth') before calling route_shortcircuit(),
which can reallocate skb->head via pskb_may_pull().
Patch 2 calls skb_cow_head() in route_shortcircuit() before modifying the
Ethernet header in-place, preventing packet header corruption when the skb
is cloned (e.g., by packet sockets, tcpdump, or dev_queue_xmit).
Patch 3 replaces direct reads of n->ha in route_shortcircuit() with
neigh_ha_snapshot() to safely snapshot the neighbour hardware address
under seqlock protection, avoiding potential torn reads during
asynchronous updates.
Patch 4 changes route_shortcircuit() to use pskb_network_may_pull() instead
of pskb_may_pull(). Since skb->data points to the MAC header on transmit
(skb_network_offset(skb) == ETH_HLEN), pskb_may_pull() was only checking
6 bytes into the IP header, leaving the remainder un-pulled in non-linear
frags.
Patch 5 applies pskb_network_may_pull() to the remaining transmit-path
header pull checks in arp_reduce(), ND solicitation proxy checks, and
MDB entry lookup, where skb->data similarly points to the Ethernet header.
====================
Link: https://patch.msgid.link/20260723144249.759100-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -1850,7 +1850,7 @@ static int arp_reduce(struct net_device *dev, struct sk_buff *skb, __be32 vni)
|
||||
if (dev->flags & IFF_NOARP)
|
||||
goto out;
|
||||
|
||||
if (!pskb_may_pull(skb, arp_hdr_len(dev))) {
|
||||
if (!pskb_network_may_pull(skb, arp_hdr_len(dev))) {
|
||||
dev_dstats_tx_dropped(dev);
|
||||
vxlan_vnifilter_count(vxlan, vni, NULL,
|
||||
VXLAN_VNI_STATS_TX_DROPS, 0);
|
||||
@@ -2111,7 +2111,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
|
||||
{
|
||||
struct iphdr *pip;
|
||||
|
||||
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
|
||||
if (!pskb_network_may_pull(skb, sizeof(struct iphdr)))
|
||||
return false;
|
||||
pip = ip_hdr(skb);
|
||||
n = neigh_lookup(&arp_tbl, &pip->daddr, dev);
|
||||
@@ -2137,7 +2137,7 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
|
||||
*/
|
||||
if (!ipv6_mod_enabled())
|
||||
return false;
|
||||
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
|
||||
if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr)))
|
||||
return false;
|
||||
pip6 = ipv6_hdr(skb);
|
||||
n = neigh_lookup(&nd_tbl, &pip6->daddr, dev);
|
||||
@@ -2159,13 +2159,19 @@ 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);
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
@@ -2757,8 +2763,8 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
return arp_reduce(dev, skb, vni);
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
else if (ntohs(eth->h_proto) == ETH_P_IPV6 &&
|
||||
pskb_may_pull(skb, sizeof(struct ipv6hdr) +
|
||||
sizeof(struct nd_msg)) &&
|
||||
pskb_network_may_pull(skb, sizeof(struct ipv6hdr) +
|
||||
sizeof(struct nd_msg)) &&
|
||||
ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) {
|
||||
struct nd_msg *m = (struct nd_msg *)(ipv6_hdr(skb) + 1);
|
||||
|
||||
@@ -2796,6 +2802,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
(ntohs(eth->h_proto) == ETH_P_IP ||
|
||||
ntohs(eth->h_proto) == ETH_P_IPV6)) {
|
||||
did_rsc = route_shortcircuit(dev, skb);
|
||||
eth = eth_hdr(skb);
|
||||
if (did_rsc)
|
||||
f = vxlan_find_mac_tx(vxlan, eth->h_dest, vni);
|
||||
}
|
||||
|
||||
@@ -1631,7 +1631,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,
|
||||
|
||||
switch (skb->protocol) {
|
||||
case htons(ETH_P_IP):
|
||||
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
|
||||
if (!pskb_network_may_pull(skb, sizeof(struct iphdr)))
|
||||
return NULL;
|
||||
group.dst.sa.sa_family = AF_INET;
|
||||
group.dst.sin.sin_addr.s_addr = ip_hdr(skb)->daddr;
|
||||
@@ -1640,7 +1640,7 @@ struct vxlan_mdb_entry *vxlan_mdb_entry_skb_get(struct vxlan_dev *vxlan,
|
||||
break;
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
case htons(ETH_P_IPV6):
|
||||
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
|
||||
if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr)))
|
||||
return NULL;
|
||||
group.dst.sa.sa_family = AF_INET6;
|
||||
group.dst.sin6.sin6_addr = ipv6_hdr(skb)->daddr;
|
||||
|
||||
Reference in New Issue
Block a user