summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYan Zhai <yan@cloudflare.com>2024-09-09 14:22:47 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2024-09-12 11:07:53 +0200
commitc60a555f4949e5cc6c3ec7717fae95a62b894ba6 (patch)
tree336fc59124036c9c6407fd5c5afc1a7acd81f5b5
parent5c3e0ed81058f4ad52f4c1e3f05d9d01aa0015df (diff)
gso: fix dodgy bit handling for GSO_UDP_L4
[ Upstream commit 9840036786d90cea11a90d1f30b6dc003b34ee67 ] Commit 1fd54773c267 ("udp: allow header check for dodgy GSO_UDP_L4 packets.") checks DODGY bit for UDP, but for packets that can be fed directly to the device after gso_segs reset, it actually falls through to fragmentation: https://lore.kernel.org/all/CAJPywTKDdjtwkLVUW6LRA2FU912qcDmQOQGt2WaDo28KzYDg+A@mail.gmail.com/ This change restores the expected behavior of GSO_UDP_L4 packets. Fixes: 1fd54773c267 ("udp: allow header check for dodgy GSO_UDP_L4 packets.") Suggested-by: Willem de Bruijn <willemdebruijn.kernel@gmail.com> Signed-off-by: Yan Zhai <yan@cloudflare.com> Reviewed-by: Willem de Bruijn <willemb@google.com> Acked-by: Jason Wang <jasowang@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net> [5.15 stable: clean backport] Signed-off-by: Willem de Bruijn <willemb@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--net/ipv4/udp_offload.c13
1 files changed, 10 insertions, 3 deletions
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index feb40325f8d7..a0e509e0b3e1 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -272,13 +272,20 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
__sum16 check;
__be16 newlen;
- if (skb_shinfo(gso_skb)->gso_type & SKB_GSO_FRAGLIST)
- return __udp_gso_segment_list(gso_skb, features, is_ipv6);
-
mss = skb_shinfo(gso_skb)->gso_size;
if (gso_skb->len <= sizeof(*uh) + mss)
return ERR_PTR(-EINVAL);
+ if (skb_gso_ok(gso_skb, features | NETIF_F_GSO_ROBUST)) {
+ /* Packet is from an untrusted source, reset gso_segs. */
+ skb_shinfo(gso_skb)->gso_segs = DIV_ROUND_UP(gso_skb->len - sizeof(*uh),
+ mss);
+ return NULL;
+ }
+
+ if (skb_shinfo(gso_skb)->gso_type & SKB_GSO_FRAGLIST)
+ return __udp_gso_segment_list(gso_skb, features, is_ipv6);
+
skb_pull(gso_skb, sizeof(*uh));
/* clear destructor to avoid skb_segment assigning it to tail */