mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-19 13:48:06 +00:00
774badd8a8
This fixes some corner cases triggered by enabling fraglist GRO, where some protocols may accidentally or intentionally linearize fraglist skbs. Previously, these skbs became unusable and segmenting them led to crashes. With this patch, they are properly handled by passing them to skb_segment instead of skb_segment_list. Signed-off-by: Felix Fietkau <nbd@nbd.name>
54 lines
2.0 KiB
Diff
54 lines
2.0 KiB
Diff
From: Willem de Bruijn <willemb@google.com>
|
|
Date: Sun, 22 Sep 2024 11:03:45 -0400
|
|
Subject: [PATCH] gso: fix gso fraglist segmentation after pull from
|
|
frag_list
|
|
|
|
Detect gso fraglist skbs with corrupted geometry (see below) and
|
|
pass these to skb_segment instead of skb_segment_list, as the first
|
|
can segment them correctly.
|
|
|
|
Valid SKB_GSO_FRAGLIST skbs
|
|
- consist of two or more segments
|
|
- the head_skb holds the protocol headers plus first gso_size
|
|
- one or more frag_list skbs hold exactly one segment
|
|
- all but the last must be gso_size
|
|
|
|
Optional datapath hooks such as NAT and BPF (bpf_skb_pull_data) can
|
|
modify these skbs, breaking these invariants.
|
|
|
|
In extreme cases they pull all data into skb linear. For UDP, this
|
|
causes a NULL ptr deref in __udpv4_gso_segment_list_csum at
|
|
udp_hdr(seg->next)->dest.
|
|
|
|
Detect invalid geometry due to pull, by checking head_skb size.
|
|
Don't just drop, as this may blackhole a destination. Convert to be
|
|
able to pass to regular skb_segment.
|
|
|
|
Link: https://lore.kernel.org/netdev/20240428142913.18666-1-shiming.cheng@mediatek.com/
|
|
Fixes: 3a1296a38d0c ("net: Support GRO/GSO fraglist chaining.")
|
|
Signed-off-by: Willem de Bruijn <willemb@google.com>
|
|
Cc: stable@vger.kernel.org
|
|
---
|
|
|
|
--- a/net/ipv4/udp_offload.c
|
|
+++ b/net/ipv4/udp_offload.c
|
|
@@ -296,8 +296,16 @@ struct sk_buff *__udp_gso_segment(struct
|
|
return NULL;
|
|
}
|
|
|
|
- if (skb_shinfo(gso_skb)->gso_type & SKB_GSO_FRAGLIST)
|
|
- return __udp_gso_segment_list(gso_skb, features, is_ipv6);
|
|
+ if (skb_shinfo(gso_skb)->gso_type & SKB_GSO_FRAGLIST) {
|
|
+ /* Detect modified geometry and pass these to skb_segment. */
|
|
+ if (skb_pagelen(gso_skb) - sizeof(*uh) == skb_shinfo(gso_skb)->gso_size)
|
|
+ return __udp_gso_segment_list(gso_skb, features, is_ipv6);
|
|
+
|
|
+ /* Setup csum, as fraglist skips this in udp4_gro_receive. */
|
|
+ gso_skb->csum_start = skb_transport_header(gso_skb) - gso_skb->head;
|
|
+ gso_skb->csum_offset = offsetof(struct udphdr, check);
|
|
+ gso_skb->ip_summed = CHECKSUM_PARTIAL;
|
|
+ }
|
|
|
|
skb_pull(gso_skb, sizeof(*uh));
|
|
|