Thread (5 messages) 5 messages, 3 authors, 22h ago

[PATCH net 1/2] net/ipv4: reserve exthdrlen as headroom instead of counting it in skb->len

flat view
HOTtoday

From: tencentos_secure(TencentOS安全公共邮箱) <hidden>
Date: 2026-10-26 11:24:21
Subsystem: networking [general], networking [ipv4/ipv6], the rest · Maintainers: "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, David Ahern, Ido Schimmel, Linus Torvalds

When sending through an xfrm route (rt->dst.header_len > 0),
__ip_append_data() counts the bundle header space ("exthdrlen")
into skb->len of the first fragment skb: the skb setup does

	skb_reserve(skb, hh_len);
	data = skb_put(skb, fraglen + exthdrlen - pagedlen);
	skb_set_network_header(skb, exthdrlen);

so a fully filled first skb has skb->len == maxfraglen + exthdrlen.
Since commit 353e5c9abd90 ("ipv4: Fix IPsec slowpath fragmentation
problem"), the next iteration's fraggap = skb_prev->len - maxfraglen
therefore inherits the whole bundle header length instead of the
intended alignment slop.

fraggap is relayed into the next skb by skb_copy_and_csum_bits(), but
the MSG_MORE (alloclen = mtu) and linear (alloclen = fraglen) branches
account for fraggap in the allocation only for as long as datalen stays
uncapped.  Once datalen is capped at maxfraglen - fragheaderlen, the
bytes copied into the fresh skb exceed the skb head allocation and run
past the end of the buffer, corrupting skb_shared_info.  The
paged-branch accounting of
commit eca856950f7c ("ipv4: account for fraggap on the paged
allocation path") relies on datalen accounting for fraggap, which
stops being true exactly when datalen is capped.

Fix the root cause by reserving exthdrlen as headroom, like IPv6 has
done since commit 1f85851e17b6 ("ipv6: fix incorrent ipv6 ipsec
packet fragment") in 2012:

	skb_reserve(skb, hh_len + exthdrlen);
	data = skb_put(skb, fraglen - pagedlen);
	skb_reset_network_header(skb);

With this, every fragment skb satisfies skb->len <= maxfraglen, so
fraggap <= mtu - maxfraglen <= 7 (the alignment slop the relay was
designed for) and the copy destination is provably in bounds.

Allocation sizes, tail positions, tailroom and truesize are unchanged
(alloc_extra already carries exthdrlen), and when dst.header_len == 0
this is a literal no-op.

Fixes: 353e5c9abd90 ("ipv4: Fix IPsec slowpath fragmentation problem")
Reported-by: TencentOS Corvus AI <redacted>
Cc: <redacted>
Signed-off-by: Anakin Zhang <redacted>
---
 net/ipv4/ip_output.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index eaa6fab..b84d56c 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -1150,16 +1150,16 @@ alloc_new_skb:
 			 */
 			skb->ip_summed = csummode;
 			skb->csum = 0;
-			skb_reserve(skb, hh_len);
+			skb_reserve(skb, hh_len + exthdrlen);
 
 			/*
 			 *	Find where to start putting bytes.
 			 */
-			data = skb_put(skb, fraglen + exthdrlen - pagedlen);
-			skb_set_network_header(skb, exthdrlen);
+			data = skb_put(skb, fraglen - pagedlen);
+			skb_reset_network_header(skb);
 			skb->transport_header = (skb->network_header +
 						 fragheaderlen);
-			data += fragheaderlen + exthdrlen;
+			data += fragheaderlen;
 
 			if (fraggap) {
 				skb->csum = skb_copy_and_csum_bits(
-- 
2.55.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help