mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net v2] net: preserve skb header offsets during headroom changes
@ 2026-11-10  5:12 Kyle Zeng
  0 siblings, 0 replies; only message in thread
From: Kyle Zeng @ 2026-11-10  5:12 UTC (permalink / raw)
  To: netdev; +Cc: davem, kuba, edumazet, linux-kernel

Large device headroom can make aligned skb expansion wrap the u16
network, transport or checksum-start offsets. An MPLS LWT/netkit
reproducer reaches an out-of-bounds Ethernet-header write when
neighbour output converts the wrapped network offset into an unsigned
pull length. Bounding skb->data alone misses the later offsets.

Reject unsafe growth in pskb_expand_head() and skb_copy_expand() before
the offsets change, preserving the transport-header sentinel. Also
bound native MPLS's initial label pop, LWT's inner-header reset, and
the TC/Open vSwitch MPLS-pop helper, which can wrap without expanding
the allocation.

The issue was found by an LLM.

Fixes: 1a37e412a022 ("net: Use 16bits for *_headers fields of struct skbuff")
Cc: stable@vger.kernel.org
Signed-off-by: Kyle Zeng <kylebot@openai.com>
---
Changes in v2:
- Validate native MPLS headroom before the first label pop/reset.
- Check the actual shared expansion before network, live transport or
  partial-checksum offsets can wrap; retain unset-header semantics.
- Cover TC/Open vSwitch MPLS pop as well.

v1: https://lists.openwall.net/netdev/2026/10/30/51
Review: https://lkml.rescloud.iu.edu/hypermail/linux/kernel/2611.0/05339.html

I have a reproducer for this issue and can share via DM

 net/core/skbuff.c        | 27 +++++++++++++++++++++++++++
 net/mpls/af_mpls.c       |  3 +++
 net/mpls/mpls_iptunnel.c |  4 ++++
 3 files changed, 34 insertions(+)

diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 8da53d5b76fc..6e0205f923f3 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -2118,6 +2118,22 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
 }
 EXPORT_SYMBOL(skb_clone);
 
+static bool skb_header_offsets_fit(const struct sk_buff *skb,
+				   unsigned int nhead)
+{
+	if (!nhead)
+		return true;
+	if (nhead > U16_MAX - skb->network_header)
+		return false;
+	if (skb_transport_header_was_set(skb) &&
+	    nhead >= U16_MAX - skb->transport_header)
+		return false;
+	if (skb->ip_summed == CHECKSUM_PARTIAL &&
+	    nhead > U16_MAX - skb->csum_start)
+		return false;
+	return true;
+}
+
 void skb_headers_offset_update(struct sk_buff *skb, int off)
 {
 	/* Only adjust this if it actually is csum_start rather than csum */
@@ -2305,6 +2321,9 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
 		return -EINVAL;
 	}
 
+	if (unlikely(!skb_header_offsets_fit(skb, nhead)))
+		return -EINVAL;
+
 	if (unlikely(skb_shared(skb))) {
 		DEBUG_NET_WARN_ON_ONCE(1);
 		return -EINVAL;
@@ -2523,6 +2542,11 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
 		return NULL;
 
 	oldheadroom = skb_headroom(skb);
+	if (unlikely(newheadroom > oldheadroom &&
+		     !skb_header_offsets_fit(skb, (unsigned int)newheadroom -
+						  oldheadroom)))
+		return NULL;
+
 	n = __alloc_skb(newheadroom + skb->len + newtailroom,
 			gfp_mask, skb_alloc_rx_flag(skb),
 			NUMA_NO_NODE);
@@ -6771,6 +6795,9 @@ int skb_mpls_pop(struct sk_buff *skb, __be16 next_proto, int mac_len,
 	if (unlikely(!eth_p_mpls(skb->protocol)))
 		return 0;
 
+	if (unlikely(skb->network_header > U16_MAX - MPLS_HLEN))
+		return -EINVAL;
+
 	err = skb_ensure_writable(skb, mac_len + MPLS_HLEN);
 	if (unlikely(err))
 		return err;
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index ad4ad6f2001a..18c9654a6b36 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -419,6 +419,9 @@ static int mpls_forward(struct sk_buff *skb, struct net_device *dev,
 	if (!nh)
 		goto err;
 
+	if (unlikely(skb_headroom(skb) > U16_MAX - sizeof(*hdr)))
+		goto err;
+
 	/* Pop the label */
 	skb_pull(skb, sizeof(*hdr));
 	skb_reset_network_header(skb);
diff --git a/net/mpls/mpls_iptunnel.c b/net/mpls/mpls_iptunnel.c
index 1a1a0eb5b787..4eccbd911fe1 100644
--- a/net/mpls/mpls_iptunnel.c
+++ b/net/mpls/mpls_iptunnel.c
@@ -109,6 +109,10 @@ static int mpls_xmit(struct sk_buff *skb)
 	if (skb_cow_head(skb, hh_len + new_header_size))
 		goto drop;
 
+	/* The aligned expansion must still fit the u16 header offsets. */
+	if (unlikely(skb_headroom(skb) > U16_MAX))
+		goto drop;
+
 	skb_set_inner_protocol(skb, skb->protocol);
 	skb_reset_inner_network_header(skb);
 

base-commit: b3dfbde62cf1981c4638939c159dfd087b7a72e9
-- 
2.55.0.openai.911.g06873a30d2f3


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-11-10  5:13 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-10  5:12 [PATCH net v2] net: preserve skb header offsets during headroom changes Kyle Zeng

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

all inboxes | Powered by JetHome®