]> git.ipfire.org Git - thirdparty/openwrt.git/blob
4e4bdf7556c3cca4f4373fd995068d34a7c0c75a
[thirdparty/openwrt.git] /
1 From 17bd3bd82f9f79f3feba15476c2b2c95a9b11ff8 Mon Sep 17 00:00:00 2001
2 From: Felix Fietkau <nbd@nbd.name>
3 Date: Thu, 26 Sep 2024 10:53:14 +0200
4 Subject: [PATCH] net: gso: fix tcp fraglist segmentation after pull from
5 frag_list
6
7 Detect tcp gso fraglist skbs with corrupted geometry (see below) and
8 pass these to skb_segment instead of skb_segment_list, as the first
9 can segment them correctly.
10
11 Valid SKB_GSO_FRAGLIST skbs
12 - consist of two or more segments
13 - the head_skb holds the protocol headers plus first gso_size
14 - one or more frag_list skbs hold exactly one segment
15 - all but the last must be gso_size
16
17 Optional datapath hooks such as NAT and BPF (bpf_skb_pull_data) can
18 modify these skbs, breaking these invariants.
19
20 In extreme cases they pull all data into skb linear. For TCP, this
21 causes a NULL ptr deref in __tcpv4_gso_segment_list_csum at
22 tcp_hdr(seg->next).
23
24 Detect invalid geometry due to pull, by checking head_skb size.
25 Don't just drop, as this may blackhole a destination. Convert to be
26 able to pass to regular skb_segment.
27
28 Approach and description based on a patch by Willem de Bruijn.
29
30 Link: https://lore.kernel.org/netdev/20240428142913.18666-1-shiming.cheng@mediatek.com/
31 Link: https://lore.kernel.org/netdev/20240922150450.3873767-1-willemdebruijn.kernel@gmail.com/
32 Fixes: bee88cd5bd83 ("net: add support for segmenting TCP fraglist GSO packets")
33 Cc: stable@vger.kernel.org
34 Signed-off-by: Felix Fietkau <nbd@nbd.name>
35 Reviewed-by: Willem de Bruijn <willemb@google.com>
36 Link: https://patch.msgid.link/20240926085315.51524-1-nbd@nbd.name
37 Signed-off-by: Jakub Kicinski <kuba@kernel.org>
38 ---
39 net/ipv4/tcp_offload.c | 10 ++++++++--
40 net/ipv6/tcpv6_offload.c | 10 ++++++++--
41 2 files changed, 16 insertions(+), 4 deletions(-)
42
43 --- a/net/ipv4/tcp_offload.c
44 +++ b/net/ipv4/tcp_offload.c
45 @@ -104,8 +104,14 @@ static struct sk_buff *tcp4_gso_segment(
46 if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
47 return ERR_PTR(-EINVAL);
48
49 - if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)
50 - return __tcp4_gso_segment_list(skb, features);
51 + if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST) {
52 + struct tcphdr *th = tcp_hdr(skb);
53 +
54 + if (skb_pagelen(skb) - th->doff * 4 == skb_shinfo(skb)->gso_size)
55 + return __tcp4_gso_segment_list(skb, features);
56 +
57 + skb->ip_summed = CHECKSUM_NONE;
58 + }
59
60 if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
61 const struct iphdr *iph = ip_hdr(skb);
62 --- a/net/ipv6/tcpv6_offload.c
63 +++ b/net/ipv6/tcpv6_offload.c
64 @@ -158,8 +158,14 @@ static struct sk_buff *tcp6_gso_segment(
65 if (!pskb_may_pull(skb, sizeof(*th)))
66 return ERR_PTR(-EINVAL);
67
68 - if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)
69 - return __tcp6_gso_segment_list(skb, features);
70 + if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST) {
71 + struct tcphdr *th = tcp_hdr(skb);
72 +
73 + if (skb_pagelen(skb) - th->doff * 4 == skb_shinfo(skb)->gso_size)
74 + return __tcp6_gso_segment_list(skb, features);
75 +
76 + skb->ip_summed = CHECKSUM_NONE;
77 + }
78
79 if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
80 const struct ipv6hdr *ipv6h = ipv6_hdr(skb);