On Wed, Oct 21, 2026 at 08:31 AM Michael S. Tsirkin [off-list ref] wrote:
No unprivileged path is known to produce one - IP input resets the
transport header to the parsed position, local delivery pulls data
exactly to it, and tunnel decapsulation clears it altogether - but a tc
BPF program can leave one behind through bpf_skb_adjust_room().
As Eric said it should be fixed on bpf side.
It's a bug in bpf_skb_net_hdr_pop().
Something like this (compile tested only):
diff --git a/net/core/filter.c b/net/core/filter.c
index b7ac145..ec47f26 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -3417,7 +3417,6 @@ static int bpf_skb_net_hdr_push(struct sk_buff *skb, u32 off, u32 len)
static int bpf_skb_net_hdr_pop(struct sk_buff *skb, u32 off, u32 len)
{
- bool trans_same = skb->transport_header == skb->network_header;
int ret;
/* Same here, __skb_push()/__skb_pull() pair not needed. */@@ -3425,8 +3424,12 @@ static int bpf_skb_net_hdr_pop(struct sk_buff *skb, u32 off, u32 len)
if (likely(!ret)) {
skb->mac_header += len;
skb->network_header += len;
- if (trans_same)
+ /* It was the same as network header or it was popped. */
+ if (skb->transport_header < skb->network_header)
skb->transport_header = skb->network_header;
+ if (skb->encapsulation &&
+ skb->inner_transport_header < skb->network_header)
+ skb->inner_transport_header = skb->network_header;
}
return ret;