From 6d8118937ba477e03fa2e94e9bd9abac7291a2a2 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Sun, 1 Nov 2026 19:05:23 +0000 Subject: [PATCH] bpf: Don't let bpf_skb_adjust_room() raise gso_size to GSO_BY_FRAGS When bpf_skb_adjust_room() shrinks a TCP GSO skb, it adds the number of removed bytes to gso_size. gso_size has 16 bits, and nothing checks the sum. Thus gso_size can wrap to 0 or to a small size, or it can be GSO_BY_FRAGS. GSO_BY_FRAGS is not a size. It tells skb_segment() to read the length of each segment from the frag_list. If the TCP GSO skb has no frag_list, skb_segment() dereferences a NULL pointer. Return -ENOTSUPP and do not change the skb if the sum is GSO_BY_FRAGS or larger. With BPF_F_ADJ_ROOM_FIXED_GSO the helper does not change gso_size, so it shrinks the skb as before. Fixes: 2be7e212d541 ("bpf: add bpf_skb_adjust_room helper") Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261101190535.1454146-16-alexei.starovoitov@gmail.com --- net/core/filter.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/core/filter.c b/net/core/filter.c index 303c0c44dc2c..c0fc9ff04bd5 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3762,6 +3762,14 @@ static int bpf_skb_net_shrink(struct sk_buff *skb, u32 off, u32 len_diff, return -ENOTSUPP; } + /* + * The MSS is raised by len_diff. It has 16 bits, and GSO_BY_FRAGS is + * not a size. + */ + if (skb_is_gso(skb) && !(flags & BPF_F_ADJ_ROOM_FIXED_GSO) && + skb_shinfo(skb)->gso_size + len_diff >= GSO_BY_FRAGS) + return -ENOTSUPP; + ret = skb_unclone(skb, GFP_ATOMIC); if (unlikely(ret < 0)) return ret;