| From 6dd912f82680761d8fb6b1bb274a69d4c7010988 Mon Sep 17 00:00:00 2001 |
| From: Willem de Bruijn <willemb@google.com> |
| Date: Mon, 25 May 2020 15:07:40 -0400 |
| Subject: [PATCH] net: check untrusted gso_size at kernel entry |
| |
| commit 6dd912f82680761d8fb6b1bb274a69d4c7010988 upstream. |
| |
| Syzkaller again found a path to a kernel crash through bad gso input: |
| a packet with gso size exceeding len. |
| |
| These packets are dropped in tcp_gso_segment and udp[46]_ufo_fragment. |
| But they may affect gso size calculations earlier in the path. |
| |
| Now that we have thlen as of commit 9274124f023b ("net: stricter |
| validation of untrusted gso packets"), check gso_size at entry too. |
| |
| Fixes: bfd5f4a3d605 ("packet: Add GSO/csum offload support.") |
| Reported-by: syzbot <syzkaller@googlegroups.com> |
| Signed-off-by: Willem de Bruijn <willemb@google.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| |
| diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h |
| index 6f6ade63b04c..88997022a4b5 100644 |
| --- a/include/linux/virtio_net.h |
| +++ b/include/linux/virtio_net.h |
| @@ -31,6 +31,7 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb, |
| { |
| unsigned int gso_type = 0; |
| unsigned int thlen = 0; |
| + unsigned int p_off = 0; |
| unsigned int ip_proto; |
| |
| if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) { |
| @@ -68,7 +69,8 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb, |
| if (!skb_partial_csum_set(skb, start, off)) |
| return -EINVAL; |
| |
| - if (skb_transport_offset(skb) + thlen > skb_headlen(skb)) |
| + p_off = skb_transport_offset(skb) + thlen; |
| + if (p_off > skb_headlen(skb)) |
| return -EINVAL; |
| } else { |
| /* gso packets without NEEDS_CSUM do not set transport_offset. |
| @@ -92,17 +94,25 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb, |
| return -EINVAL; |
| } |
| |
| - if (keys.control.thoff + thlen > skb_headlen(skb) || |
| + p_off = keys.control.thoff + thlen; |
| + if (p_off > skb_headlen(skb) || |
| keys.basic.ip_proto != ip_proto) |
| return -EINVAL; |
| |
| skb_set_transport_header(skb, keys.control.thoff); |
| + } else if (gso_type) { |
| + p_off = thlen; |
| + if (p_off > skb_headlen(skb)) |
| + return -EINVAL; |
| } |
| } |
| |
| if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) { |
| u16 gso_size = __virtio16_to_cpu(little_endian, hdr->gso_size); |
| |
| + if (skb->len - p_off <= gso_size) |
| + return -EINVAL; |
| + |
| skb_shinfo(skb)->gso_size = gso_size; |
| skb_shinfo(skb)->gso_type = gso_type; |
| |
| -- |
| 2.7.4 |
| |