From a9d08fe60fa728ce0e3a99085241e8e790fb14c6 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Sun, 1 Nov 2026 19:05:33 +0000 Subject: [PATCH] bpf: Move the transport header with the moved headers bpf_skb_net_hdr_pop() removes len bytes at offset off. bpf_skb_net_hdr_push() does the opposite. skb->data off before | A | removed | B | after | A | B | skb->data off mac_header and network_header move with A. transport_header moves only when it is equal to network_header. inner_transport_header never moves. This is correct only for a header in B. In bpf_lwt_seg6_adjust_srh() transport_header is in A. bpf_skb_adjust_room() can remove both transport headers. With an incorrect header offset, the stack can drop the skb or read outside of it. Also move transport_header with A when it is in A. Set it to offset off when it is in the removed bytes. In BPF_ADJ_ROOM_MAC mode it is then equal to network_header. Do not unset it, because skb_gso_validate_mac_len() uses it without a check. After a removal, set inner_transport_header to off when it is not in B and skb->encapsulation is set. A has only outer headers. Fixes: fe94cc290f53 ("bpf: Add IPv6 Segment Routing helpers") Fixes: 868d523535c2 ("bpf: add bpf_skb_adjust_room encap flags") Signed-off-by: Chris Mason Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261101190535.1454146-26-alexei.starovoitov@gmail.com --- net/core/filter.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/net/core/filter.c b/net/core/filter.c index 08168ea381c2..b5534dac9e6d 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3402,9 +3402,25 @@ static int bpf_skb_generic_pop(struct sk_buff *skb, u32 off, u32 len) return 0; } +/* + * bpf_skb_net_hdr_push() inserts bytes at @off, bpf_skb_net_hdr_pop() removes + * bytes at @off. Both move the bytes in front of @off together with skb->data, + * and leave the rest of the packet where it is. Return true if the transport + * header is in the part that moves. A transport header that is equal to the + * network header stays equal to it. + */ +static bool bpf_skb_trans_hdr_moves(const struct sk_buff *skb, u32 off) +{ + if (skb->transport_header == skb->network_header) + return true; + + return skb_transport_header_was_set(skb) && + (u32)skb_transport_offset(skb) < off; +} + static int bpf_skb_net_hdr_push(struct sk_buff *skb, u32 off, u32 len) { - bool trans_same = skb->transport_header == skb->network_header; + bool trans_moves = bpf_skb_trans_hdr_moves(skb, off); int ret; /* There's no need for __skb_push()/__skb_pull() pair to @@ -3415,8 +3431,8 @@ static int bpf_skb_net_hdr_push(struct sk_buff *skb, u32 off, u32 len) if (likely(!ret)) { skb->mac_header -= len; skb->network_header -= len; - if (trans_same) - skb->transport_header = skb->network_header; + if (trans_moves) + skb->transport_header -= len; } return ret; @@ -3424,7 +3440,7 @@ 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; + bool trans_moves = bpf_skb_trans_hdr_moves(skb, off); int ret; /* Same here, __skb_push()/__skb_pull() pair not needed. */ @@ -3432,8 +3448,17 @@ 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) - skb->transport_header = skb->network_header; + if (trans_moves) { + skb->transport_header += len; + } else if (skb_transport_header_was_set(skb) && + skb_transport_offset(skb) < (int)off) { + /* It was in the removed bytes or before skb->data. */ + skb_set_transport_header(skb, off); + } + /* Do not leave the inner transport header in front of @off. */ + if (skb->encapsulation && + skb_inner_transport_offset(skb) < (int)off) + skb_set_inner_transport_header(skb, off); } return ret;