All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jiayuan Chen <jiayuan.chen@linux.dev>
To: netdev@vger.kernel.org
Cc: "Jiayuan Chen" <jiayuan.chen@linux.dev>,
	"Kenneth Lee" <kennethbwlee@snu.ac.kr>,
	"David S. Miller" <davem@davemloft.net>,
	"Eric Dumazet" <edumazet@google.com>,
	"Jakub Kicinski" <kuba@kernel.org>,
	"Paolo Abeni" <pabeni@redhat.com>,
	"Simon Horman" <horms@kernel.org>,
	"Alexei Starovoitov" <ast@kernel.org>,
	"Daniel Borkmann" <daniel@iogearbox.net>,
	"Jesper Dangaard Brouer" <hawk@kernel.org>,
	"John Fastabend" <john.fastabend@gmail.com>,
	"Stanislav Fomichev" <sdf@fomichev.me>,
	"Kuniyuki Iwashima" <kuniyu@google.com>,
	"Hangbin Liu" <liuhangbin@gmail.com>,
	"Krishna Kumar" <krikku@gmail.com>,
	"Martin Karsten" <mkarsten@uwaterloo.ca>,
	"Toke Høiland-Jørgensen" <toke@redhat.com>,
	"Martin Willi" <martin@strongswan.org>,
	linux-kernel@vger.kernel.org, bpf@vger.kernel.org
Subject: [PATCH net-next] net: xdp: don't assume an Ethernet header in generic XDP
Date: Thu, 13 Aug 2026 19:32:24 +0800	[thread overview]
Message-ID: <20260813113229.381136-1-jiayuan.chen@linux.dev> (raw)

Kenneth Lee reported a bug [1]:

  skb_vlan_push got skb with skb->data not at mac header (offset 14)
  WARNING: net/core/skbuff.c:6474 at skb_vlan_push+0x5ec/0x8a0
   ____bpf_skb_vlan_push net/core/filter.c:3239 [inline]
   bpf_skb_vlan_push+0x216/0x8b0 net/core/filter.c:3229
   bpf_prog_run_data_pointers+0x17c/0x240 include/linux/filter.h:917
   tcf_bpf_act+0x31c/0x940 net/sched/act_bpf.c:50
   tcf_action_exec+0x2c1/0x720 net/sched/act_api.c:1147
   tcf_classify+0x6e4/0x1080 net/sched/cls_api.c:1860
   tc_run+0x31c/0x5d0 net/core/dev.c:4411
   sch_handle_ingress net/core/dev.c:4486 [inline]
   __netif_receive_skb_core+0x141b/0x2ec0 net/core/dev.c:6054

bpf_prog_run_generic_xdp() just assumes xdp->data is an Ethernet header:

	eth = (struct ethhdr *)xdp->data;
	orig_host = ether_addr_equal_64bits(eth->h_dest, skb->dev->dev_addr);
	orig_bcast = is_multicast_ether_addr_64bits(eth->h_dest);
	orig_eth_type = eth->h_proto;

But an L3 device (ARPHRD_NONE, ARPHRD_TUNNEL, ...) has no L2 header at
all - its mac_len is 0 and xdp->data is really the L3 header, so those
orig_* values are garbage.

When that garbage comparison says the header changed, we then do:

	__skb_push(skb, ETH_HLEN);
	skb->pkt_type = PACKET_HOST;
	skb->protocol = eth_type_trans(skb, skb->dev);

eth_type_trans() resets mac_header and pulls ETH_HLEN back, so skb->data
does not move at all, but we now have mac_len == 0 while
skb_mac_header(skb) == skb->data - ETH_HLEN.

tc ingress relies on mac_len == skb->data - skb_mac_header(skb) to get
skb->data onto the mac header, so __skb_push(skb, skb->mac_len) pushes
nothing, the program runs with skb->data ETH_HLEN past the mac header,
and bpf_skb_vlan_push() hits the WARN_ONCE above.  skb->protocol also
ends up parsed from uninitialised headroom.

So skip the Ethernet part entirely when the skb has no ETH_HLEN sized L2
header.  The program still runs and its action is still returned.

Refusing to attach XDP to L3 devices instead would not help: mac_len is a
property of the skb, not of the netdev, so an ARPHRD_ETHER device can see
mac_len == 0 too.

[1]: https://lore.kernel.org/bpf/20260812043643.808295-1-kennethbwlee@snu.ac.kr/

Fixes: 22b6034323fd ("net, xdp: Update pkt_type if generic XDP changes unicast MAC")
Reported-by: Kenneth Lee <kennethbwlee@snu.ac.kr>
Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
---
 net/core/dev.c | 42 ++++++++++++++++++++++++++++--------------
 1 file changed, 28 insertions(+), 14 deletions(-)

diff --git a/net/core/dev.c b/net/core/dev.c
index b390c2edfb33..5c395460854f 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -5489,11 +5489,12 @@ u32 bpf_prog_run_generic_xdp(struct sk_buff *skb, struct xdp_buff *xdp,
 {
 	void *orig_data, *orig_data_end, *hard_start;
 	struct netdev_rx_queue *rxqueue;
-	bool orig_bcast, orig_host;
+	bool orig_bcast = false, orig_host = false;
+	__be16 orig_eth_type = 0;
 	u32 mac_len, frame_sz;
-	__be16 orig_eth_type;
 	struct ethhdr *eth;
 	u32 metalen, act;
+	bool has_eth;
 	int off;
 
 	/* The XDP program wants to see the packet starting at the MAC
@@ -5519,10 +5520,21 @@ u32 bpf_prog_run_generic_xdp(struct sk_buff *skb, struct xdp_buff *xdp,
 
 	orig_data_end = xdp->data_end;
 	orig_data = xdp->data;
-	eth = (struct ethhdr *)xdp->data;
-	orig_host = ether_addr_equal_64bits(eth->h_dest, skb->dev->dev_addr);
-	orig_bcast = is_multicast_ether_addr_64bits(eth->h_dest);
-	orig_eth_type = eth->h_proto;
+
+	/* xdp->data only points at an Ethernet header if this skb actually
+	 * carries one.  Devices with a different link layer (mac_len == 0
+	 * for ARPHRD_NONE/TUNNEL/RAWIP/PPP/..., IPOIB_ENCAP_LEN for IPoIB)
+	 * have nothing to inspect here, and must not have skb->mac_header
+	 * relocated by the ETH_HLEN fixup below.
+	 */
+	has_eth = mac_len == ETH_HLEN;
+	if (has_eth) {
+		eth = (struct ethhdr *)xdp->data;
+		orig_host = ether_addr_equal_64bits(eth->h_dest,
+						    skb->dev->dev_addr);
+		orig_bcast = is_multicast_ether_addr_64bits(eth->h_dest);
+		orig_eth_type = eth->h_proto;
+	}
 
 	act = bpf_prog_run_xdp(xdp_prog, xdp);
 
@@ -5554,14 +5566,16 @@ u32 bpf_prog_run_generic_xdp(struct sk_buff *skb, struct xdp_buff *xdp,
 		skb->data_len = 0;
 
 	/* check if XDP changed eth hdr such SKB needs update */
-	eth = (struct ethhdr *)xdp->data;
-	if ((orig_eth_type != eth->h_proto) ||
-	    (orig_host != ether_addr_equal_64bits(eth->h_dest,
-						  skb->dev->dev_addr)) ||
-	    (orig_bcast != is_multicast_ether_addr_64bits(eth->h_dest))) {
-		__skb_push(skb, ETH_HLEN);
-		skb->pkt_type = PACKET_HOST;
-		skb->protocol = eth_type_trans(skb, skb->dev);
+	if (has_eth) {
+		eth = (struct ethhdr *)xdp->data;
+		if ((orig_eth_type != eth->h_proto) ||
+		    (orig_host != ether_addr_equal_64bits(eth->h_dest,
+							  skb->dev->dev_addr)) ||
+		    (orig_bcast != is_multicast_ether_addr_64bits(eth->h_dest))) {
+			__skb_push(skb, ETH_HLEN);
+			skb->pkt_type = PACKET_HOST;
+			skb->protocol = eth_type_trans(skb, skb->dev);
+		}
 	}
 
 	/* Redirect/Tx gives L2 packet, code that will reuse skb must __skb_pull
-- 
2.43.0


                 reply	other threads:[~2026-08-13 11:32 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260813113229.381136-1-jiayuan.chen@linux.dev \
    --to=jiayuan.chen@linux.dev \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=hawk@kernel.org \
    --cc=horms@kernel.org \
    --cc=john.fastabend@gmail.com \
    --cc=kennethbwlee@snu.ac.kr \
    --cc=krikku@gmail.com \
    --cc=kuba@kernel.org \
    --cc=kuniyu@google.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=liuhangbin@gmail.com \
    --cc=martin@strongswan.org \
    --cc=mkarsten@uwaterloo.ca \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=sdf@fomichev.me \
    --cc=toke@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.