ipv6_rpl_srh_rcv() processes a Routing Protocol for LLNs Source Routing Header by decompressing it, swapping the next segment address into ipv6_hdr->daddr, recompressing, and pushing the new header back. The recompressed header can be larger than the original when the address-elision opportunities are worse after the swap. The function pulls (hdr->hdrlen + 1) << 3 bytes (the old header) and pushes (chdr->hdrlen + 1) << 3 + sizeof(ipv6hdr) bytes (the new header plus the IPv6 header). pskb_expand_head() is called to guarantee headroom only when segments_left == 0. A crafted SRH that loops back to the local host (each segment is a local address, so ip6_route_input() delivers it back to ipv6_rpl_srh_rcv()) with chdr growing on each pass exhausts headroom over several iterations. When skb_push() lands skb->data exactly at skb->head, skb_reset_network_header() stores 0, and skb_mac_header_rebuild()'s skb_set_mac_header(skb, -skb->mac_len) computes 0 + (u16)(-14) = 65522. The subsequent memmove writes 14 bytes at skb->head + 65522. Expand the head whenever there is insufficient room for the push, not only on the final segment. Cc: "David S. Miller" Cc: David Ahern Cc: Eric Dumazet Cc: Jakub Kicinski Cc: Paolo Abeni Cc: Simon Horman Reported-by: Anthropic Cc: stable Assisted-by: gkh_clanker_t1000 Signed-off-by: Greg Kroah-Hartman --- net/ipv6/exthdrs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 95558fd6f447..d866ab011e0a 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -592,7 +592,9 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb) skb_pull(skb, ((hdr->hdrlen + 1) << 3)); skb_postpull_rcsum(skb, oldhdr, sizeof(struct ipv6hdr) + ((hdr->hdrlen + 1) << 3)); - if (unlikely(!hdr->segments_left)) { + if (unlikely(!hdr->segments_left || + skb_headroom(skb) < sizeof(struct ipv6hdr) + + ((chdr->hdrlen + 1) << 3))) { if (pskb_expand_head(skb, sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3), 0, GFP_ATOMIC)) { __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUTDISCARDS); -- 2.53.0