From: Jun Yang ipv6_srh_rcv() runs with skb->data at the Segment Routing Header while skb_network_header() points at the fixed IPv6 header. It restores the data position by pushing sizeof(struct ipv6hdr) before dst_input(), which assumes the SRH immediately follows the IPv6 header. If another extension header precedes the SRH, skb_network_offset() remains negative and neighbour output passes it to __skb_pull() as an unsigned length. Keep skb->data at the current extension header while rerouting and restore it by the exact network offset before each dst_input() handoff. Remove the fixed pull from the loopback path. This matches ipv6_rthdr_rcv() and handles any valid preceding extension header. The trace was captured with kernel release 7.2.0-rc2-00023-g714b725feef4-dirty: BUG: KASAN: use-after-free in eth_header (net/ethernet/eth.c:86) Write of size 2 at addr ffff8881247b9a3e by task ipv6_srh_poc/9336 Call Trace: eth_header (net/ethernet/eth.c:86) neigh_resolve_output (net/core/neighbour.c:1611) ip6_finish_output2 (net/ipv6/ip6_output.c:136) ip6_forward (net/ipv6/ip6_output.c:690) ipv6_rthdr_rcv (net/ipv6/exthdrs.c:832) Fixes: 1ababeba4a21 ("ipv6: implement dataplane support for rthdr type 4 (Segment Routing Header)") Cc: stable@kernel.org Reported-by: TencentOS Corvus AI Assisted-by: tencentos-corvus-ai:kimi-k3 Signed-off-by: Jun Yang --- A KASAN reproducer for this issue is available if requested. net/ipv6/exthdrs.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 9c677eb1d1a6..4c407520e9da 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -452,8 +452,6 @@ static int ipv6_srh_rcv(struct sk_buff *skb) hdr->segments_left--; addr = hdr->segments + hdr->segments_left; - skb_push(skb, sizeof(struct ipv6hdr)); - if (skb->ip_summed == CHECKSUM_COMPLETE) seg6_update_csum(skb); @@ -462,6 +460,7 @@ static int ipv6_srh_rcv(struct sk_buff *skb) ip6_route_input(skb); if (skb_dst(skb)->error) { + skb_push(skb, -skb_network_offset(skb)); dst_input(skb); return -1; } @@ -476,10 +475,10 @@ static int ipv6_srh_rcv(struct sk_buff *skb) } ipv6_hdr(skb)->hop_limit--; - skb_pull(skb, sizeof(struct ipv6hdr)); goto looped_back; } + skb_push(skb, -skb_network_offset(skb)); dst_input(skb); return -1; -- 2.55.0