Re: [PATCH net v2] ipv6: rpl: reserve mac_len headroom when recompressed SRH grows

Greg Kroah-Hartman
2026-04-21T13:12:00Z

On Tue, Apr 21, 2026 at 02:32:59PM +0200, Greg Kroah-Hartman wrote:
> ipv6_rpl_srh_rcv() decompresses an RFC 6554 Source Routing Header, swaps
> the next segment into ipv6_hdr->daddr, recompresses, then pulls the old
> header and pushes the new one plus the IPv6 header back. The
> recompressed header can be larger than the received one when the swap
> reduces the common-prefix length the segments share with daddr (CmprI=0,
> CmprE>0, seg[0][0] != daddr[0] gives the maximum +8 bytes).
>
> pskb_expand_head() was gated on segments_left == 0, so on earlier
> segments the push consumed unchecked headroom. Once skb_push() leaves
> fewer than skb->mac_len bytes in front of data,
> skb_mac_header_rebuild()'s call to:
>
> skb_set_mac_header(skb, -skb->mac_len);
>
> will store (data - head) - mac_len into the u16 mac_header field, which
> wraps to ~65530, and the following memmove() writes mac_len bytes ~64KiB
> past skb->head.
>
> A single AF_INET6/SOCK_RAW/IPV6_HDRINCL packet over lo with a two
> segment type-3 SRH (CmprI=0, CmprE=15) reaches headroom 8 after one
> pass; KASAN reports a 14-byte OOB write in ipv6_rthdr_rcv.
>
> Fix this by expanding the head whenever the remaining room is less than
> the push size plus mac_len, and request that much extra so the rebuilt
> MAC header fits afterwards.
>
> Fixes: 8610c7c6e3bd ("net: ipv6: add support for rpl sr exthdr")
> Cc: "David S. Miller" <davem@davemloft.net>
> Cc: David Ahern <dsahern@kernel.org>
> Cc: Eric Dumazet <edumazet@google.com>
> Cc: Jakub Kicinski <kuba@kernel.org>
> Cc: Paolo Abeni <pabeni@redhat.com>
> Cc: Simon Horman <horms@kernel.org>
> Cc: stable <stable@kernel.org>
> Reported-by: Anthropic
> Assisted-by: gkh_clanker_t1000
> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> ---
> v2: - fixed up if statement to actually work properly, and test it against
> a working poc (poc will be sent separately)
> Reworded the changelog and the subject to make more sense
> Link to v1: https://lore.kernel.org/r/2026042024-cabbie-gills-9371@gregkh
>
> net/ipv6/exthdrs.c | 13 +++++++------
> 1 file changed, 7 insertions(+), 6 deletions(-)
>
> diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
> index 95558fd6f447..b86a638d51e4 100644
> --- a/net/ipv6/exthdrs.c
> +++ b/net/ipv6/exthdrs.c
> @@ -491,6 +491,7 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)
> struct net *net = dev_net(skb->dev);
> struct inet6_dev *idev;
> struct ipv6hdr *oldhdr;
> + unsigned int chdr_len;
> unsigned char *buf;
> int accept_rpl_seg;
> int i, err;
> @@ -590,11 +591,11 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)
> oldhdr = ipv6_hdr(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 (pskb_expand_head(skb, sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3), 0,
> - GFP_ATOMIC)) {
> + chdr_len = sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3);
> + skb_postpull_rcsum(skb, oldhdr, chdr_len);

Crap, nope, this is wrong, let me go fix this...