When the iptables TEE target copies an SCTP GSO packet with pskb_copy(), the new skb shares the frag_list skbs by reference (their refcounts are bumped but they are not deep-copied). The SCTP receive path skips linearization for uncloned GSO packets, so it later operates directly on the shared frag_list entries. If skb_copy_bits() encounters a length discrepancy or invalid offset in such a shared frag_list skb, it returns -EFAULT, which triggers a BUG_ON in __pskb_pull_tail(). Extend the linearization condition to also cover GSO packets whose frag_list head is shared, so SCTP obtains an exclusive, contiguous copy of the data before processing it. Fixes: 4c2f24549644 ("sctp: linearize early if it's not GSO") Reported-by: syzbot+f15a6eff7a53f6eb7dc3@syzkaller.appspotmail.com Signed-off-by: Xin Long --- net/sctp/input.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net/sctp/input.c b/net/sctp/input.c index 9494cfa51106..db3c7818724b 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -117,8 +117,13 @@ int sctp_rcv(struct sk_buff *skb) * it's better to just linearize it otherwise crc computing * takes longer. */ - if (((!is_gso || skb_cloned(skb)) && skb_linearize(skb)) || - !pskb_may_pull(skb, sizeof(struct sctphdr))) + if (!is_gso || skb_cloned(skb) || + (skb_has_frag_list(skb) && skb_shared(skb_shinfo(skb)->frag_list))) { + if (skb_linearize(skb)) + goto discard_it; + } + + if (!pskb_may_pull(skb, sizeof(struct sctphdr))) goto discard_it; /* Pull up the IP header. */ -- 2.47.1