* [PATCH net-next 4/4] net: Fix UDP length overflow with PMTU discover and big MTU
@ 2026-08-05 20:59 Alice Mikityanska
2026-08-06 3:26 ` Willem de Bruijn
0 siblings, 1 reply; 4+ messages in thread
From: Alice Mikityanska @ 2026-08-05 20:59 UTC (permalink / raw)
To: Willem de Bruijn, David Ahern, Ido Schimmel, Jakub Kicinski,
Paolo Abeni
Cc: David S. Miller, Eric Dumazet, Simon Horman, Shuah Khan, netdev,
linux-kselftest, Alice Mikityanska, syzbot+ce13c07d96d04716eaa2
From: Alice Mikityanska <alice@isovalent.com>
This commit bounds cork->base.fragsize to IP(6)_MAX_MTU to avoid a
possible overflow of UDP length that triggers a WARN in
udp_set_len_short when setsockopt IP(V6)_MTU_DISCOVER is set to
IPV6_PMTUDISC_DO or IP(V6)_PMTUDISC_PROBE, and a large packet is sent
over a netdev with an unusually large MTU.
Steps to reproduce (included in the new selftest):
1. Set device MTU bigger than IP6_MAX_MTU (or IP_MAX_MTU + 20).
cork->base.fragsize will be set to that MTU in ip(6)_setup_cork.
2. Set IP(V6)_MTU_DISCOVER to IP(V6)_PMTUDISC_PROBE or IPV6_PMTUDISC_DO.
It lets maxnonfragsize be set to device MTU (cork->fragsize) in
__ip(6)_append_data, rather than to IP(6)_MAX_MTU.
3. Send 65528 bytes of payload (+8 bytes of UDP header, +20/40 bytes of
IPv4/IPv6 header). Device MTU allows it (it's only one byte bigger
than IP6_MAX_MTU or IP_MAX_MTU + IPv4 header, and the device MTU is
bigger than that).
4. The UDP length in the built packet is 65536, which overflows the
16-bit length field and triggers the WARN in udp_set_len_short.
Note: IP_PMTUDISC_DO with IPv4 is safe, because ip_dst_mtu_maybe_forward
always clamps at IP_MAX_MTU, unlike ip6_dst_mtu_maybe_forward.
Reported-by: syzbot+ce13c07d96d04716eaa2@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/netdev/6a6a966c.86abc875.e5c3d.0054.GAE@google.com/
Signed-off-by: Alice Mikityanska <alice@isovalent.com>
Assisted-by: Claude:claude-sonnet-4.6
Cc: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
---
net/ipv4/ip_output.c | 2 +
net/ipv6/ip6_output.c | 2 +-
tools/testing/selftests/net/Makefile | 1 +
tools/testing/selftests/net/cork_fragsize.py | 77 ++++++++++++++++++++
4 files changed, 81 insertions(+), 1 deletion(-)
create mode 100755 tools/testing/selftests/net/cork_fragsize.py
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index e6dd1e5b8c32..2bc997c4fa7b 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -1299,6 +1299,8 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork,
cork->fragsize = ip_sk_use_pmtu(sk) ?
dst4_mtu(&rt->dst) : READ_ONCE(rt->dst.dev->mtu);
+ if (cork->fragsize > IP_MAX_MTU)
+ cork->fragsize = IP_MAX_MTU;
if (!inetdev_valid_mtu(cork->fragsize))
return -ENETUNREACH;
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 368e4fa3b43c..91c92cb8e529 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -1430,7 +1430,7 @@ static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork,
if (frag_size && frag_size < mtu)
mtu = frag_size;
- cork->base.fragsize = mtu;
+ cork->base.fragsize = min(mtu, IP6_MAX_MTU);
cork->base.gso_size = ipc6->gso_size;
cork->base.tx_flags = 0;
cork->base.mark = ipc6->sockc.mark;
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index b43ddf192ecc..628203fda424 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -25,6 +25,7 @@ TEST_PROGS := \
cmsg_so_mark.sh \
cmsg_so_priority.sh \
cmsg_time.sh \
+ cork_fragsize.py \
double_udp_encap.sh \
drop_monitor_tests.sh \
ecmp_rehash.sh \
diff --git a/tools/testing/selftests/net/cork_fragsize.py b/tools/testing/selftests/net/cork_fragsize.py
new file mode 100755
index 000000000000..7a1bb069227a
--- /dev/null
+++ b/tools/testing/selftests/net/cork_fragsize.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+# Test possible UDP length overflow in udp_send_skb/udp_v6_send_skb.
+
+from lib.py import ksft_run, ksft_exit, ksft_true
+from lib.py import ip, NetNS, NetNSEnter
+import errno
+import socket
+import subprocess
+
+
+IP_MTU_DISCOVER = 10
+IP_PMTUDISC_PROBE = 3
+IPV6_MTU_DISCOVER = 23
+IPV6_PMTUDISC_DO = 2
+
+
+def check_dmesg_clean(func) -> bool:
+ dmesg = subprocess.Popen(['dmesg'], stdout=subprocess.PIPE)
+ result = subprocess.run(['grep', '-q', f'WARNING:.*{func}'], stdin=dmesg.stdout)
+ dmesg.wait()
+ return result.returncode != 0 and dmesg.returncode == 0
+
+
+def test_ipv6() -> None:
+ with NetNS() as ns:
+ ip('link add dummy type dummy', ns=ns)
+ ip('link set dummy mtu 65576', ns=ns)
+ ip('link set dummy up', ns=ns)
+ ip('-6 addr add fd00::1/64 dev dummy nodad', ns=ns)
+ ip('-6 neigh add fd00::2 lladdr 02:00:00:00:00:02 dev dummy nud permanent', ns=ns)
+
+ with NetNSEnter(ns):
+ with socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) as fd:
+ fd.setsockopt(socket.IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_DO)
+ try:
+ fd.sendto(b' ' * 65528, ('fd00::2', 1234))
+ except OSError as e:
+ # Ignore EMSGSIZE: it happens on kernels with the fix.
+ if e.errno != errno.EMSGSIZE:
+ raise
+
+ ip('link del dummy', ns=ns)
+
+ ksft_true(check_dmesg_clean('udp_v6_send_skb'), 'WARNING detected in dmesg')
+
+
+def test_ipv4() -> None:
+ with NetNS() as ns:
+ ip('link add dummy type dummy', ns=ns)
+ ip('link set dummy mtu 65556', ns=ns)
+ ip('link set dummy up', ns=ns)
+ ip('addr add 10.0.0.1/24 dev dummy', ns=ns)
+ ip('neigh add 10.0.0.2 lladdr 02:00:00:00:00:02 dev dummy nud permanent', ns=ns)
+
+ with NetNSEnter(ns):
+ with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as fd:
+ fd.setsockopt(socket.IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_PROBE)
+ try:
+ fd.sendto(b' ' * 65528, ('10.0.0.2', 1234))
+ except OSError as e:
+ # Ignore EMSGSIZE: the check happens after the WARN is printed.
+ if e.errno != errno.EMSGSIZE:
+ raise
+
+ ip('link del dummy', ns=ns)
+
+ ksft_true(check_dmesg_clean('udp_send_skb'), 'WARNING detected in dmesg')
+
+
+if __name__ == "__main__":
+ ksft_run([
+ test_ipv6,
+ test_ipv4,
+ ])
+ ksft_exit()
--
2.55.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH net-next 4/4] net: Fix UDP length overflow with PMTU discover and big MTU
2026-08-05 20:59 [PATCH net-next 4/4] net: Fix UDP length overflow with PMTU discover and big MTU Alice Mikityanska
@ 2026-08-06 3:26 ` Willem de Bruijn
2026-08-06 9:45 ` Alice Mikityanska
0 siblings, 1 reply; 4+ messages in thread
From: Willem de Bruijn @ 2026-08-06 3:26 UTC (permalink / raw)
To: Alice Mikityanska, Willem de Bruijn, David Ahern, Ido Schimmel,
Jakub Kicinski, Paolo Abeni
Cc: David S. Miller, Eric Dumazet, Simon Horman, Shuah Khan, netdev,
linux-kselftest, Alice Mikityanska, syzbot+ce13c07d96d04716eaa2,
vadim.fedorenko
Alice Mikityanska wrote:
> From: Alice Mikityanska <alice@isovalent.com>
>
> This commit bounds cork->base.fragsize to IP(6)_MAX_MTU to avoid a
> possible overflow of UDP length that triggers a WARN in
> udp_set_len_short when setsockopt IP(V6)_MTU_DISCOVER is set to
> IPV6_PMTUDISC_DO or IP(V6)_PMTUDISC_PROBE, and a large packet is sent
> over a netdev with an unusually large MTU.
>
> Steps to reproduce (included in the new selftest):
>
> 1. Set device MTU bigger than IP6_MAX_MTU (or IP_MAX_MTU + 20).
> cork->base.fragsize will be set to that MTU in ip(6)_setup_cork.
> 2. Set IP(V6)_MTU_DISCOVER to IP(V6)_PMTUDISC_PROBE or IPV6_PMTUDISC_DO.
> It lets maxnonfragsize be set to device MTU (cork->fragsize) in
> __ip(6)_append_data, rather than to IP(6)_MAX_MTU.
> 3. Send 65528 bytes of payload (+8 bytes of UDP header, +20/40 bytes of
> IPv4/IPv6 header). Device MTU allows it (it's only one byte bigger
> than IP6_MAX_MTU or IP_MAX_MTU + IPv4 header, and the device MTU is
> bigger than that).
> 4. The UDP length in the built packet is 65536, which overflows the
> 16-bit length field and triggers the WARN in udp_set_len_short.
This is discovered thanks to udp_set_len_short, but is this a
preexisting bug and the fix go to net with a Fixes tag?
> Note: IP_PMTUDISC_DO with IPv4 is safe, because ip_dst_mtu_maybe_forward
> always clamps at IP_MAX_MTU, unlike ip6_dst_mtu_maybe_forward.
That was introduced in commit 14972cbd34ff ("net: lwtunnel: Handle
fragmentation"), the message of which includes "This includes .. some
mtu fixes" without elaborating on those.
That introduced the same clamp in ip6_mtu. Which was removed in
commit 427faee167bc ("net: ipv6: introduce ip6_dst_mtu_maybe_forward")
Tangential to this fix, but maybe that should be reinstated. I don't
immediately see why the two would diverge on this point.
> Reported-by: syzbot+ce13c07d96d04716eaa2@syzkaller.appspotmail.com
> Closes: https://lore.kernel.org/netdev/6a6a966c.86abc875.e5c3d.0054.GAE@google.com/
> Signed-off-by: Alice Mikityanska <alice@isovalent.com>
> Assisted-by: Claude:claude-sonnet-4.6
> Cc: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
I only see this patch 4/4. Is there more that did not make it to the list?
> ---
> net/ipv4/ip_output.c | 2 +
> net/ipv6/ip6_output.c | 2 +-
> tools/testing/selftests/net/Makefile | 1 +
> tools/testing/selftests/net/cork_fragsize.py | 77 ++++++++++++++++++++
> 4 files changed, 81 insertions(+), 1 deletion(-)
> create mode 100755 tools/testing/selftests/net/cork_fragsize.py
>
> diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
> index e6dd1e5b8c32..2bc997c4fa7b 100644
> --- a/net/ipv4/ip_output.c
> +++ b/net/ipv4/ip_output.c
> @@ -1299,6 +1299,8 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork,
>
> cork->fragsize = ip_sk_use_pmtu(sk) ?
> dst4_mtu(&rt->dst) : READ_ONCE(rt->dst.dev->mtu);
> + if (cork->fragsize > IP_MAX_MTU)
> + cork->fragsize = IP_MAX_MTU;
>
> if (!inetdev_valid_mtu(cork->fragsize))
> return -ENETUNREACH;
> diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
> index 368e4fa3b43c..91c92cb8e529 100644
> --- a/net/ipv6/ip6_output.c
> +++ b/net/ipv6/ip6_output.c
> @@ -1430,7 +1430,7 @@ static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork,
> if (frag_size && frag_size < mtu)
> mtu = frag_size;
>
> - cork->base.fragsize = mtu;
> + cork->base.fragsize = min(mtu, IP6_MAX_MTU);
> cork->base.gso_size = ipc6->gso_size;
> cork->base.tx_flags = 0;
> cork->base.mark = ipc6->sockc.mark;
> diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
> index b43ddf192ecc..628203fda424 100644
> --- a/tools/testing/selftests/net/Makefile
> +++ b/tools/testing/selftests/net/Makefile
> @@ -25,6 +25,7 @@ TEST_PROGS := \
> cmsg_so_mark.sh \
> cmsg_so_priority.sh \
> cmsg_time.sh \
> + cork_fragsize.py \
> double_udp_encap.sh \
> drop_monitor_tests.sh \
> ecmp_rehash.sh \
> diff --git a/tools/testing/selftests/net/cork_fragsize.py b/tools/testing/selftests/net/cork_fragsize.py
> new file mode 100755
> index 000000000000..7a1bb069227a
> --- /dev/null
> +++ b/tools/testing/selftests/net/cork_fragsize.py
> @@ -0,0 +1,77 @@
> +#!/usr/bin/env python3
> +# SPDX-License-Identifier: GPL-2.0
> +
> +# Test possible UDP length overflow in udp_send_skb/udp_v6_send_skb.
> +
> +from lib.py import ksft_run, ksft_exit, ksft_true
> +from lib.py import ip, NetNS, NetNSEnter
> +import errno
> +import socket
> +import subprocess
> +
> +
> +IP_MTU_DISCOVER = 10
> +IP_PMTUDISC_PROBE = 3
> +IPV6_MTU_DISCOVER = 23
> +IPV6_PMTUDISC_DO = 2
> +
> +
> +def check_dmesg_clean(func) -> bool:
> + dmesg = subprocess.Popen(['dmesg'], stdout=subprocess.PIPE)
> + result = subprocess.run(['grep', '-q', f'WARNING:.*{func}'], stdin=dmesg.stdout)
> + dmesg.wait()
> + return result.returncode != 0 and dmesg.returncode == 0
> +
> +
> +def test_ipv6() -> None:
> + with NetNS() as ns:
> + ip('link add dummy type dummy', ns=ns)
> + ip('link set dummy mtu 65576', ns=ns)
> + ip('link set dummy up', ns=ns)
> + ip('-6 addr add fd00::1/64 dev dummy nodad', ns=ns)
> + ip('-6 neigh add fd00::2 lladdr 02:00:00:00:00:02 dev dummy nud permanent', ns=ns)
> +
> + with NetNSEnter(ns):
> + with socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) as fd:
> + fd.setsockopt(socket.IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_DO)
> + try:
> + fd.sendto(b' ' * 65528, ('fd00::2', 1234))
> + except OSError as e:
> + # Ignore EMSGSIZE: it happens on kernels with the fix.
> + if e.errno != errno.EMSGSIZE:
> + raise
> +
> + ip('link del dummy', ns=ns)
> +
> + ksft_true(check_dmesg_clean('udp_v6_send_skb'), 'WARNING detected in dmesg')
> +
> +
> +def test_ipv4() -> None:
> + with NetNS() as ns:
> + ip('link add dummy type dummy', ns=ns)
> + ip('link set dummy mtu 65556', ns=ns)
> + ip('link set dummy up', ns=ns)
> + ip('addr add 10.0.0.1/24 dev dummy', ns=ns)
> + ip('neigh add 10.0.0.2 lladdr 02:00:00:00:00:02 dev dummy nud permanent', ns=ns)
> +
> + with NetNSEnter(ns):
> + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as fd:
> + fd.setsockopt(socket.IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_PROBE)
> + try:
> + fd.sendto(b' ' * 65528, ('10.0.0.2', 1234))
> + except OSError as e:
> + # Ignore EMSGSIZE: the check happens after the WARN is printed.
> + if e.errno != errno.EMSGSIZE:
> + raise
> +
> + ip('link del dummy', ns=ns)
> +
> + ksft_true(check_dmesg_clean('udp_send_skb'), 'WARNING detected in dmesg')
> +
> +
> +if __name__ == "__main__":
> + ksft_run([
> + test_ipv6,
> + test_ipv4,
> + ])
> + ksft_exit()
> --
> 2.55.0
>
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net-next 4/4] net: Fix UDP length overflow with PMTU discover and big MTU
2026-08-06 3:26 ` Willem de Bruijn
@ 2026-08-06 9:45 ` Alice Mikityanska
2026-08-06 15:06 ` Willem de Bruijn
0 siblings, 1 reply; 4+ messages in thread
From: Alice Mikityanska @ 2026-08-06 9:45 UTC (permalink / raw)
To: Willem de Bruijn, David Ahern, Ido Schimmel, Jakub Kicinski,
Paolo Abeni
Cc: David S. Miller, Eric Dumazet, Simon Horman, Shuah Khan, netdev,
linux-kselftest, Alice Mikityanska, syzbot, vadim.fedorenko
On Thu, Aug 6, 2026, at 06:26, Willem de Bruijn wrote:
> Alice Mikityanska wrote:
>> From: Alice Mikityanska <alice@isovalent.com>
>>
>> This commit bounds cork->base.fragsize to IP(6)_MAX_MTU to avoid a
>> possible overflow of UDP length that triggers a WARN in
>> udp_set_len_short when setsockopt IP(V6)_MTU_DISCOVER is set to
>> IPV6_PMTUDISC_DO or IP(V6)_PMTUDISC_PROBE, and a large packet is sent
>> over a netdev with an unusually large MTU.
>>
>> Steps to reproduce (included in the new selftest):
>>
>> 1. Set device MTU bigger than IP6_MAX_MTU (or IP_MAX_MTU + 20).
>> cork->base.fragsize will be set to that MTU in ip(6)_setup_cork.
>> 2. Set IP(V6)_MTU_DISCOVER to IP(V6)_PMTUDISC_PROBE or IPV6_PMTUDISC_DO.
>> It lets maxnonfragsize be set to device MTU (cork->fragsize) in
>> __ip(6)_append_data, rather than to IP(6)_MAX_MTU.
>> 3. Send 65528 bytes of payload (+8 bytes of UDP header, +20/40 bytes of
>> IPv4/IPv6 header). Device MTU allows it (it's only one byte bigger
>> than IP6_MAX_MTU or IP_MAX_MTU + IPv4 header, and the device MTU is
>> bigger than that).
>> 4. The UDP length in the built packet is 65536, which overflows the
>> 16-bit length field and triggers the WARN in udp_set_len_short.
>
> This is discovered thanks to udp_set_len_short, but is this a
> preexisting bug and the fix go to net with a Fixes tag?
You're right, it's preexisting, it can go to net.
For the Fixes tag, I'm not sure about the first occurrence of this bug.
It could even be as old as 1470ddf7f8ce ("inet: Remove explicit write
references to sk/inet in ip_append_data"), but I can't compile this
kernel with modern tools and check myself, unless I bring up some VM
with an ancient distro from 2011. And I guess, it could be even older,
as corking existed before. At the same time, something else might have
prevented this bug back then.
If needed, I can try to do this archaeology.
>> Note: IP_PMTUDISC_DO with IPv4 is safe, because ip_dst_mtu_maybe_forward
>> always clamps at IP_MAX_MTU, unlike ip6_dst_mtu_maybe_forward.
>
> That was introduced in commit 14972cbd34ff ("net: lwtunnel: Handle
> fragmentation"), the message of which includes "This includes .. some
> mtu fixes" without elaborating on those.
>
> That introduced the same clamp in ip6_mtu. Which was removed in
> commit 427faee167bc ("net: ipv6: introduce ip6_dst_mtu_maybe_forward")
Looks like it could be by accident, it's a refactoring commit. The
similar change for IPv4 in commit ac6627a28dbf ("net: ipv4: Consolidate
ipv4_mtu and ip_dst_mtu_maybe_forward") preserves the clamp.
> Tangential to this fix, but maybe that should be reinstated. I don't
> immediately see why the two would diverge on this point.
I agree; even though the output case should be fixed by my patch, it
might still be relevant for forwarding. Let's see if Vadim has any
comment on the history of the above.
>> Reported-by: syzbot+ce13c07d96d04716eaa2@syzkaller.appspotmail.com
>> Closes: https://lore.kernel.org/netdev/6a6a966c.86abc875.e5c3d.0054.GAE@google.com/
>> Signed-off-by: Alice Mikityanska <alice@isovalent.com>
>> Assisted-by: Claude:claude-sonnet-4.6
>> Cc: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
>
> I only see this patch 4/4. Is there more that did not make it to the list?
Sorry, I sent it like this by accident, this is an only patch in this
submission.
>
>> ---
>> net/ipv4/ip_output.c | 2 +
>> net/ipv6/ip6_output.c | 2 +-
>> tools/testing/selftests/net/Makefile | 1 +
>> tools/testing/selftests/net/cork_fragsize.py | 77 ++++++++++++++++++++
>> 4 files changed, 81 insertions(+), 1 deletion(-)
>> create mode 100755 tools/testing/selftests/net/cork_fragsize.py
>>
>> diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
>> index e6dd1e5b8c32..2bc997c4fa7b 100644
>> --- a/net/ipv4/ip_output.c
>> +++ b/net/ipv4/ip_output.c
>> @@ -1299,6 +1299,8 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork,
>>
>> cork->fragsize = ip_sk_use_pmtu(sk) ?
>> dst4_mtu(&rt->dst) : READ_ONCE(rt->dst.dev->mtu);
>> + if (cork->fragsize > IP_MAX_MTU)
>> + cork->fragsize = IP_MAX_MTU;
>>
>> if (!inetdev_valid_mtu(cork->fragsize))
>> return -ENETUNREACH;
>> diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
>> index 368e4fa3b43c..91c92cb8e529 100644
>> --- a/net/ipv6/ip6_output.c
>> +++ b/net/ipv6/ip6_output.c
>> @@ -1430,7 +1430,7 @@ static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork,
>> if (frag_size && frag_size < mtu)
>> mtu = frag_size;
>>
>> - cork->base.fragsize = mtu;
>> + cork->base.fragsize = min(mtu, IP6_MAX_MTU);
>> cork->base.gso_size = ipc6->gso_size;
>> cork->base.tx_flags = 0;
>> cork->base.mark = ipc6->sockc.mark;
>> diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
>> index b43ddf192ecc..628203fda424 100644
>> --- a/tools/testing/selftests/net/Makefile
>> +++ b/tools/testing/selftests/net/Makefile
>> @@ -25,6 +25,7 @@ TEST_PROGS := \
>> cmsg_so_mark.sh \
>> cmsg_so_priority.sh \
>> cmsg_time.sh \
>> + cork_fragsize.py \
>> double_udp_encap.sh \
>> drop_monitor_tests.sh \
>> ecmp_rehash.sh \
>> diff --git a/tools/testing/selftests/net/cork_fragsize.py b/tools/testing/selftests/net/cork_fragsize.py
>> new file mode 100755
>> index 000000000000..7a1bb069227a
>> --- /dev/null
>> +++ b/tools/testing/selftests/net/cork_fragsize.py
>> @@ -0,0 +1,77 @@
>> +#!/usr/bin/env python3
>> +# SPDX-License-Identifier: GPL-2.0
>> +
>> +# Test possible UDP length overflow in udp_send_skb/udp_v6_send_skb.
>> +
>> +from lib.py import ksft_run, ksft_exit, ksft_true
>> +from lib.py import ip, NetNS, NetNSEnter
>> +import errno
>> +import socket
>> +import subprocess
>> +
>> +
>> +IP_MTU_DISCOVER = 10
>> +IP_PMTUDISC_PROBE = 3
>> +IPV6_MTU_DISCOVER = 23
>> +IPV6_PMTUDISC_DO = 2
>> +
>> +
>> +def check_dmesg_clean(func) -> bool:
>> + dmesg = subprocess.Popen(['dmesg'], stdout=subprocess.PIPE)
>> + result = subprocess.run(['grep', '-q', f'WARNING:.*{func}'], stdin=dmesg.stdout)
>> + dmesg.wait()
>> + return result.returncode != 0 and dmesg.returncode == 0
>> +
>> +
>> +def test_ipv6() -> None:
>> + with NetNS() as ns:
>> + ip('link add dummy type dummy', ns=ns)
>> + ip('link set dummy mtu 65576', ns=ns)
>> + ip('link set dummy up', ns=ns)
>> + ip('-6 addr add fd00::1/64 dev dummy nodad', ns=ns)
>> + ip('-6 neigh add fd00::2 lladdr 02:00:00:00:00:02 dev dummy nud permanent', ns=ns)
>> +
>> + with NetNSEnter(ns):
>> + with socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) as fd:
>> + fd.setsockopt(socket.IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_DO)
>> + try:
>> + fd.sendto(b' ' * 65528, ('fd00::2', 1234))
>> + except OSError as e:
>> + # Ignore EMSGSIZE: it happens on kernels with the fix.
>> + if e.errno != errno.EMSGSIZE:
>> + raise
>> +
>> + ip('link del dummy', ns=ns)
>> +
>> + ksft_true(check_dmesg_clean('udp_v6_send_skb'), 'WARNING detected in dmesg')
>> +
>> +
>> +def test_ipv4() -> None:
>> + with NetNS() as ns:
>> + ip('link add dummy type dummy', ns=ns)
>> + ip('link set dummy mtu 65556', ns=ns)
>> + ip('link set dummy up', ns=ns)
>> + ip('addr add 10.0.0.1/24 dev dummy', ns=ns)
>> + ip('neigh add 10.0.0.2 lladdr 02:00:00:00:00:02 dev dummy nud permanent', ns=ns)
>> +
>> + with NetNSEnter(ns):
>> + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as fd:
>> + fd.setsockopt(socket.IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_PROBE)
>> + try:
>> + fd.sendto(b' ' * 65528, ('10.0.0.2', 1234))
>> + except OSError as e:
>> + # Ignore EMSGSIZE: the check happens after the WARN is printed.
>> + if e.errno != errno.EMSGSIZE:
>> + raise
>> +
>> + ip('link del dummy', ns=ns)
>> +
>> + ksft_true(check_dmesg_clean('udp_send_skb'), 'WARNING detected in dmesg')
>> +
>> +
>> +if __name__ == "__main__":
>> + ksft_run([
>> + test_ipv6,
>> + test_ipv4,
>> + ])
>> + ksft_exit()
>> --
>> 2.55.0
>>
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net-next 4/4] net: Fix UDP length overflow with PMTU discover and big MTU
2026-08-06 9:45 ` Alice Mikityanska
@ 2026-08-06 15:06 ` Willem de Bruijn
0 siblings, 0 replies; 4+ messages in thread
From: Willem de Bruijn @ 2026-08-06 15:06 UTC (permalink / raw)
To: Alice Mikityanska, Willem de Bruijn, David Ahern, Ido Schimmel,
Jakub Kicinski, Paolo Abeni
Cc: David S. Miller, Eric Dumazet, Simon Horman, Shuah Khan, netdev,
linux-kselftest, Alice Mikityanska, syzbot, vadim.fedorenko
Alice Mikityanska wrote:
> On Thu, Aug 6, 2026, at 06:26, Willem de Bruijn wrote:
> > Alice Mikityanska wrote:
> >> From: Alice Mikityanska <alice@isovalent.com>
> >>
> >> This commit bounds cork->base.fragsize to IP(6)_MAX_MTU to avoid a
> >> possible overflow of UDP length that triggers a WARN in
> >> udp_set_len_short when setsockopt IP(V6)_MTU_DISCOVER is set to
> >> IPV6_PMTUDISC_DO or IP(V6)_PMTUDISC_PROBE, and a large packet is sent
> >> over a netdev with an unusually large MTU.
> >>
> >> Steps to reproduce (included in the new selftest):
> >>
> >> 1. Set device MTU bigger than IP6_MAX_MTU (or IP_MAX_MTU + 20).
> >> cork->base.fragsize will be set to that MTU in ip(6)_setup_cork.
> >> 2. Set IP(V6)_MTU_DISCOVER to IP(V6)_PMTUDISC_PROBE or IPV6_PMTUDISC_DO.
> >> It lets maxnonfragsize be set to device MTU (cork->fragsize) in
> >> __ip(6)_append_data, rather than to IP(6)_MAX_MTU.
In __ip6_append_data I only see
if (ip6_sk_ignore_df(sk))
maxnonfragsize = sizeof(struct ipv6hdr) + IPV6_MAXPLEN;
else
maxnonfragsize = mtu;
> >> 3. Send 65528 bytes of payload (+8 bytes of UDP header, +20/40 bytes of
> >> IPv4/IPv6 header). Device MTU allows it (it's only one byte bigger
> >> than IP6_MAX_MTU or IP_MAX_MTU + IPv4 header, and the device MTU is
> >> bigger than that).
> >> 4. The UDP length in the built packet is 65536, which overflows the
> >> 16-bit length field and triggers the WARN in udp_set_len_short.
> >
> > This is discovered thanks to udp_set_len_short, but is this a
> > preexisting bug and the fix go to net with a Fixes tag?
>
> You're right, it's preexisting, it can go to net.
>
> For the Fixes tag, I'm not sure about the first occurrence of this bug.
> It could even be as old as 1470ddf7f8ce ("inet: Remove explicit write
> references to sk/inet in ip_append_data"), but I can't compile this
> kernel with modern tools and check myself, unless I bring up some VM
> with an ancient distro from 2011. And I guess, it could be even older,
> as corking existed before. At the same time, something else might have
> prevented this bug back then.
>
> If needed, I can try to do this archaeology.
I also suspect that this has been present for a long time, given that
your repro does not exercise anything particularly new.
Definitely no need to try to reproduce on an ancient system. We can
estimate the introduction based on code analysis.
In practice, most important is that the Fixes correcty identifies all
relevant active stable branches that could use the fix. If helpful, I
can also take a look.
Aside: I was not even aware that devices allow setting a device MTU
beyond ETH_MAX_MTU. But loopback indeed has no dev->max_mtu and
accepts up to INT32_MAX.
> >> Note: IP_PMTUDISC_DO with IPv4 is safe, because ip_dst_mtu_maybe_forward
> >> always clamps at IP_MAX_MTU, unlike ip6_dst_mtu_maybe_forward.
> >
> > That was introduced in commit 14972cbd34ff ("net: lwtunnel: Handle
> > fragmentation"), the message of which includes "This includes .. some
> > mtu fixes" without elaborating on those.
> >
> > That introduced the same clamp in ip6_mtu. Which was removed in
> > commit 427faee167bc ("net: ipv6: introduce ip6_dst_mtu_maybe_forward")
>
> Looks like it could be by accident, it's a refactoring commit. The
> similar change for IPv4 in commit ac6627a28dbf ("net: ipv4: Consolidate
> ipv4_mtu and ip_dst_mtu_maybe_forward") preserves the clamp.
>
> > Tangential to this fix, but maybe that should be reinstated. I don't
> > immediately see why the two would diverge on this point.
>
> I agree; even though the output case should be fixed by my patch, it
> might still be relevant for forwarding. Let's see if Vadim has any
> comment on the history of the above.
>
> >> Reported-by: syzbot+ce13c07d96d04716eaa2@syzkaller.appspotmail.com
> >> Closes: https://lore.kernel.org/netdev/6a6a966c.86abc875.e5c3d.0054.GAE@google.com/
> >> Signed-off-by: Alice Mikityanska <alice@isovalent.com>
> >> Assisted-by: Claude:claude-sonnet-4.6
> >> Cc: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
> >
> > I only see this patch 4/4. Is there more that did not make it to the list?
>
> Sorry, I sent it like this by accident, this is an only patch in this
> submission.
That explains. No worries.
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-08-06 15:06 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-05 20:59 [PATCH net-next 4/4] net: Fix UDP length overflow with PMTU discover and big MTU Alice Mikityanska
2026-08-06 3:26 ` Willem de Bruijn
2026-08-06 9:45 ` Alice Mikityanska
2026-08-06 15:06 ` 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