summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2026-10-29 09:11:22 -0700
committerJakub Kicinski <kuba@kernel.org>2026-10-29 09:11:23 -0700
commitc9897252a66b43a17d04c19bc7303fd6539a56ba (patch)
tree36c9d235ac934e9112cb0279ef6be6917351386c /net
parentb7ff0e7a9ae3df2f123807a7e670bc348b1e1b9d (diff)
parent498c631f9c6d74aad4bf43039fa3ce70ae9a8bb7 (diff)
Merge branch 'net-fix-eth_type_trans-crashes-on-skbs-coming-from-the-stack'
Eric Dumazet says: ==================== net: fix eth_type_trans() crashes on skbs coming from the stack eth_type_trans() assumes ETH_HLEN bytes are present in skb->head. NIC drivers always provide this, and they should keep using the fast version, without an extra pskb_may_pull(). Devices looping skbs from their transmit path back to their receive path get skbs built by the stack instead. tc actions (act_vlan pop_eth, act_ife decode), BPF helpers (bpf_skb_change_head() before a redirect) or OVS actions (pop_eth followed by pop_mpls/pop_nsh) can leave fewer than ETH_HLEN bytes in the linear part, and eth_type_trans() then hits the BUG() in __skb_pull(). Instead of fixing every producer, or adding a check to all eth_type_trans() users, fix the few consumers exposed to this: - Patch 1 adds eth_type_trans_careful(), which pulls the Ethernet header first and returns a drop reason on failure. - Patch 2 uses it in loopback_xmit(). - Patch 3 uses it in __dev_forward_skb2(), used by BPF redirects to ingress and by veth, macvlan, ipvlan, netdevsim and l2tp_eth. - Patch 4 uses it in OVS internal_dev_recv(), covering both the MPLS (TEB) and NSH pop cases at once (only the MPLS case was tested). - Patch 5 (from Jamal) fixes act_ife decode on egress, which was consuming the inner Ethernet header. This is no longer a crash after patch 2, but egress decode never delivered a correct frame. Other eth_type_trans() users already pull the header or never see such skbs: - vrf_process_v{4,6}_outbound() already pull ETH_HLEN + IP header before vrf_local_xmit(). - tun already uses pskb_may_pull_reason(skb, ETH_HLEN). - XDP: bpf_xdp_adjust_head() and bpf_xdp_adjust_tail() keep at least ETH_HLEN bytes in the linear part, and multi-buffer tail shrinking only trims frags. Tested under virtme-ng with AF_PACKET reproducers. Without the series, all four cases crash in eth_type_trans(): - lo egress "action vlan pop_eth", eth/eth/8192 bytes frame - lo egress "action ife decode pipe", IFE frame with 8192 bytes payload - lo egress "action vlan pop_eth continue", then a BPF program doing bpf_skb_change_head(skb, 1, 0) + bpf_redirect(veth1, BPF_F_INGRESS) - OVS flow "pop_eth,pop_mpls(0x6558),0" to the internal port, eth/mpls/eth/4096 bytes frame With the series, no crash. An ICMP echo inside an IFE frame decoded on lo egress is now received and answered. Normal traffic on lo, veth (including 8000 byte pings) and vrf is unchanged. Link: https://lore.kernel.org/netdev/20261005052249.1914367-2-weir@nebusec.ai/ Link: https://lore.kernel.org/netdev/ACT-N6N4.v3.r0.20261023200137-4@mojatatu.com/ v1: https://lore.kernel.org/netdev/20261027082604.1485903-1-edumazet@kernel.org/ ==================== Link: https://patch.msgid.link/20261028122703.2093730-1-edumazet@kernel.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'net')
-rw-r--r--net/core/dev.c18
-rw-r--r--net/openvswitch/vport-internal_dev.c8
-rw-r--r--net/sched/act_ife.c4
3 files changed, 24 insertions, 6 deletions
diff --git a/net/core/dev.c b/net/core/dev.c
index 87a79d6004c8..3ac700d37e59 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2501,14 +2501,22 @@ EXPORT_SYMBOL_GPL(is_skb_forwardable);
static int __dev_forward_skb2(struct net_device *dev, struct sk_buff *skb,
bool check_mtu)
{
- int ret = ____dev_forward_skb(dev, skb, check_mtu);
+ enum skb_drop_reason reason;
+ int ret;
+
+ ret = ____dev_forward_skb(dev, skb, check_mtu);
+ if (unlikely(ret))
+ return ret;
- if (likely(!ret)) {
- skb->protocol = eth_type_trans(skb, dev);
- skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
+ reason = eth_type_trans_careful(skb, dev);
+ if (unlikely(reason)) {
+ dev_core_stats_rx_dropped_inc(dev);
+ kfree_skb_reason(skb, reason);
+ return NET_RX_DROP;
}
+ skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
- return ret;
+ return 0;
}
int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb)
diff --git a/net/openvswitch/vport-internal_dev.c b/net/openvswitch/vport-internal_dev.c
index 77a16d5ebd54..8f035087f0ee 100644
--- a/net/openvswitch/vport-internal_dev.c
+++ b/net/openvswitch/vport-internal_dev.c
@@ -188,6 +188,7 @@ static void internal_dev_destroy(struct vport *vport)
static int internal_dev_recv(struct sk_buff *skb)
{
struct net_device *netdev = skb->dev;
+ enum skb_drop_reason reason;
if (unlikely(!(netdev->flags & IFF_UP))) {
kfree_skb(skb);
@@ -199,7 +200,12 @@ static int internal_dev_recv(struct sk_buff *skb)
nf_reset_ct(skb);
skb->pkt_type = PACKET_HOST;
- skb->protocol = eth_type_trans(skb, netdev);
+ reason = eth_type_trans_careful(skb, netdev);
+ if (unlikely(reason)) {
+ dev_core_stats_rx_dropped_inc(netdev);
+ kfree_skb_reason(skb, reason);
+ return NETDEV_TX_OK;
+ }
skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
dev_sw_netstats_rx_add(netdev, skb->len);
diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c
index 2afd68983ece..3946a9589bb8 100644
--- a/net/sched/act_ife.c
+++ b/net/sched/act_ife.c
@@ -769,6 +769,10 @@ static int tcf_ife_decode(struct sk_buff *skb, const struct tc_action *a,
skb->protocol = eth_type_trans(skb, skb->dev);
skb_reset_network_header(skb);
+ if (!skb_at_tc_ingress(skb)) {
+ skb_push(skb, ETH_HLEN);
+ skb_reset_mac_len(skb);
+ }
return action;
}