diff mbox series

[net] net: more strict VIRTIO_NET_HDR_GSO_UDP_L4 validation

Message ID 20231011140126.800508-1-willemdebruijn.kernel@gmail.com (mailing list archive)
State Accepted
Commit fc8b2a619469378717e7270d2a4e1ef93c585f7a
Delegated to: Netdev Maintainers
Headers show
Series [net] net: more strict VIRTIO_NET_HDR_GSO_UDP_L4 validation | expand

Checks

Context Check Description
netdev/series_format success Single patches do not need cover letters
netdev/tree_selection success Clearly marked for net, async
netdev/fixes_present success Fixes tag present in non-next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 1365 this patch: 1365
netdev/cc_maintainers warning 3 maintainers not CCed: virtualization@lists.linux-foundation.org xuanzhuo@linux.alibaba.com mst@redhat.com
netdev/build_clang success Errors and warnings before: 1387 this patch: 1387
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success Fixes tag looks correct
netdev/build_allmodconfig_warn success Errors and warnings before: 1392 this patch: 1392
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 33 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Willem de Bruijn Oct. 11, 2023, 2:01 p.m. UTC
From: Willem de Bruijn <willemb@google.com>

Syzbot reported two new paths to hit an internal WARNING using the
new virtio gso type VIRTIO_NET_HDR_GSO_UDP_L4.

    RIP: 0010:skb_checksum_help+0x4a2/0x600 net/core/dev.c:3260
    skb len=64521 gso_size=344
and

    RIP: 0010:skb_warn_bad_offload+0x118/0x240 net/core/dev.c:3262

Older virtio types have historically had loose restrictions, leading
to many entirely impractical fuzzer generated packets causing
problems deep in the kernel stack. Ideally, we would have had strict
validation for all types from the start.

New virtio types can have tighter validation. Limit UDP GSO packets
inserted via virtio to the same limits imposed by the UDP_SEGMENT
socket interface:

1. must use checksum offload
2. checksum offload matches UDP header
3. no more segments than UDP_MAX_SEGMENTS
4. UDP GSO does not take modifier flags, notably SKB_GSO_TCP_ECN

Fixes: 860b7f27b8f7 ("linux/virtio_net.h: Support USO offload in vnet header.")
Reported-by: syzbot+01cdbc31e9c0ae9b33ac@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/netdev/0000000000005039270605eb0b7f@google.com/
Reported-by: syzbot+c99d835ff081ca30f986@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/netdev/0000000000005426680605eb0b9f@google.com/
Signed-off-by: Willem de Bruijn <willemb@google.com>
---
 include/linux/virtio_net.h | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

Comments

Eric Dumazet Oct. 11, 2023, 2:10 p.m. UTC | #1
On Wed, Oct 11, 2023 at 4:01 PM Willem de Bruijn
<willemdebruijn.kernel@gmail.com> wrote:
>
> From: Willem de Bruijn <willemb@google.com>
>
> Syzbot reported two new paths to hit an internal WARNING using the
> new virtio gso type VIRTIO_NET_HDR_GSO_UDP_L4.
>
>     RIP: 0010:skb_checksum_help+0x4a2/0x600 net/core/dev.c:3260
>     skb len=64521 gso_size=344
> and
>
>     RIP: 0010:skb_warn_bad_offload+0x118/0x240 net/core/dev.c:3262
>
> Older virtio types have historically had loose restrictions, leading
> to many entirely impractical fuzzer generated packets causing
> problems deep in the kernel stack. Ideally, we would have had strict
> validation for all types from the start.
>
> New virtio types can have tighter validation. Limit UDP GSO packets
> inserted via virtio to the same limits imposed by the UDP_SEGMENT
> socket interface:
>
> 1. must use checksum offload
> 2. checksum offload matches UDP header
> 3. no more segments than UDP_MAX_SEGMENTS
> 4. UDP GSO does not take modifier flags, notably SKB_GSO_TCP_ECN
>
> Fixes: 860b7f27b8f7 ("linux/virtio_net.h: Support USO offload in vnet header.")
> Reported-by: syzbot+01cdbc31e9c0ae9b33ac@syzkaller.appspotmail.com
> Closes: https://lore.kernel.org/netdev/0000000000005039270605eb0b7f@google.com/
> Reported-by: syzbot+c99d835ff081ca30f986@syzkaller.appspotmail.com
> Closes: https://lore.kernel.org/netdev/0000000000005426680605eb0b9f@google.com/
> Signed-off-by: Willem de Bruijn <willemb@google.com>
> ---
>  include/linux/virtio_net.h | 19 ++++++++++++++++---
>  1 file changed, 16 insertions(+), 3 deletions(-)
>
> diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
> index 7b4dd69555e49..27cc1d4643219 100644
> --- a/include/linux/virtio_net.h
> +++ b/include/linux/virtio_net.h
> @@ -3,8 +3,8 @@
>  #define _LINUX_VIRTIO_NET_H
>
>  #include <linux/if_vlan.h>
> +#include <linux/udp.h>
>  #include <uapi/linux/tcp.h>
> -#include <uapi/linux/udp.h>
>  #include <uapi/linux/virtio_net.h>
>
>  static inline bool virtio_net_hdr_match_proto(__be16 protocol, __u8 gso_type)
> @@ -151,9 +151,22 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
>                 unsigned int nh_off = p_off;
>                 struct skb_shared_info *shinfo = skb_shinfo(skb);
>
> -               /* UFO may not include transport header in gso_size. */
> -               if (gso_type & SKB_GSO_UDP)
> +               switch (gso_type & ~SKB_GSO_TCP_ECN) {
> +               case SKB_GSO_UDP:
> +                       /* UFO may not include transport header in gso_size. */
>                         nh_off -= thlen;
> +                       break;
> +               case SKB_GSO_UDP_L4:
> +                       if (!(hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM))
> +                               return -EINVAL;
> +                       if (skb->csum_offset != offsetof(struct udphdr, check))
> +                               return -EINVAL;
> +                       if (skb->len - p_off > gso_size * UDP_MAX_SEGMENTS)
> +                               return -EINVAL;
> +                       if (gso_type != SKB_GSO_UDP_L4)
> +                               return -EINVAL;
> +                       break;
> +               }
>

Very nice, thanks Willem !

Reviewed-by: Eric Dumazet <edumazet@google.com>
Jason Wang Oct. 12, 2023, 8 a.m. UTC | #2
On Wed, Oct 11, 2023 at 10:01 PM Willem de Bruijn
<willemdebruijn.kernel@gmail.com> wrote:
>
> From: Willem de Bruijn <willemb@google.com>
>
> Syzbot reported two new paths to hit an internal WARNING using the
> new virtio gso type VIRTIO_NET_HDR_GSO_UDP_L4.
>
>     RIP: 0010:skb_checksum_help+0x4a2/0x600 net/core/dev.c:3260
>     skb len=64521 gso_size=344
> and
>
>     RIP: 0010:skb_warn_bad_offload+0x118/0x240 net/core/dev.c:3262
>
> Older virtio types have historically had loose restrictions, leading
> to many entirely impractical fuzzer generated packets causing
> problems deep in the kernel stack. Ideally, we would have had strict
> validation for all types from the start.
>
> New virtio types can have tighter validation. Limit UDP GSO packets
> inserted via virtio to the same limits imposed by the UDP_SEGMENT
> socket interface:
>
> 1. must use checksum offload
> 2. checksum offload matches UDP header
> 3. no more segments than UDP_MAX_SEGMENTS
> 4. UDP GSO does not take modifier flags, notably SKB_GSO_TCP_ECN
>
> Fixes: 860b7f27b8f7 ("linux/virtio_net.h: Support USO offload in vnet header.")
> Reported-by: syzbot+01cdbc31e9c0ae9b33ac@syzkaller.appspotmail.com
> Closes: https://lore.kernel.org/netdev/0000000000005039270605eb0b7f@google.com/
> Reported-by: syzbot+c99d835ff081ca30f986@syzkaller.appspotmail.com
> Closes: https://lore.kernel.org/netdev/0000000000005426680605eb0b9f@google.com/
> Signed-off-by: Willem de Bruijn <willemb@google.com>
> ---
>  include/linux/virtio_net.h | 19 ++++++++++++++++---
>  1 file changed, 16 insertions(+), 3 deletions(-)
>
> diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
> index 7b4dd69555e49..27cc1d4643219 100644
> --- a/include/linux/virtio_net.h
> +++ b/include/linux/virtio_net.h
> @@ -3,8 +3,8 @@
>  #define _LINUX_VIRTIO_NET_H
>
>  #include <linux/if_vlan.h>
> +#include <linux/udp.h>
>  #include <uapi/linux/tcp.h>
> -#include <uapi/linux/udp.h>
>  #include <uapi/linux/virtio_net.h>
>
>  static inline bool virtio_net_hdr_match_proto(__be16 protocol, __u8 gso_type)
> @@ -151,9 +151,22 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
>                 unsigned int nh_off = p_off;
>                 struct skb_shared_info *shinfo = skb_shinfo(skb);
>
> -               /* UFO may not include transport header in gso_size. */
> -               if (gso_type & SKB_GSO_UDP)
> +               switch (gso_type & ~SKB_GSO_TCP_ECN) {
> +               case SKB_GSO_UDP:
> +                       /* UFO may not include transport header in gso_size. */
>                         nh_off -= thlen;
> +                       break;
> +               case SKB_GSO_UDP_L4:
> +                       if (!(hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM))
> +                               return -EINVAL;
> +                       if (skb->csum_offset != offsetof(struct udphdr, check))
> +                               return -EINVAL;
> +                       if (skb->len - p_off > gso_size * UDP_MAX_SEGMENTS)
> +                               return -EINVAL;

Acked-by: Jason Wang <jasowang@redhat.com>

But a question comes into my mind: whether the udp max segments should
be part of the virtio ABI or not.

Otherwise guests may notice subtle differences after migration?

Thanks


> +                       if (gso_type != SKB_GSO_UDP_L4)
> +                               return -EINVAL;
> +                       break;
> +               }
>
>                 /* Kernel has a special handling for GSO_BY_FRAGS. */
>                 if (gso_size == GSO_BY_FRAGS)
> --
> 2.42.0.609.gbb76f46606-goog
>
Willem de Bruijn Oct. 12, 2023, 12:29 p.m. UTC | #3
On Thu, Oct 12, 2023 at 4:00 AM Jason Wang <jasowang@redhat.com> wrote:
>
> On Wed, Oct 11, 2023 at 10:01 PM Willem de Bruijn
> <willemdebruijn.kernel@gmail.com> wrote:
> >
> > From: Willem de Bruijn <willemb@google.com>
> >
> > Syzbot reported two new paths to hit an internal WARNING using the
> > new virtio gso type VIRTIO_NET_HDR_GSO_UDP_L4.
> >
> >     RIP: 0010:skb_checksum_help+0x4a2/0x600 net/core/dev.c:3260
> >     skb len=64521 gso_size=344
> > and
> >
> >     RIP: 0010:skb_warn_bad_offload+0x118/0x240 net/core/dev.c:3262
> >
> > Older virtio types have historically had loose restrictions, leading
> > to many entirely impractical fuzzer generated packets causing
> > problems deep in the kernel stack. Ideally, we would have had strict
> > validation for all types from the start.
> >
> > New virtio types can have tighter validation. Limit UDP GSO packets
> > inserted via virtio to the same limits imposed by the UDP_SEGMENT
> > socket interface:
> >
> > 1. must use checksum offload
> > 2. checksum offload matches UDP header
> > 3. no more segments than UDP_MAX_SEGMENTS
> > 4. UDP GSO does not take modifier flags, notably SKB_GSO_TCP_ECN
> >
> > Fixes: 860b7f27b8f7 ("linux/virtio_net.h: Support USO offload in vnet header.")
> > Reported-by: syzbot+01cdbc31e9c0ae9b33ac@syzkaller.appspotmail.com
> > Closes: https://lore.kernel.org/netdev/0000000000005039270605eb0b7f@google.com/
> > Reported-by: syzbot+c99d835ff081ca30f986@syzkaller.appspotmail.com
> > Closes: https://lore.kernel.org/netdev/0000000000005426680605eb0b9f@google.com/
> > Signed-off-by: Willem de Bruijn <willemb@google.com>
> > ---
> >  include/linux/virtio_net.h | 19 ++++++++++++++++---
> >  1 file changed, 16 insertions(+), 3 deletions(-)
> >
> > diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
> > index 7b4dd69555e49..27cc1d4643219 100644
> > --- a/include/linux/virtio_net.h
> > +++ b/include/linux/virtio_net.h
> > @@ -3,8 +3,8 @@
> >  #define _LINUX_VIRTIO_NET_H
> >
> >  #include <linux/if_vlan.h>
> > +#include <linux/udp.h>
> >  #include <uapi/linux/tcp.h>
> > -#include <uapi/linux/udp.h>
> >  #include <uapi/linux/virtio_net.h>
> >
> >  static inline bool virtio_net_hdr_match_proto(__be16 protocol, __u8 gso_type)
> > @@ -151,9 +151,22 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
> >                 unsigned int nh_off = p_off;
> >                 struct skb_shared_info *shinfo = skb_shinfo(skb);
> >
> > -               /* UFO may not include transport header in gso_size. */
> > -               if (gso_type & SKB_GSO_UDP)
> > +               switch (gso_type & ~SKB_GSO_TCP_ECN) {
> > +               case SKB_GSO_UDP:
> > +                       /* UFO may not include transport header in gso_size. */
> >                         nh_off -= thlen;
> > +                       break;
> > +               case SKB_GSO_UDP_L4:
> > +                       if (!(hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM))
> > +                               return -EINVAL;
> > +                       if (skb->csum_offset != offsetof(struct udphdr, check))
> > +                               return -EINVAL;
> > +                       if (skb->len - p_off > gso_size * UDP_MAX_SEGMENTS)
> > +                               return -EINVAL;
>
> Acked-by: Jason Wang <jasowang@redhat.com>
>
> But a question comes into my mind: whether the udp max segments should
> be part of the virtio ABI or not.

Implicitly it is part of the ABI, but so are other sensible
limitations, such as MAX_SKB_FRAGS. The limit was chosen high enough
to be unlikely to be a barrier to normal segmentation operations.
Jason Wang Oct. 13, 2023, 1:30 a.m. UTC | #4
On Thu, Oct 12, 2023 at 8:29 PM Willem de Bruijn
<willemdebruijn.kernel@gmail.com> wrote:
>
> On Thu, Oct 12, 2023 at 4:00 AM Jason Wang <jasowang@redhat.com> wrote:
> >
> > On Wed, Oct 11, 2023 at 10:01 PM Willem de Bruijn
> > <willemdebruijn.kernel@gmail.com> wrote:
> > >
> > > From: Willem de Bruijn <willemb@google.com>
> > >
> > > Syzbot reported two new paths to hit an internal WARNING using the
> > > new virtio gso type VIRTIO_NET_HDR_GSO_UDP_L4.
> > >
> > >     RIP: 0010:skb_checksum_help+0x4a2/0x600 net/core/dev.c:3260
> > >     skb len=64521 gso_size=344
> > > and
> > >
> > >     RIP: 0010:skb_warn_bad_offload+0x118/0x240 net/core/dev.c:3262
> > >
> > > Older virtio types have historically had loose restrictions, leading
> > > to many entirely impractical fuzzer generated packets causing
> > > problems deep in the kernel stack. Ideally, we would have had strict
> > > validation for all types from the start.
> > >
> > > New virtio types can have tighter validation. Limit UDP GSO packets
> > > inserted via virtio to the same limits imposed by the UDP_SEGMENT
> > > socket interface:
> > >
> > > 1. must use checksum offload
> > > 2. checksum offload matches UDP header
> > > 3. no more segments than UDP_MAX_SEGMENTS
> > > 4. UDP GSO does not take modifier flags, notably SKB_GSO_TCP_ECN
> > >
> > > Fixes: 860b7f27b8f7 ("linux/virtio_net.h: Support USO offload in vnet header.")
> > > Reported-by: syzbot+01cdbc31e9c0ae9b33ac@syzkaller.appspotmail.com
> > > Closes: https://lore.kernel.org/netdev/0000000000005039270605eb0b7f@google.com/
> > > Reported-by: syzbot+c99d835ff081ca30f986@syzkaller.appspotmail.com
> > > Closes: https://lore.kernel.org/netdev/0000000000005426680605eb0b9f@google.com/
> > > Signed-off-by: Willem de Bruijn <willemb@google.com>
> > > ---
> > >  include/linux/virtio_net.h | 19 ++++++++++++++++---
> > >  1 file changed, 16 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
> > > index 7b4dd69555e49..27cc1d4643219 100644
> > > --- a/include/linux/virtio_net.h
> > > +++ b/include/linux/virtio_net.h
> > > @@ -3,8 +3,8 @@
> > >  #define _LINUX_VIRTIO_NET_H
> > >
> > >  #include <linux/if_vlan.h>
> > > +#include <linux/udp.h>
> > >  #include <uapi/linux/tcp.h>
> > > -#include <uapi/linux/udp.h>
> > >  #include <uapi/linux/virtio_net.h>
> > >
> > >  static inline bool virtio_net_hdr_match_proto(__be16 protocol, __u8 gso_type)
> > > @@ -151,9 +151,22 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
> > >                 unsigned int nh_off = p_off;
> > >                 struct skb_shared_info *shinfo = skb_shinfo(skb);
> > >
> > > -               /* UFO may not include transport header in gso_size. */
> > > -               if (gso_type & SKB_GSO_UDP)
> > > +               switch (gso_type & ~SKB_GSO_TCP_ECN) {
> > > +               case SKB_GSO_UDP:
> > > +                       /* UFO may not include transport header in gso_size. */
> > >                         nh_off -= thlen;
> > > +                       break;
> > > +               case SKB_GSO_UDP_L4:
> > > +                       if (!(hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM))
> > > +                               return -EINVAL;
> > > +                       if (skb->csum_offset != offsetof(struct udphdr, check))
> > > +                               return -EINVAL;
> > > +                       if (skb->len - p_off > gso_size * UDP_MAX_SEGMENTS)
> > > +                               return -EINVAL;
> >
> > Acked-by: Jason Wang <jasowang@redhat.com>
> >
> > But a question comes into my mind: whether the udp max segments should
> > be part of the virtio ABI or not.
>
> Implicitly it is part of the ABI, but so are other sensible
> limitations, such as MAX_SKB_FRAGS.

There's no easy to detect things like MAX_SKB_FRAGS or anything I miss
here? For example, guests can send a packet with s/g more than
MAX_SKB_FRAGS, TUN can arrange the skb allocation to make sure it
doesn't exceed the limitation. This is not the case for
UDP_MAX_SEGMENTS.

Thanks

> The limit was chosen high enough
> to be unlikely to be a barrier to normal segmentation operations.
>
Willem de Bruijn Oct. 13, 2023, 11:40 a.m. UTC | #5
On Thu, Oct 12, 2023 at 9:30 PM Jason Wang <jasowang@redhat.com> wrote:
>
> On Thu, Oct 12, 2023 at 8:29 PM Willem de Bruijn
> <willemdebruijn.kernel@gmail.com> wrote:
> >
> > On Thu, Oct 12, 2023 at 4:00 AM Jason Wang <jasowang@redhat.com> wrote:
> > >
> > > On Wed, Oct 11, 2023 at 10:01 PM Willem de Bruijn
> > > <willemdebruijn.kernel@gmail.com> wrote:
> > > >
> > > > From: Willem de Bruijn <willemb@google.com>
> > > >
> > > > Syzbot reported two new paths to hit an internal WARNING using the
> > > > new virtio gso type VIRTIO_NET_HDR_GSO_UDP_L4.
> > > >
> > > >     RIP: 0010:skb_checksum_help+0x4a2/0x600 net/core/dev.c:3260
> > > >     skb len=64521 gso_size=344
> > > > and
> > > >
> > > >     RIP: 0010:skb_warn_bad_offload+0x118/0x240 net/core/dev.c:3262
> > > >
> > > > Older virtio types have historically had loose restrictions, leading
> > > > to many entirely impractical fuzzer generated packets causing
> > > > problems deep in the kernel stack. Ideally, we would have had strict
> > > > validation for all types from the start.
> > > >
> > > > New virtio types can have tighter validation. Limit UDP GSO packets
> > > > inserted via virtio to the same limits imposed by the UDP_SEGMENT
> > > > socket interface:
> > > >
> > > > 1. must use checksum offload
> > > > 2. checksum offload matches UDP header
> > > > 3. no more segments than UDP_MAX_SEGMENTS
> > > > 4. UDP GSO does not take modifier flags, notably SKB_GSO_TCP_ECN
> > > >
> > > > Fixes: 860b7f27b8f7 ("linux/virtio_net.h: Support USO offload in vnet header.")
> > > > Reported-by: syzbot+01cdbc31e9c0ae9b33ac@syzkaller.appspotmail.com
> > > > Closes: https://lore.kernel.org/netdev/0000000000005039270605eb0b7f@google.com/
> > > > Reported-by: syzbot+c99d835ff081ca30f986@syzkaller.appspotmail.com
> > > > Closes: https://lore.kernel.org/netdev/0000000000005426680605eb0b9f@google.com/
> > > > Signed-off-by: Willem de Bruijn <willemb@google.com>
> > > > ---
> > > >  include/linux/virtio_net.h | 19 ++++++++++++++++---
> > > >  1 file changed, 16 insertions(+), 3 deletions(-)
> > > >
> > > > diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
> > > > index 7b4dd69555e49..27cc1d4643219 100644
> > > > --- a/include/linux/virtio_net.h
> > > > +++ b/include/linux/virtio_net.h
> > > > @@ -3,8 +3,8 @@
> > > >  #define _LINUX_VIRTIO_NET_H
> > > >
> > > >  #include <linux/if_vlan.h>
> > > > +#include <linux/udp.h>
> > > >  #include <uapi/linux/tcp.h>
> > > > -#include <uapi/linux/udp.h>
> > > >  #include <uapi/linux/virtio_net.h>
> > > >
> > > >  static inline bool virtio_net_hdr_match_proto(__be16 protocol, __u8 gso_type)
> > > > @@ -151,9 +151,22 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
> > > >                 unsigned int nh_off = p_off;
> > > >                 struct skb_shared_info *shinfo = skb_shinfo(skb);
> > > >
> > > > -               /* UFO may not include transport header in gso_size. */
> > > > -               if (gso_type & SKB_GSO_UDP)
> > > > +               switch (gso_type & ~SKB_GSO_TCP_ECN) {
> > > > +               case SKB_GSO_UDP:
> > > > +                       /* UFO may not include transport header in gso_size. */
> > > >                         nh_off -= thlen;
> > > > +                       break;
> > > > +               case SKB_GSO_UDP_L4:
> > > > +                       if (!(hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM))
> > > > +                               return -EINVAL;
> > > > +                       if (skb->csum_offset != offsetof(struct udphdr, check))
> > > > +                               return -EINVAL;
> > > > +                       if (skb->len - p_off > gso_size * UDP_MAX_SEGMENTS)
> > > > +                               return -EINVAL;
> > >
> > > Acked-by: Jason Wang <jasowang@redhat.com>
> > >
> > > But a question comes into my mind: whether the udp max segments should
> > > be part of the virtio ABI or not.
> >
> > Implicitly it is part of the ABI, but so are other sensible
> > limitations, such as MAX_SKB_FRAGS.
>
> There's no easy to detect things like MAX_SKB_FRAGS or anything I miss
> here? For example, guests can send a packet with s/g more than
> MAX_SKB_FRAGS, TUN can arrange the skb allocation to make sure it
> doesn't exceed the limitation. This is not the case for
> UDP_MAX_SEGMENTS.

Perhaps MAX_SKB_FRAGS is not the best example. But there are other
conditions that are discoverable by validation returning an error when
outside the bounds of normal operation.

UDP_MAX_SEGMENTS is also not explicitly exposed to UDP_SEGMENT socket
users, without issues.

If absolutely needed, the boundary can be detected through probing.
But it should not be needed as chosen to be well outside normal
operating range.

A secondary benefit is that future kernels can relax (but not tighten)
the restriction if needed. The current limit was chosen with the usual
64KB / 1500B operating default in mind. If we would extend BIGTCP to
UDP, the existing limit of 64 might need relaxing (for both virtio and
sockets simultaneously). Anything ABI is set in stone, best to avoid
if not strictly necessary.
patchwork-bot+netdevbpf@kernel.org Oct. 15, 2023, 7 p.m. UTC | #6
Hello:

This patch was applied to netdev/net.git (main)
by David S. Miller <davem@davemloft.net>:

On Wed, 11 Oct 2023 10:01:14 -0400 you wrote:
> From: Willem de Bruijn <willemb@google.com>
> 
> Syzbot reported two new paths to hit an internal WARNING using the
> new virtio gso type VIRTIO_NET_HDR_GSO_UDP_L4.
> 
>     RIP: 0010:skb_checksum_help+0x4a2/0x600 net/core/dev.c:3260
>     skb len=64521 gso_size=344
> and
> 
> [...]

Here is the summary with links:
  - [net] net: more strict VIRTIO_NET_HDR_GSO_UDP_L4 validation
    https://git.kernel.org/netdev/net/c/fc8b2a619469

You are awesome, thank you!
Jason Wang Oct. 16, 2023, 6:39 a.m. UTC | #7
On Fri, Oct 13, 2023 at 7:40 PM Willem de Bruijn
<willemdebruijn.kernel@gmail.com> wrote:
>
> On Thu, Oct 12, 2023 at 9:30 PM Jason Wang <jasowang@redhat.com> wrote:
> >
> > On Thu, Oct 12, 2023 at 8:29 PM Willem de Bruijn
> > <willemdebruijn.kernel@gmail.com> wrote:
> > >
> > > On Thu, Oct 12, 2023 at 4:00 AM Jason Wang <jasowang@redhat.com> wrote:
> > > >
> > > > On Wed, Oct 11, 2023 at 10:01 PM Willem de Bruijn
> > > > <willemdebruijn.kernel@gmail.com> wrote:
> > > > >
> > > > > From: Willem de Bruijn <willemb@google.com>
> > > > >
> > > > > Syzbot reported two new paths to hit an internal WARNING using the
> > > > > new virtio gso type VIRTIO_NET_HDR_GSO_UDP_L4.
> > > > >
> > > > >     RIP: 0010:skb_checksum_help+0x4a2/0x600 net/core/dev.c:3260
> > > > >     skb len=64521 gso_size=344
> > > > > and
> > > > >
> > > > >     RIP: 0010:skb_warn_bad_offload+0x118/0x240 net/core/dev.c:3262
> > > > >
> > > > > Older virtio types have historically had loose restrictions, leading
> > > > > to many entirely impractical fuzzer generated packets causing
> > > > > problems deep in the kernel stack. Ideally, we would have had strict
> > > > > validation for all types from the start.
> > > > >
> > > > > New virtio types can have tighter validation. Limit UDP GSO packets
> > > > > inserted via virtio to the same limits imposed by the UDP_SEGMENT
> > > > > socket interface:
> > > > >
> > > > > 1. must use checksum offload
> > > > > 2. checksum offload matches UDP header
> > > > > 3. no more segments than UDP_MAX_SEGMENTS
> > > > > 4. UDP GSO does not take modifier flags, notably SKB_GSO_TCP_ECN
> > > > >
> > > > > Fixes: 860b7f27b8f7 ("linux/virtio_net.h: Support USO offload in vnet header.")
> > > > > Reported-by: syzbot+01cdbc31e9c0ae9b33ac@syzkaller.appspotmail.com
> > > > > Closes: https://lore.kernel.org/netdev/0000000000005039270605eb0b7f@google.com/
> > > > > Reported-by: syzbot+c99d835ff081ca30f986@syzkaller.appspotmail.com
> > > > > Closes: https://lore.kernel.org/netdev/0000000000005426680605eb0b9f@google.com/
> > > > > Signed-off-by: Willem de Bruijn <willemb@google.com>
> > > > > ---
> > > > >  include/linux/virtio_net.h | 19 ++++++++++++++++---
> > > > >  1 file changed, 16 insertions(+), 3 deletions(-)
> > > > >
> > > > > diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
> > > > > index 7b4dd69555e49..27cc1d4643219 100644
> > > > > --- a/include/linux/virtio_net.h
> > > > > +++ b/include/linux/virtio_net.h
> > > > > @@ -3,8 +3,8 @@
> > > > >  #define _LINUX_VIRTIO_NET_H
> > > > >
> > > > >  #include <linux/if_vlan.h>
> > > > > +#include <linux/udp.h>
> > > > >  #include <uapi/linux/tcp.h>
> > > > > -#include <uapi/linux/udp.h>
> > > > >  #include <uapi/linux/virtio_net.h>
> > > > >
> > > > >  static inline bool virtio_net_hdr_match_proto(__be16 protocol, __u8 gso_type)
> > > > > @@ -151,9 +151,22 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
> > > > >                 unsigned int nh_off = p_off;
> > > > >                 struct skb_shared_info *shinfo = skb_shinfo(skb);
> > > > >
> > > > > -               /* UFO may not include transport header in gso_size. */
> > > > > -               if (gso_type & SKB_GSO_UDP)
> > > > > +               switch (gso_type & ~SKB_GSO_TCP_ECN) {
> > > > > +               case SKB_GSO_UDP:
> > > > > +                       /* UFO may not include transport header in gso_size. */
> > > > >                         nh_off -= thlen;
> > > > > +                       break;
> > > > > +               case SKB_GSO_UDP_L4:
> > > > > +                       if (!(hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM))
> > > > > +                               return -EINVAL;
> > > > > +                       if (skb->csum_offset != offsetof(struct udphdr, check))
> > > > > +                               return -EINVAL;
> > > > > +                       if (skb->len - p_off > gso_size * UDP_MAX_SEGMENTS)
> > > > > +                               return -EINVAL;
> > > >
> > > > Acked-by: Jason Wang <jasowang@redhat.com>
> > > >
> > > > But a question comes into my mind: whether the udp max segments should
> > > > be part of the virtio ABI or not.
> > >
> > > Implicitly it is part of the ABI, but so are other sensible
> > > limitations, such as MAX_SKB_FRAGS.
> >
> > There's no easy to detect things like MAX_SKB_FRAGS or anything I miss
> > here? For example, guests can send a packet with s/g more than
> > MAX_SKB_FRAGS, TUN can arrange the skb allocation to make sure it
> > doesn't exceed the limitation. This is not the case for
> > UDP_MAX_SEGMENTS.
>
> Perhaps MAX_SKB_FRAGS is not the best example. But there are other
> conditions that are discoverable by validation returning an error when
> outside the bounds of normal operation.
>
> UDP_MAX_SEGMENTS is also not explicitly exposed to UDP_SEGMENT socket
> users, without issues.
>
> If absolutely needed, the boundary can be detected through probing.

See above, probing can only be done during driver probe.

> But it should not be needed as chosen to be well outside normal
> operating range.
>
> A secondary benefit is that future kernels can relax (but not tighten)
> the restriction if needed. The current limit was chosen with the usual
> 64KB / 1500B operating default in mind. If we would extend BIGTCP to
> UDP, the existing limit of 64 might need relaxing (for both virtio and
> sockets simultaneously). Anything ABI is set in stone, best to avoid
> if not strictly necessary.

The main concern is the migration, if we migrate from a Linux
hypervisor to another. Guests notice the difference in the limitation.

Thanks

>
diff mbox series

Patch

diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
index 7b4dd69555e49..27cc1d4643219 100644
--- a/include/linux/virtio_net.h
+++ b/include/linux/virtio_net.h
@@ -3,8 +3,8 @@ 
 #define _LINUX_VIRTIO_NET_H
 
 #include <linux/if_vlan.h>
+#include <linux/udp.h>
 #include <uapi/linux/tcp.h>
-#include <uapi/linux/udp.h>
 #include <uapi/linux/virtio_net.h>
 
 static inline bool virtio_net_hdr_match_proto(__be16 protocol, __u8 gso_type)
@@ -151,9 +151,22 @@  static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
 		unsigned int nh_off = p_off;
 		struct skb_shared_info *shinfo = skb_shinfo(skb);
 
-		/* UFO may not include transport header in gso_size. */
-		if (gso_type & SKB_GSO_UDP)
+		switch (gso_type & ~SKB_GSO_TCP_ECN) {
+		case SKB_GSO_UDP:
+			/* UFO may not include transport header in gso_size. */
 			nh_off -= thlen;
+			break;
+		case SKB_GSO_UDP_L4:
+			if (!(hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM))
+				return -EINVAL;
+			if (skb->csum_offset != offsetof(struct udphdr, check))
+				return -EINVAL;
+			if (skb->len - p_off > gso_size * UDP_MAX_SEGMENTS)
+				return -EINVAL;
+			if (gso_type != SKB_GSO_UDP_L4)
+				return -EINVAL;
+			break;
+		}
 
 		/* Kernel has a special handling for GSO_BY_FRAGS. */
 		if (gso_size == GSO_BY_FRAGS)