[PATCH net-next 9/9] net: skbuff: remove the BUG_ON()s from skb_shift()

From: Josef Bacik

Date: Tue Oct 06 2026 - 13:17:13 EST


skb_shift() has two BUG_ON()s. The first fires if the caller asks to
shift more than @skb holds. Nothing has been touched yet, and returning
0 already means "shifted nothing", which the TCP callers handle by
falling back. Warn once and return 0.

The second fires if the frags run out before @shiftlen does, but it
only checks after the shift has been committed to both skbs, when
there's nothing left to back out to. The loop that builds the new frag
layout only writes @tgt's frag slots past its nr_frags, and the one
branch that modifies @skb's frags also finishes the shift. So if the
loop ends with bytes still left to shift, nothing visible has changed
yet. That's the same state the MAX_SKB_FRAGS bail-out inside the loop
returns 0 from. Move the check up to just before the commit, warn once
and return 0 there.

Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@xxxxxxxxxxxxxx>
---
net/core/skbuff.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 8c6a45a20eb0..59f74850150b 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -4321,7 +4321,8 @@ int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen)
int from, to, merge, todo;
skb_frag_t *fragfrom, *fragto;

- BUG_ON(shiftlen > skb->len);
+ if (WARN_ON_ONCE(shiftlen > skb->len))
+ return 0;

if (skb_headlen(skb))
return 0;
@@ -4401,6 +4402,12 @@ int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen)
}
}

+ /* The frags ran out before shiftlen did. Nothing has been committed
+ * yet, so back out.
+ */
+ if (WARN_ON_ONCE(todo > 0))
+ return 0;
+
/* Ready to "commit" this state change to tgt */
skb_shinfo(tgt)->nr_frags = to;

@@ -4418,8 +4425,6 @@ int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen)
skb_shinfo(skb)->frags[to++] = skb_shinfo(skb)->frags[from++];
skb_shinfo(skb)->nr_frags = to;

- BUG_ON(todo > 0 && !skb_shinfo(skb)->nr_frags);
-
onlymerged:
/* Most likely the tgt won't ever need its checksum anymore, skb on
* the other hand might need it if it needs to be resent

--
2.55.0