From: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
To: bo cao <treebacker@foxmail.com>, netdev@vger.kernel.org
Cc: davem@davemloft.net, kuba@kernel.org, pabeni@redhat.com,
dsahern@kernel.org, edumazet@kernel.org, idosch@nvidia.com,
horms@kernel.org, stable@vger.kernel.org,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH net v3] ipv6: fix dead half of the MTU guard in __ip6_append_data()
Date: Sat, 31 Oct 2026 10:32:31 -0400 [thread overview]
Message-ID: <willemdebruijn.kernel.22a3131380a8c@gmail.com> (raw)
In-Reply-To: <tencent_AE3DA7423209553B562E5C7FBCB808EB680A@qq.com>
bo cao wrote:
> The second half of the MTU guard in __ip6_append_data() has been dead
> code since the initial git import:
>
> if (mtu <= fragheaderlen ||
> ((mtu - fragheaderlen) & ~7) + fragheaderlen <= sizeof(struct frag_hdr))
> goto emsgsize;
>
> (x & ~7) is unsigned and >= 0, and fragheaderlen is at least
> sizeof(struct ipv6hdr) = 40 > sizeof(struct frag_hdr) = 8, so the
> second condition can never be true.
>
> For mtu - fragheaderlen in [1,7], (x & ~7) == 0, so
>
> maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen - 8
>
> underflows below fragheaderlen. Every later bound computed as
> maxfraglen - fragheaderlen then wraps to 0xfffffff8, in particular the
> datalen clamp inside the fragment loop, which never fires and lets
> fraglen = datalen + fragheaderlen escape the MTU constraint.
>
> With MSG_MORE on an egress device without NETIF_F_SG, alloclen = mtu
> while skb_put() is called with the unclamped fraglen, so a single
> sendmsg() on a raw IPv6 socket with a 1240-byte HOPOPTS cmsg and
> IPV6_MTU = fragheaderlen + 1 triggers:
>
> skbuff: skb_over_panic: len:61280 put:61280 ...
> kernel BUG at net/core/skbuff.c
> skb_put __ip6_append_data ip6_append_data rawv6_sendmsg
>
> The same re-lowering exists with a transport-mode xfrm bundle: ESP/AH
> bytes counted in rt->dst.header_len are not part of fragheaderlen
> (ESP lacks XFRM_TYPE_NON_FRAGMENT), and ip6_append_data_mtu()
> subtracts dst.header_len from mtu for the first fragment, so after
> the entry guard passes, the recomputed maxfraglen can be pushed below
> fragheaderlen the same way.
>
> Replace the dead expression with a single check that requires room
> for at least one 8-octet payload block after the fragment header,
> matching the minimum ip6_fragment() already enforces:
>
> reserved_headerlen = fragheaderlen;
> if (!skb && !(rt->dst.flags & DST_XFRM_TUNNEL))
> reserved_headerlen += rt->dst.header_len;
>
> if (mtu < reserved_headerlen + sizeof(struct frag_hdr) + 8)
> goto emsgsize;
>
> For the transport-mode xfrm case, pre-reserving dst.header_len in the
> entry check makes the later ip6_append_data_mtu() recompute safe: the
> subtracted mtu still satisfies the same minimum, so the recomputed
> maxfraglen cannot drop below fragheaderlen either. The check runs
> before any zerocopy uarg is allocated, so the emsgsize path needs no
> cleanup.
>
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Cc: stable@vger.kernel.org
> Signed-off-by: bo cao <treebacker@foxmail.com>
> ---
> Changes in v3: collapse the fix into the entry check of
> __ip6_append_data() and leave ip6_append_data_mtu() unchanged, as
> suggested in review of v2; this also removes the emsgsize cleanup
> hazard for the zerocopy uarg and the separate maxfraglen check.
> ---
> net/ipv6/ip6_output.c | 9 ++++++---
> 1 file changed, 6 insertions(+), 3 deletions(-)
>
> diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
> index 25fe0ba98b1a..936dad85f1c9 100644
> --- a/net/ipv6/ip6_output.c
> +++ b/net/ipv6/ip6_output.c
> @@ -1477,7 +1477,7 @@ static int __ip6_append_data(struct sock *sk,
> bool paged, hold_tskey = false, extra_uref = false;
> struct ipv6_txoptions *opt = v6_cork->opt;
> int csummode = CHECKSUM_NONE;
> - unsigned int maxnonfragsize, headersize;
> + unsigned int maxnonfragsize, headersize, reserved_headerlen;
> unsigned int wmem_alloc_delta = 0;
>
> skb = skb_peek_tail(queue);
> @@ -1499,8 +1499,11 @@ static int __ip6_append_data(struct sock *sk,
> (opt ? opt->opt_flen + opt->opt_nflen : 0) +
> rt->rt6i_nfheader_len;
>
> - if (mtu <= fragheaderlen ||
> - ((mtu - fragheaderlen) & ~7) + fragheaderlen <= sizeof(struct frag_hdr))
> + reserved_headerlen = fragheaderlen;
> + if (!skb && !(rt->dst.flags & DST_XFRM_TUNNEL))
> + reserved_headerlen += rt->dst.header_len;
ip6_fragment() only enforces `mtu >= hlen + sizeof(struct frag_hdr) + 8`
once `skb->len > mtu`. Checking it unconditionally here rejects valid
unfragmented datagrams that fit within `mtu` and never carry a fragment
header (even with IPV6_DONTFRAG set)
Placing this before the `v6_cork->dontfrag` check below also skips
ipv6_local_rxpmtu() when `dontfrag` is set and the payload exceeds `mtu`
while `mtu < reserved_headerlen + sizeof(struct frag_hdr) + 8`.
If this guard is moved below the `dontfrag` / `maxnonfragsize` checks and
gated on fragmentation actually being needed (`reserved_headerlen +
cork->length + length > mtu`), `reserved_headerlen` should also include
`rt->dst.header_len` regardless of `!skb`, so a subsequent corked append
(`skb != NULL`) that pushes an initially unfragmented datagram past `mtu`
still accounts for the first fragment's xfrm header space.
> +
> + if (mtu < reserved_headerlen + sizeof(struct frag_hdr) + 8)
> goto emsgsize;
prev parent reply other threads:[~2026-10-31 14:32 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-30 8:48 [PATCH net v3] ipv6: fix dead half of the MTU guard in __ip6_append_data() bo cao
2026-10-30 8:53 ` netdev-bot+sinfo
2026-10-30 8:57 ` bo cao
2026-10-31 14:32 ` Willem de Bruijn [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=willemdebruijn.kernel.22a3131380a8c@gmail.com \
--to=willemdebruijn.kernel@gmail.com \
--cc=davem@davemloft.net \
--cc=dsahern@kernel.org \
--cc=edumazet@kernel.org \
--cc=horms@kernel.org \
--cc=idosch@nvidia.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=stable@vger.kernel.org \
--cc=treebacker@foxmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.