From e6974aad4bd7561ed2f1ee56acaee38e3d3c5d01 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Wed, 7 Oct 2026 17:36:07 +0000 Subject: [PATCH] net: skbuff: remove the BUG_ON()s from skb_shift() 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. Signed-off-by: Josef Bacik Link: https://patch.msgid.link/20261007-b4-skbuff-bug-on-v2-8-b9a5f732895b@toxicpanda.com Signed-off-by: Jakub Kicinski --- 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 c69c3d4cc8f4..93c2f440b9ec 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -4335,7 +4335,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; @@ -4415,6 +4418,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; @@ -4432,8 +4443,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