From: Zhiling Zou IP output paths reserve the output device's LL_RESERVED_SPACE() before recording skb network and transport header offsets. These skb header offset fields are 16-bit values, so every offset written by the IP output and raw HDRINCL paths must be representable in that type. Namespace-created stacked devices can make hard_header_len plus needed_headroom exceed U16_MAX. Without a range check, skb header offsets wrap while the actual skb data pointer remains after the large reserved headroom. The packet is then built at the wrong address and later header consumers operate on stale or invalid data. Reject IPv4 and IPv6 sends when the largest header offset that the path is about to store cannot be encoded in the skb header fields. Fixes: 1a37e412a022 ("net: Use 16bits for *_headers fields of struct skbuff") Cc: stable@vger.kernel.org Reported-by: Vega Assisted-by: Codex:gpt-5.4 Signed-off-by: Zhiling Zou Signed-off-by: Ren Wei --- net/ipv4/ip_output.c | 2 ++ net/ipv4/raw.c | 5 ++++- net/ipv6/ip6_output.c | 4 ++++ net/ipv6/raw.c | 2 ++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index e6dd1e5b8c327..c29af28ae1844 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -983,6 +983,8 @@ static int __ip_append_data(struct sock *sk, hh_len = LL_RESERVED_SPACE(rt->dst.dev); fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0); + if (hh_len + exthdrlen + fragheaderlen > U16_MAX) + return -EINVAL; maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen; maxnonfragsize = ip_sk_ignore_df(sk) ? IP_MAX_MTU : mtu; diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 7f74d8b95a373..4460a8db32bae 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -356,6 +356,8 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4, goto out; hlen = LL_RESERVED_SPACE(rt->dst.dev); + if (hlen > U16_MAX) + return -EINVAL; tlen = rt->dst.dev->needed_tailroom; skb = sock_alloc_send_skb(sk, length + hlen + tlen + 15, @@ -397,7 +399,8 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4, * in, reject the frame as invalid */ err = -EINVAL; - if (iphlen > length || iphlen < sizeof(*iph)) + if (iphlen > length || iphlen < sizeof(*iph) || + iphlen > U16_MAX - hlen) goto error_free; if (iphlen >= sizeof(*iph)) { diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 368e4fa3b43ca..7b41f95a40ad1 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1495,6 +1495,10 @@ static int __ip6_append_data(struct sock *sk, (opt ? opt->opt_flen + opt->opt_nflen : 0) + rt->rt6i_nfheader_len; + if (hh_len + sizeof(struct frag_hdr) + dst_exthdrlen + exthdrlen + + fragheaderlen > U16_MAX) + return -EINVAL; + if (mtu <= fragheaderlen || ((mtu - fragheaderlen) & ~7) + fragheaderlen <= sizeof(struct frag_hdr)) goto emsgsize; diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index b88d364e78aae..bb3803338eb91 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -613,6 +613,8 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length, if (flags&MSG_PROBE) goto out; + if (hlen > U16_MAX) + return -EINVAL; skb = sock_alloc_send_skb(sk, length + hlen + tlen + 15, flags & MSG_DONTWAIT, &err); -- 2.43.0