ipv4: fix a non-progressing fragmentation loop from undersized effective MTUs
Subsystems (3)
v2 cover ipv4: fix a non-progressing fragmentation loop from undersized effective MTUs
From: Yong Wang <[email protected]>
Hi Linux kernel maintainers,
This series fixes a non-progressing IPv4 fragmentation loop in net/ipv4/ip_output.c. The bug is reachable by a non-root user with CAP_NET_ADMIN in a user-created user and network namespace.
---- details below ----
Bug details:
ip_do_fragment() subtracts the IPv4 header length from the effective MTU and passes the resulting payload MTU to ip_frag_next().
If the effective MTU is smaller than hlen + 8, ip_frag_next() rounds the fragment payload length down to zero. The fragmentation state then never makes forward progress: state->left, state->ptr and state->offset stay unchanged while ip_do_fragment() keeps allocating and transmitting header-only fragments until the softlockup detector fires.
This is reproducible with a route installed using "mtu lock 20", but it is also reproducible without route MTU lock, for example by forwarding a packet to a device whose MTU is 20.
Because the failure happens in the fragmentation path itself, this series fixes it in ip_do_fragment() by rejecting mtu < hlen + 8 with -EMSGSIZE, matching the existing IPv6 fragmentation check.
The bug is reachable through more than one userspace-controlled path. A user with CAP_NET_ADMIN in a user-created network namespace can trigger it either by installing an undersized locked route MTU or by forwarding through an egress device whose MTU is too small.
Tested in a 2 vCPU, 2 GB RAM x86 QEMU guest:
- the old kernel reproduces the softlockup both with "mtu lock 20" and with a dummy egress device whose MTU is 20
- the new kernel returns packet loss in the no-route-mtu-lock reproducer and the guest remains alive
Reproducer:
Run inside the guest as root:
bash poc.sh
The PoC sets up forwarding through an egress dummy device whose MTU is 20, without installing any route MTU lock, and injects one forwarded non-DF ping from a child netns.
We run the PoC in a 2 vCPU, 2 GB RAM x86 QEMU environment.
v2:
- move the fix from ip_metrics_convert() to ip_do_fragment()
- update the commit message and cover letter per reviewer and Sashiko feedback
- switch the cover-letter reproducer to the no-route-mtu-lock dummy-device case
v1: https://lore.kernel.org/netdev/[email protected]/
------BEGIN poc.sh------
#!/bin/bash
sysctl -w net.ipv4.ip_forward=1
ip link add name dummy1 up mtu 20 type dummy
ip address add 192.0.2.1/24 dev dummy1
ip link add veth0 type veth peer name veth1
ip addr add 198.51.100.1/24 dev veth0
ip link set veth0 up
ip netns add ns1
ip link set veth1 netns ns1
ip -n ns1 address add 198.51.100.2/24 dev veth1 ip -n ns1 link set veth1 up
ip -n ns1 route add default via 198.51.100.1 ip netns exec ns1 ping -M dont -s 1000 -c 1 192.0.2.2
------END poc.sh--------
Best regards,
Yong Wang
Yong Wang (1):
ipv4: reject undersized MTUs in ip_do_fragment()
net/ipv4/ip_output.c | 4 ++++ 1 file changed, 4 insertions(+)
Signature · 1 line
2.53.0
PATCH v2 1/1 ipv4: reject undersized MTUs in ip_do_fragment()
From: Yong Wang <[email protected]>
ip_do_fragment() subtracts the IPv4 header length from the effective MTU and passes the resulting payload MTU to ip_frag_next().
If the effective MTU is smaller than hlen + 8, ip_frag_next() rounds the fragment payload length down to zero. The fragmentation state then never makes forward progress: state->left, state->ptr and state->offset stay unchanged while ip_do_fragment() keeps allocating and transmitting header-only fragments until the softlockup detector fires.
This is reproducible with a route installed using "mtu lock 20", but it is also reproducible without route MTU lock, for example by forwarding a packet to a device whose MTU is 20.
Fix it in ip_do_fragment() by rejecting mtu < hlen + 8 with -EMSGSIZE, matching the existing IPv6 fragmentation check.
--- net/ipv4/ip_output.c | 4 ++++ 1 file changed, 4 insertions(+)
net/ipv4/ip_output.c +4 -0
@@ -790,6 +790,10 @@ int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,*/ hlen = iph->ihl *4 ; +if (mtu < hlen +8 ) { + err = -EMSGSIZE ; + goto fail; + } mtu = mtu - hlen;/* Size of data space */ IPCB (skb)->flags |=IPSKB_FRAG_COMPLETE ; ll_rs =LL_RESERVED_SPACE (rt->dst.dev);
Signature · 1 line
2.53.0
Re: [PATCH net v2 1/1] ipv4: reject undersized MTUs in ip_do_fragment()
Quoted 18 lines · Ren Wei
From: Yong Wang <[email protected]>
ip_do_fragment()subtracts the IPv4 header length from the effective MTU and passes the resulting payload MTU toip_frag_next().If the effective MTU is smaller than hlen + 8,
ip_frag_next()rounds the fragment payload length down to zero. The fragmentation state then never makes forward progress:state->left,state->ptrandstate->offsetstay unchanged whileip_do_fragment()keeps allocating and transmitting header-only fragments until the softlockup detector fires.This is reproducible with a route installed using "mtu lock 20", but it is also reproducible without route MTU lock, for example by forwarding a packet to a device whose MTU is 20.
Fix it in
ip_do_fragment()by rejecting mtu < hlen + 8 with-EMSGSIZE, matching the existing IPv6 fragmentation check.
Quoted 6 lines
Fixes 1da177e4c3f4 ("Linux-2.6.12-rc2")Reported-by Vega <[email protected]>Assisted-by Codex:GPT-5.4Signed-off-by Yong Wang <[email protected]>Signed-off-by Ren Wei <[email protected]>
This patch was applied to netdev/net.git (main) by Jakub Kicinski <[email protected]>:
Hello:
This patch was applied to netdev/net.git (main)
by Jakub Kicinski <[email protected]>:
Quoted 9 lines · On Fri, 14 Aug 2026 01:35:25 +0800 you wrote:
From: Yong Wang <[email protected]>
Hi Linux kernel maintainers,
This series fixes a non-progressing IPv4 fragmentation loop in
net/ipv4/ip_output.c. The bug is reachable by a non-root user withCAP_NET_ADMINin a user-created user and network namespace.[...]
Here is the summary with links:
- [net,v2,1/1] ipv4: reject undersized MTUs in ip_do_fragment()
https://git.kernel.org/netdev/net/c/c0726f0caf8c
You are awesome, thank you!
Signature · 2 lines
Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html