Thread (6 messages) flat view 6 messages, 3 authors, 2d ago
WARM2d

[PATCH net v2] xfrm: validate ihl in xfrm4_transport_output()

From: Qihang <hidden>
Date: 2026-09-23 02:16:46
Also in: stable
Subsystem: networking [general], networking [ipsec], the rest · Maintainers: "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Steffen Klassert, Herbert Xu, Linus Torvalds

xfrm4_transport_output() reads the IPv4 header length (ihl) from the
packet and uses it for __skb_pull() and memmove() without validating it,
so a frame with ihl * 4 larger than the linear header underflows skb->len
to a huge value or trips BUG() in __skb_pull().

Packets injected into an xfrm interface (e.g. AF_PACKET on an xfrmi
device) reach xfrm output without the header validation that
ip_rcv_core() applies to received traffic.  The underflowed length then
flows into esp_output() and the crypto scatterlist setup, where every
length in the underflow window ends in a fatal fault (BUG_ON in
__skb_to_sgvec()).  This is a deterministic local denial of service
reachable by an unprivileged user through a user and network namespace.

Reject ihl < 5, matching ip_rcv_core(), so a complete IPv4 header is
present.  Then use pskb_may_pull() to make the header linear: this
rejects ihl > skb->len, linearizes a header that currently lives in
fragments, and guarantees __skb_pull()'s precondition that the pull must
not drive skb->len below skb->data_len, and it keeps the memmove() source
in bounds.  ip_hdr() is re-read afterwards because pskb_may_pull() may
reallocate the skb head.  xfrm6_transport_output() already bounds its
header length through xfrm6_hdr_offset().

Fixes: b59f45d0b2878 ("[IPSEC] xfrm: Abstract out encapsulation modes")
Cc: stable@vger.kernel.org
Signed-off-by: Qihang <redacted>
---
 net/xfrm/xfrm_output.c | 5 +++++
 1 file changed, 5 insertions(+)
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c
index e305ba3..86943e2 100644
--- a/net/xfrm/xfrm_output.c
+++ b/net/xfrm/xfrm_output.c
@@ -66,6 +66,11 @@ static int xfrm4_transport_output(struct xfrm_state *x, struct sk_buff *skb)
 	struct iphdr *iph = ip_hdr(skb);
 	int ihl = iph->ihl * 4;
 
+	if (iph->ihl < 5 || !pskb_may_pull(skb, ihl))
+		return -EINVAL;
+
+	iph = ip_hdr(skb);
+
 	if (!skb->inner_protocol)
 		skb_set_inner_transport_header(skb,
 					       skb_transport_offset(skb));
-- 
2.46.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