From: Kyle Zeng <kylebot@openai.com>
To: netdev@vger.kernel.org
Cc: davem@davemloft.net, kuba@kernel.org, edumazet@kernel.org,
pabeni@redhat.com, linux-kernel@vger.kernel.org,
Kyle Zeng <kylebot@openai.com>,
stable@vger.kernel.org
Subject: [PATCH net v3] net: preserve skb header offsets during headroom changes
Date: Fri, 13 Nov 2026 15:50:19 -0800 [thread overview]
Message-ID: <20261113235019.74384-1-kylebot@openai.com> (raw)
An MPLS LWT route over a netkit device configured with large headroom
can make aligned skb expansion wrap the 16-bit network_header.
Neighbour output interprets the resulting negative skb_network_offset()
as a large unsigned pull length, causing eth_header() to write outside
the skb allocation.
Reject growth that cannot preserve the network, set MAC and transport,
recorded inner-network, or partial-checksum offsets. Do not relocate a
set MAC or transport header onto its unset value. Check the actual
post-pop positions in skb_mpls_pop(), bound the native MPLS label pop,
and reject excessive LWT headroom before reading the IP header.
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 v3:
- Check the actual post-pop MAC/network positions and reject the MAC
unset value in skb_mpls_pop().
- Check a set outer MAC header and a recorded inner network header
during positive relocation.
- Move the MPLS LWT entry check before reading the IP header.
v2: https://lore.kernel.org/netdev/20261110051258.64350-1-kylebot@openai.com/
v1: https://lore.kernel.org/netdev/20261030042246.74144-1-kylebot@openai.com/
I have a reproducer for this issue and can share via DM.
net/core/skbuff.c | 35 +++++++++++++++++++++++++++++++++++
net/mpls/af_mpls.c | 3 +++
net/mpls/mpls_iptunnel.c | 3 +++
3 files changed, 41 insertions(+)
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 1ac1e327ac4c..bc836adafecd 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -2130,6 +2130,28 @@ 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_mac_header_was_set(skb) &&
+ nhead >= U16_MAX - skb->mac_header)
+ return false;
+ if (skb->ip_summed == CHECKSUM_PARTIAL &&
+ nhead > U16_MAX - skb->csum_start)
+ return false;
+ if (skb_inner_network_header_was_set(skb) &&
+ nhead > U16_MAX - skb->inner_network_header)
+ 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 */
@@ -2317,6 +2339,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;
@@ -2535,6 +2560,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);
@@ -6804,11 +6834,16 @@ EXPORT_SYMBOL_GPL(skb_mpls_push);
int skb_mpls_pop(struct sk_buff *skb, __be16 next_proto, int mac_len,
bool ethernet)
{
+ unsigned int headroom = skb_headroom(skb);
int err;
if (unlikely(!eth_p_mpls(skb->protocol)))
return 0;
+ if (unlikely(headroom >= U16_MAX - MPLS_HLEN ||
+ (unsigned int)mac_len > U16_MAX - MPLS_HLEN - headroom))
+ 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..5123394234cc 100644
--- a/net/mpls/mpls_iptunnel.c
+++ b/net/mpls/mpls_iptunnel.c
@@ -59,6 +59,9 @@ static int mpls_xmit(struct sk_buff *skb)
!dst->lwtstate || skb_warn_if_lro(skb))
goto drop;
+ if (unlikely(skb_headroom(skb) > U16_MAX))
+ goto drop;
+
skb_forward_csum(skb);
tun_encap_info = mpls_lwtunnel_encap(dst->lwtstate);
base-commit: 2800868f63227c8237d70c41e70d441c919620d6
--
2.55.0.openai.911.g06873a30d2f3
next reply other threads:[~2026-11-13 23:50 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-13 23:50 Kyle Zeng [this message]
2026-11-15 0:17 ` [PATCH net v3] net: preserve skb header offsets during headroom changes netdev-bot+sashiko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261113235019.74384-1-kylebot@openai.com \
--to=kylebot@openai.com \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.