From: Josef Bacik When bpf_skb_adjust_room() grows a TCP GSO packet without BPF_F_ADJ_ROOM_FIXED_GSO, gso_size is lowered by the number of bytes added. Nothing looks at how large it is. gso_size is the size of the segments the peer has sent if the packet comes out of GRO, and what the sender has written into the virtio header if it comes from a tun device or a packet socket. If it equals len_diff the result is 0, and a packet of up to 64 KB is no GSO packet any more. If it is smaller, the 16 bit value wraps around. One less than len_diff gives GSO_BY_FRAGS. skb_segment() then takes the length of each segment from the frag_list. A packet with more than 64 KB of payload gets that far, and one from a tun device has no frag_list: BUG: KASAN: null-ptr-deref in skb_segment+0xcf1/0x1a90 BUG: kernel NULL pointer dereference, address: 0000000000000070 tcp_gso_segment+0x1be/0x9f0 inet_gso_segment+0x288/0x740 inet_gso_segment+0x288/0x740 skb_mac_gso_segment+0x179/0x2e0 Refuse to grow such a packet, with the error that other packets which cannot be adjusted get. Fixes: 2be7e212d541 ("bpf: add bpf_skb_adjust_room helper") Signed-off-by: Josef Bacik Signed-off-by: Alexei Starovoitov --- net/core/filter.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/core/filter.c b/net/core/filter.c index ba536be2915f..a18702f89feb 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3612,6 +3612,11 @@ static int bpf_skb_net_grow(struct sk_buff *skb, u32 off, u32 len_diff, return -ENOTSUPP; } + /* The MSS is lowered by len_diff: something has to be left of it. */ + if (skb_is_gso(skb) && !(flags & BPF_F_ADJ_ROOM_FIXED_GSO) && + skb_shinfo(skb)->gso_size <= len_diff) + return -ENOTSUPP; + ret = skb_cow_head(skb, meta_len + len_diff); if (unlikely(ret < 0)) return ret; -- 2.55.0