[PATCH net-next v2 8/8] net: skbuff: remove the BUG_ON()s from skb_shift()
From: Josef Bacik
Date: Wed Oct 07 2026 - 13:39:50 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. Return 0, with a DEBUG_NET_WARN_ON_ONCE() for debug
kernels.
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 and
return 0 there the same way.
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@xxxxxxxxxxxxxx>
---
net/core/skbuff.c | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 629de22d98e4..7d23c2d10550 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -4331,7 +4331,10 @@ 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 (unlikely(shiftlen > skb->len)) {
+ DEBUG_NET_WARN_ON_ONCE(1);
+ return 0;
+ }
if (skb_headlen(skb))
return 0;
@@ -4411,6 +4414,14 @@ 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 (unlikely(todo > 0)) {
+ DEBUG_NET_WARN_ON_ONCE(1);
+ return 0;
+ }
+
/* Ready to "commit" this state change to tgt */
skb_shinfo(tgt)->nr_frags = to;
@@ -4428,8 +4439,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