qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/1] virtio-net: fix bug 1451 aka "assert(!virtio_net_get_subqueue(nc)->async_tx.elem); "
@ 2024-04-05 11:20 Alexey Dobriyan
  2024-04-08  7:26 ` Jason Wang
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Alexey Dobriyan @ 2024-04-05 11:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: adobriyan, adobriyan, mst, jasowang, vsementsov

Don't send zero length packets in virtio_net_flush_tx().

Reproducer from https://gitlab.com/qemu-project/qemu/-/issues/1451
creates small packet (1 segment, len = 10 == n->guest_hdr_len),
destroys queue.

"if (n->host_hdr_len != n->guest_hdr_len)" is triggered, if body creates
zero length/zero segment packet, because there is nothing after guest
header.

qemu_sendv_packet_async() tries to send it.

slirp discards it because it is smaller than Ethernet header,
but returns 0.

0 length is propagated upwards and is interpreted as "packet has been sent"
which is terrible because queue is being destroyed, nothing has been sent,
nobody is waiting for TX to complete and assert it triggered.

Signed-off-by: Alexey Dobriyan <adobriyan@yandex-team.ru>
---
 hw/net/virtio-net.c | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 58014a92ad..258633f885 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -2765,18 +2765,14 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
         out_sg = elem->out_sg;
         if (out_num < 1) {
             virtio_error(vdev, "virtio-net header not in first element");
-            virtqueue_detach_element(q->tx_vq, elem, 0);
-            g_free(elem);
-            return -EINVAL;
+            goto detach;
         }
 
         if (n->has_vnet_hdr) {
             if (iov_to_buf(out_sg, out_num, 0, &vhdr, n->guest_hdr_len) <
                 n->guest_hdr_len) {
                 virtio_error(vdev, "virtio-net header incorrect");
-                virtqueue_detach_element(q->tx_vq, elem, 0);
-                g_free(elem);
-                return -EINVAL;
+                goto detach;
             }
             if (n->needs_vnet_hdr_swap) {
                 virtio_net_hdr_swap(vdev, (void *) &vhdr);
@@ -2807,6 +2803,11 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
                              n->guest_hdr_len, -1);
             out_num = sg_num;
             out_sg = sg;
+
+            if (iov_size(out_sg, out_num) == 0) {
+                virtio_error(vdev, "virtio-net nothing to send");
+                goto detach;
+            }
         }
 
         ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index),
@@ -2827,6 +2828,11 @@ drop:
         }
     }
     return num_packets;
+
+detach:
+    virtqueue_detach_element(q->tx_vq, elem, 0);
+    g_free(elem);
+    return -EINVAL;
 }
 
 static void virtio_net_tx_timer(void *opaque);
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/1] virtio-net: fix bug 1451 aka "assert(!virtio_net_get_subqueue(nc)->async_tx.elem); "
  2024-04-05 11:20 Alexey Dobriyan
@ 2024-04-08  7:26 ` Jason Wang
  2024-04-09  6:01   ` [PATCH 1/1] virtio-net: fix bug 1451 aka "assert(!virtio_net_get_subqueue(nc)->async_tx.elem);" Alexey Dobriyan
  2024-04-09  6:49 ` Michael S. Tsirkin
  2024-04-09  6:51 ` Michael S. Tsirkin
  2 siblings, 1 reply; 9+ messages in thread
From: Jason Wang @ 2024-04-08  7:26 UTC (permalink / raw)
  To: Alexey Dobriyan; +Cc: qemu-devel, adobriyan, mst, vsementsov

On Fri, Apr 5, 2024 at 7:22 PM Alexey Dobriyan <adobriyan@yandex-team.ru> wrote:
>
> Don't send zero length packets in virtio_net_flush_tx().
>
> Reproducer from https://gitlab.com/qemu-project/qemu/-/issues/1451
> creates small packet (1 segment, len = 10 == n->guest_hdr_len),
> destroys queue.
>
> "if (n->host_hdr_len != n->guest_hdr_len)" is triggered, if body creates
> zero length/zero segment packet, because there is nothing after guest
> header.

And in this case host_hdr_len is 0.

>
> qemu_sendv_packet_async() tries to send it.
>
> slirp discards it because it is smaller than Ethernet header,
> but returns 0.
>
> 0 length is propagated upwards and is interpreted as "packet has been sent"
> which is terrible because queue is being destroyed, nothing has been sent,
> nobody is waiting for TX to complete and assert it triggered.
>
> Signed-off-by: Alexey Dobriyan <adobriyan@yandex-team.ru>
> ---
>  hw/net/virtio-net.c | 18 ++++++++++++------
>  1 file changed, 12 insertions(+), 6 deletions(-)
>
> diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
> index 58014a92ad..258633f885 100644
> --- a/hw/net/virtio-net.c
> +++ b/hw/net/virtio-net.c
> @@ -2765,18 +2765,14 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
>          out_sg = elem->out_sg;
>          if (out_num < 1) {
>              virtio_error(vdev, "virtio-net header not in first element");
> -            virtqueue_detach_element(q->tx_vq, elem, 0);
> -            g_free(elem);
> -            return -EINVAL;
> +            goto detach;
>          }
>
>          if (n->has_vnet_hdr) {
>              if (iov_to_buf(out_sg, out_num, 0, &vhdr, n->guest_hdr_len) <
>                  n->guest_hdr_len) {
>                  virtio_error(vdev, "virtio-net header incorrect");
> -                virtqueue_detach_element(q->tx_vq, elem, 0);
> -                g_free(elem);
> -                return -EINVAL;
> +                goto detach;
>              }
>              if (n->needs_vnet_hdr_swap) {
>                  virtio_net_hdr_swap(vdev, (void *) &vhdr);
> @@ -2807,6 +2803,11 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
>                               n->guest_hdr_len, -1);
>              out_num = sg_num;
>              out_sg = sg;
> +
> +            if (iov_size(out_sg, out_num) == 0) {
> +                virtio_error(vdev, "virtio-net nothing to send");
> +                goto detach;
> +            }

Nit, I think we can do this check before the iov_copy()?

Thanks

>          }
>
>          ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index),
> @@ -2827,6 +2828,11 @@ drop:
>          }
>      }
>      return num_packets;
> +
> +detach:
> +    virtqueue_detach_element(q->tx_vq, elem, 0);
> +    g_free(elem);
> +    return -EINVAL;
>  }
>
>  static void virtio_net_tx_timer(void *opaque);
> --
> 2.34.1
>



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/1] virtio-net: fix bug 1451 aka "assert(!virtio_net_get_subqueue(nc)->async_tx.elem);"
  2024-04-08  7:26 ` Jason Wang
@ 2024-04-09  6:01   ` Alexey Dobriyan
  0 siblings, 0 replies; 9+ messages in thread
From: Alexey Dobriyan @ 2024-04-09  6:01 UTC (permalink / raw)
  To: Jason Wang; +Cc: Alexey Dobriyan, qemu-devel, mst, vsementsov

On Mon, Apr 08, 2024 at 03:26:35PM +0800, Jason Wang wrote:
> On Fri, Apr 5, 2024 at 7:22 PM Alexey Dobriyan <adobriyan@yandex-team.ru> wrote:
> >
> > Don't send zero length packets in virtio_net_flush_tx().
> >
> > Reproducer from https://gitlab.com/qemu-project/qemu/-/issues/1451
> > creates small packet (1 segment, len = 10 == n->guest_hdr_len),
> > destroys queue.
> >
> > "if (n->host_hdr_len != n->guest_hdr_len)" is triggered, if body creates
> > zero length/zero segment packet, because there is nothing after guest
> > header.
> 
> And in this case host_hdr_len is 0.

Yes.

> > qemu_sendv_packet_async() tries to send it.
> >
> > slirp discards it because it is smaller than Ethernet header,
> > but returns 0.
> >
> > 0 length is propagated upwards and is interpreted as "packet has been sent"
> > which is terrible because queue is being destroyed, nothing has been sent,
> > nobody is waiting for TX to complete and assert it triggered.

> > @@ -2807,6 +2803,11 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
> >                               n->guest_hdr_len, -1);
> >              out_num = sg_num;
> >              out_sg = sg;
> > +
> > +            if (iov_size(out_sg, out_num) == 0) {
> > +                virtio_error(vdev, "virtio-net nothing to send");
> > +                goto detach;
> > +            }
> 
> Nit, I think we can do this check before the iov_copy()?

I'd rather check for "badness" right before emitting packet.

> >          }
> >
> >          ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index),
> > @@ -2827,6 +2828,11 @@ drop:
> >          }
> >      }
> >      return num_packets;
> > +
> > +detach:
> > +    virtqueue_detach_element(q->tx_vq, elem, 0);
> > +    g_free(elem);
> > +    return -EINVAL;
> >  }
> >
> >  static void virtio_net_tx_timer(void *opaque);
> > --
> > 2.34.1
> >
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/1] virtio-net: fix bug 1451 aka "assert(!virtio_net_get_subqueue(nc)->async_tx.elem);"
  2024-04-05 11:20 Alexey Dobriyan
  2024-04-08  7:26 ` Jason Wang
@ 2024-04-09  6:49 ` Michael S. Tsirkin
  2024-04-09  6:51 ` Michael S. Tsirkin
  2 siblings, 0 replies; 9+ messages in thread
From: Michael S. Tsirkin @ 2024-04-09  6:49 UTC (permalink / raw)
  To: Alexey Dobriyan; +Cc: qemu-devel, adobriyan, jasowang, vsementsov

On Fri, Apr 05, 2024 at 02:20:15PM +0300, Alexey Dobriyan wrote:
> Don't send zero length packets in virtio_net_flush_tx().
> 
> Reproducer from https://gitlab.com/qemu-project/qemu/-/issues/1451
> creates small packet (1 segment, len = 10 == n->guest_hdr_len),
> destroys queue.
> 
> "if (n->host_hdr_len != n->guest_hdr_len)" is triggered, if body creates
> zero length/zero segment packet, because there is nothing after guest
> header.
> 
> qemu_sendv_packet_async() tries to send it.
> 
> slirp discards it because it is smaller than Ethernet header,
> but returns 0.
> 
> 0 length is propagated upwards and is interpreted as "packet has been sent"
> which is terrible because queue is being destroyed, nothing has been sent,
> nobody is waiting for TX to complete and assert it triggered.
> 
> Signed-off-by: Alexey Dobriyan <adobriyan@yandex-team.ru>
> ---
>  hw/net/virtio-net.c | 18 ++++++++++++------
>  1 file changed, 12 insertions(+), 6 deletions(-)
> 
> diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
> index 58014a92ad..258633f885 100644
> --- a/hw/net/virtio-net.c
> +++ b/hw/net/virtio-net.c
> @@ -2765,18 +2765,14 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
>          out_sg = elem->out_sg;
>          if (out_num < 1) {
>              virtio_error(vdev, "virtio-net header not in first element");
> -            virtqueue_detach_element(q->tx_vq, elem, 0);
> -            g_free(elem);
> -            return -EINVAL;
> +            goto detach;
>          }
>  
>          if (n->has_vnet_hdr) {
>              if (iov_to_buf(out_sg, out_num, 0, &vhdr, n->guest_hdr_len) <
>                  n->guest_hdr_len) {
>                  virtio_error(vdev, "virtio-net header incorrect");
> -                virtqueue_detach_element(q->tx_vq, elem, 0);
> -                g_free(elem);
> -                return -EINVAL;
> +                goto detach;
>              }
>              if (n->needs_vnet_hdr_swap) {
>                  virtio_net_hdr_swap(vdev, (void *) &vhdr);
> @@ -2807,6 +2803,11 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
>                               n->guest_hdr_len, -1);
>              out_num = sg_num;
>              out_sg = sg;
> +
> +            if (iov_size(out_sg, out_num) == 0) {

calling iov_size an extra time on data path and scanning
a potentially long s/g list just so we can check
it's not 0 is not welcome, though.


won't the previous iov_copy return 0 in this case?

> +                virtio_error(vdev, "virtio-net nothing to send");
> +                goto detach;
> +            }
>          }
>  
>          ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index),
> @@ -2827,6 +2828,11 @@ drop:
>          }
>      }
>      return num_packets;
> +
> +detach:
> +    virtqueue_detach_element(q->tx_vq, elem, 0);
> +    g_free(elem);
> +    return -EINVAL;
>  }
>  
>  static void virtio_net_tx_timer(void *opaque);
> -- 
> 2.34.1



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/1] virtio-net: fix bug 1451 aka "assert(!virtio_net_get_subqueue(nc)->async_tx.elem);"
  2024-04-05 11:20 Alexey Dobriyan
  2024-04-08  7:26 ` Jason Wang
  2024-04-09  6:49 ` Michael S. Tsirkin
@ 2024-04-09  6:51 ` Michael S. Tsirkin
  2024-04-09 16:37   ` Alexey Dobriyan
  2 siblings, 1 reply; 9+ messages in thread
From: Michael S. Tsirkin @ 2024-04-09  6:51 UTC (permalink / raw)
  To: Alexey Dobriyan; +Cc: qemu-devel, adobriyan, jasowang, vsementsov

On Fri, Apr 05, 2024 at 02:20:15PM +0300, Alexey Dobriyan wrote:
> Don't send zero length packets in virtio_net_flush_tx().
> 
> Reproducer from https://gitlab.com/qemu-project/qemu/-/issues/1451
> creates small packet (1 segment, len = 10 == n->guest_hdr_len),
> destroys queue.
> 
> "if (n->host_hdr_len != n->guest_hdr_len)" is triggered, if body creates
> zero length/zero segment packet, because there is nothing after guest
> header.
> 
> qemu_sendv_packet_async() tries to send it.
> 
> slirp discards it because it is smaller than Ethernet header,
> but returns 0.

won't the same issue trigger with a 1 byte packet
as opposed to a 0 byte packet though?



> 0 length is propagated upwards and is interpreted as "packet has been sent"
> which is terrible because queue is being destroyed, nothing has been sent,
> nobody is waiting for TX to complete and assert it triggered.
> 
> Signed-off-by: Alexey Dobriyan <adobriyan@yandex-team.ru>
> ---
>  hw/net/virtio-net.c | 18 ++++++++++++------
>  1 file changed, 12 insertions(+), 6 deletions(-)
> 
> diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
> index 58014a92ad..258633f885 100644
> --- a/hw/net/virtio-net.c
> +++ b/hw/net/virtio-net.c
> @@ -2765,18 +2765,14 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
>          out_sg = elem->out_sg;
>          if (out_num < 1) {
>              virtio_error(vdev, "virtio-net header not in first element");
> -            virtqueue_detach_element(q->tx_vq, elem, 0);
> -            g_free(elem);
> -            return -EINVAL;
> +            goto detach;
>          }
>  
>          if (n->has_vnet_hdr) {
>              if (iov_to_buf(out_sg, out_num, 0, &vhdr, n->guest_hdr_len) <
>                  n->guest_hdr_len) {
>                  virtio_error(vdev, "virtio-net header incorrect");
> -                virtqueue_detach_element(q->tx_vq, elem, 0);
> -                g_free(elem);
> -                return -EINVAL;
> +                goto detach;
>              }
>              if (n->needs_vnet_hdr_swap) {
>                  virtio_net_hdr_swap(vdev, (void *) &vhdr);
> @@ -2807,6 +2803,11 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
>                               n->guest_hdr_len, -1);
>              out_num = sg_num;
>              out_sg = sg;
> +
> +            if (iov_size(out_sg, out_num) == 0) {
> +                virtio_error(vdev, "virtio-net nothing to send");
> +                goto detach;
> +            }
>          }
>  
>          ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index),
> @@ -2827,6 +2828,11 @@ drop:
>          }
>      }
>      return num_packets;
> +
> +detach:
> +    virtqueue_detach_element(q->tx_vq, elem, 0);
> +    g_free(elem);
> +    return -EINVAL;
>  }
>  
>  static void virtio_net_tx_timer(void *opaque);
> -- 
> 2.34.1



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/1] virtio-net: fix bug 1451 aka "assert(!virtio_net_get_subqueue(nc)->async_tx.elem);"
  2024-04-09  6:51 ` Michael S. Tsirkin
@ 2024-04-09 16:37   ` Alexey Dobriyan
  2024-04-09 16:41     ` Michael S. Tsirkin
  0 siblings, 1 reply; 9+ messages in thread
From: Alexey Dobriyan @ 2024-04-09 16:37 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: Alexey Dobriyan, qemu-devel, jasowang, vsementsov

On Tue, Apr 09, 2024 at 02:51:38AM -0400, Michael S. Tsirkin wrote:
> On Fri, Apr 05, 2024 at 02:20:15PM +0300, Alexey Dobriyan wrote:
> > Don't send zero length packets in virtio_net_flush_tx().
> > 
> > Reproducer from https://gitlab.com/qemu-project/qemu/-/issues/1451
> > creates small packet (1 segment, len = 10 == n->guest_hdr_len),
> > destroys queue.
> > 
> > "if (n->host_hdr_len != n->guest_hdr_len)" is triggered, if body creates
> > zero length/zero segment packet, because there is nothing after guest
> > header.
> > 
> > qemu_sendv_packet_async() tries to send it.
> > 
> > slirp discards it because it is smaller than Ethernet header,
> > but returns 0.
> 
> won't the same issue trigger with a 1 byte packet
> as opposed to a 0 byte packet though?

I don't think so. Only "return 0" is problematic from qemu_sendv_packet_async().
->deliver hooks are supposed to return total length, so 1 should be fine.

> > 0 length is propagated upwards and is interpreted as "packet has been sent"
> > which is terrible because queue is being destroyed, nothing has been sent,
> > nobody is waiting for TX to complete and assert it triggered.
> > 
> > Signed-off-by: Alexey Dobriyan <adobriyan@yandex-team.ru>
> > ---
> >  hw/net/virtio-net.c | 18 ++++++++++++------
> >  1 file changed, 12 insertions(+), 6 deletions(-)
> > 
> > diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
> > index 58014a92ad..258633f885 100644
> > --- a/hw/net/virtio-net.c
> > +++ b/hw/net/virtio-net.c
> > @@ -2765,18 +2765,14 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
> >          out_sg = elem->out_sg;
> >          if (out_num < 1) {
> >              virtio_error(vdev, "virtio-net header not in first element");
> > -            virtqueue_detach_element(q->tx_vq, elem, 0);
> > -            g_free(elem);
> > -            return -EINVAL;
> > +            goto detach;
> >          }
> >  
> >          if (n->has_vnet_hdr) {
> >              if (iov_to_buf(out_sg, out_num, 0, &vhdr, n->guest_hdr_len) <
> >                  n->guest_hdr_len) {
> >                  virtio_error(vdev, "virtio-net header incorrect");
> > -                virtqueue_detach_element(q->tx_vq, elem, 0);
> > -                g_free(elem);
> > -                return -EINVAL;
> > +                goto detach;
> >              }
> >              if (n->needs_vnet_hdr_swap) {
> >                  virtio_net_hdr_swap(vdev, (void *) &vhdr);
> > @@ -2807,6 +2803,11 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
> >                               n->guest_hdr_len, -1);
> >              out_num = sg_num;
> >              out_sg = sg;
> > +
> > +            if (iov_size(out_sg, out_num) == 0) {
> > +                virtio_error(vdev, "virtio-net nothing to send");
> > +                goto detach;
> > +            }
> >          }
> >  
> >          ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index),
> > @@ -2827,6 +2828,11 @@ drop:
> >          }
> >      }
> >      return num_packets;
> > +
> > +detach:
> > +    virtqueue_detach_element(q->tx_vq, elem, 0);
> > +    g_free(elem);
> > +    return -EINVAL;
> >  }
> >  
> >  static void virtio_net_tx_timer(void *opaque);
> > -- 
> > 2.34.1
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/1] virtio-net: fix bug 1451 aka "assert(!virtio_net_get_subqueue(nc)->async_tx.elem);"
  2024-04-09 16:37   ` Alexey Dobriyan
@ 2024-04-09 16:41     ` Michael S. Tsirkin
  2024-04-09 17:15       ` Alexey Dobriyan
  0 siblings, 1 reply; 9+ messages in thread
From: Michael S. Tsirkin @ 2024-04-09 16:41 UTC (permalink / raw)
  To: Alexey Dobriyan; +Cc: Alexey Dobriyan, qemu-devel, jasowang, vsementsov

On Tue, Apr 09, 2024 at 07:37:04PM +0300, Alexey Dobriyan wrote:
> On Tue, Apr 09, 2024 at 02:51:38AM -0400, Michael S. Tsirkin wrote:
> > On Fri, Apr 05, 2024 at 02:20:15PM +0300, Alexey Dobriyan wrote:
> > > Don't send zero length packets in virtio_net_flush_tx().
> > > 
> > > Reproducer from https://gitlab.com/qemu-project/qemu/-/issues/1451
> > > creates small packet (1 segment, len = 10 == n->guest_hdr_len),
> > > destroys queue.
> > > 
> > > "if (n->host_hdr_len != n->guest_hdr_len)" is triggered, if body creates
> > > zero length/zero segment packet, because there is nothing after guest
> > > header.
> > > 
> > > qemu_sendv_packet_async() tries to send it.
> > > 
> > > slirp discards it because it is smaller than Ethernet header,
> > > but returns 0.
> > 
> > won't the same issue trigger with a 1 byte packet
> > as opposed to a 0 byte packet though?
> 
> I don't think so. Only "return 0" is problematic from qemu_sendv_packet_async().
> ->deliver hooks are supposed to return total length, so 1 should be fine.


But you said it's smaller than Ethernet is the problem?

> > > 0 length is propagated upwards and is interpreted as "packet has been sent"
> > > which is terrible because queue is being destroyed, nothing has been sent,
> > > nobody is waiting for TX to complete and assert it triggered.
> > > 
> > > Signed-off-by: Alexey Dobriyan <adobriyan@yandex-team.ru>
> > > ---
> > >  hw/net/virtio-net.c | 18 ++++++++++++------
> > >  1 file changed, 12 insertions(+), 6 deletions(-)
> > > 
> > > diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
> > > index 58014a92ad..258633f885 100644
> > > --- a/hw/net/virtio-net.c
> > > +++ b/hw/net/virtio-net.c
> > > @@ -2765,18 +2765,14 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
> > >          out_sg = elem->out_sg;
> > >          if (out_num < 1) {
> > >              virtio_error(vdev, "virtio-net header not in first element");
> > > -            virtqueue_detach_element(q->tx_vq, elem, 0);
> > > -            g_free(elem);
> > > -            return -EINVAL;
> > > +            goto detach;
> > >          }
> > >  
> > >          if (n->has_vnet_hdr) {
> > >              if (iov_to_buf(out_sg, out_num, 0, &vhdr, n->guest_hdr_len) <
> > >                  n->guest_hdr_len) {
> > >                  virtio_error(vdev, "virtio-net header incorrect");
> > > -                virtqueue_detach_element(q->tx_vq, elem, 0);
> > > -                g_free(elem);
> > > -                return -EINVAL;
> > > +                goto detach;
> > >              }
> > >              if (n->needs_vnet_hdr_swap) {
> > >                  virtio_net_hdr_swap(vdev, (void *) &vhdr);
> > > @@ -2807,6 +2803,11 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
> > >                               n->guest_hdr_len, -1);
> > >              out_num = sg_num;
> > >              out_sg = sg;
> > > +
> > > +            if (iov_size(out_sg, out_num) == 0) {
> > > +                virtio_error(vdev, "virtio-net nothing to send");
> > > +                goto detach;
> > > +            }
> > >          }
> > >  
> > >          ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index),
> > > @@ -2827,6 +2828,11 @@ drop:
> > >          }
> > >      }
> > >      return num_packets;
> > > +
> > > +detach:
> > > +    virtqueue_detach_element(q->tx_vq, elem, 0);
> > > +    g_free(elem);
> > > +    return -EINVAL;
> > >  }
> > >  
> > >  static void virtio_net_tx_timer(void *opaque);
> > > -- 
> > > 2.34.1
> > 



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/1] virtio-net: fix bug 1451 aka "assert(!virtio_net_get_subqueue(nc)->async_tx.elem);"
  2024-04-09 16:41     ` Michael S. Tsirkin
@ 2024-04-09 17:15       ` Alexey Dobriyan
  0 siblings, 0 replies; 9+ messages in thread
From: Alexey Dobriyan @ 2024-04-09 17:15 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: Alexey Dobriyan, qemu-devel, jasowang, vsementsov

On Tue, Apr 09, 2024 at 12:41:39PM -0400, Michael S. Tsirkin wrote:
> On Tue, Apr 09, 2024 at 07:37:04PM +0300, Alexey Dobriyan wrote:
> > On Tue, Apr 09, 2024 at 02:51:38AM -0400, Michael S. Tsirkin wrote:
> > > On Fri, Apr 05, 2024 at 02:20:15PM +0300, Alexey Dobriyan wrote:
> > > > Don't send zero length packets in virtio_net_flush_tx().
> > > > 
> > > > Reproducer from https://gitlab.com/qemu-project/qemu/-/issues/1451
> > > > creates small packet (1 segment, len = 10 == n->guest_hdr_len),
> > > > destroys queue.
> > > > 
> > > > "if (n->host_hdr_len != n->guest_hdr_len)" is triggered, if body creates
> > > > zero length/zero segment packet, because there is nothing after guest
> > > > header.
> > > > 
> > > > qemu_sendv_packet_async() tries to send it.
> > > > 
> > > > slirp discards it because it is smaller than Ethernet header,
> > > > but returns 0.
> > > 
> > > won't the same issue trigger with a 1 byte packet
> > > as opposed to a 0 byte packet though?
> > 
> > I don't think so. Only "return 0" is problematic from qemu_sendv_packet_async().
> > ->deliver hooks are supposed to return total length, so 1 should be fine.
> 
> 
> But you said it's smaller than Ethernet is the problem?

No, the problem is iov_cnt=0 packet is being generated, which enters
qemu_sendv_packet_async(), discarded inside slirp driver, but
net_slirp_receive() returns 0 which is the length of meaningless packet.
which is propagated upwards.

> > > > 0 length is propagated upwards and is interpreted as "packet has been sent"
> > > > which is terrible because queue is being destroyed, nothing has been sent,
> > > > nobody is waiting for TX to complete and assert it triggered.
> > > > 
> > > > Signed-off-by: Alexey Dobriyan <adobriyan@yandex-team.ru>
> > > > ---
> > > >  hw/net/virtio-net.c | 18 ++++++++++++------
> > > >  1 file changed, 12 insertions(+), 6 deletions(-)
> > > > 
> > > > diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
> > > > index 58014a92ad..258633f885 100644
> > > > --- a/hw/net/virtio-net.c
> > > > +++ b/hw/net/virtio-net.c
> > > > @@ -2765,18 +2765,14 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
> > > >          out_sg = elem->out_sg;
> > > >          if (out_num < 1) {
> > > >              virtio_error(vdev, "virtio-net header not in first element");
> > > > -            virtqueue_detach_element(q->tx_vq, elem, 0);
> > > > -            g_free(elem);
> > > > -            return -EINVAL;
> > > > +            goto detach;
> > > >          }
> > > >  
> > > >          if (n->has_vnet_hdr) {
> > > >              if (iov_to_buf(out_sg, out_num, 0, &vhdr, n->guest_hdr_len) <
> > > >                  n->guest_hdr_len) {
> > > >                  virtio_error(vdev, "virtio-net header incorrect");
> > > > -                virtqueue_detach_element(q->tx_vq, elem, 0);
> > > > -                g_free(elem);
> > > > -                return -EINVAL;
> > > > +                goto detach;
> > > >              }
> > > >              if (n->needs_vnet_hdr_swap) {
> > > >                  virtio_net_hdr_swap(vdev, (void *) &vhdr);
> > > > @@ -2807,6 +2803,11 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
> > > >                               n->guest_hdr_len, -1);
> > > >              out_num = sg_num;
> > > >              out_sg = sg;
> > > > +
> > > > +            if (iov_size(out_sg, out_num) == 0) {
> > > > +                virtio_error(vdev, "virtio-net nothing to send");
> > > > +                goto detach;
> > > > +            }
> > > >          }
> > > >  
> > > >          ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index),
> > > > @@ -2827,6 +2828,11 @@ drop:
> > > >          }
> > > >      }
> > > >      return num_packets;
> > > > +
> > > > +detach:
> > > > +    virtqueue_detach_element(q->tx_vq, elem, 0);
> > > > +    g_free(elem);
> > > > +    return -EINVAL;
> > > >  }
> > > >  
> > > >  static void virtio_net_tx_timer(void *opaque);
> > > > -- 
> > > > 2.34.1
> > > 
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/1] virtio-net: fix bug 1451 aka "assert(!virtio_net_get_subqueue(nc)->async_tx.elem); "
@ 2024-04-09 19:05 Alexey Dobriyan
  0 siblings, 0 replies; 9+ messages in thread
From: Alexey Dobriyan @ 2024-04-09 19:05 UTC (permalink / raw)
  To: qemu-devel; +Cc: adobriyan, adobriyan, mst, jasowang, vsementsov

Reproducer from https://gitlab.com/qemu-project/qemu/-/issues/1451
creates small packet (1 segment, len=10 == n->guest_hdr_len),
destroys queue.

"if (n->host_hdr_len != n->guest_hdr_len)" is triggered. There is
nothing after guest header, if body creates zero length/zero segment packet.

qemu_sendv_packet_async() tries to send it.

Packet is discarded inside net_slirp_receive() which returns number of
sent bytes which is 0.

0 is propagated upwards and is interpreted as "zero bytes has been sent"
which is terrible because queue is being destroyed, nobody is waiting
for TX to complete, TX complete hook is _not_ called and
assert(async_tx.elem) is eventually triggered.

The fix is to discard such phantom packets in virtio_net_flush_tx().

Signed-off-by: Alexey Dobriyan <adobriyan@yandex-team.ru>
---
 hw/net/virtio-net.c | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 58014a92ad..e156f1d78c 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -2765,18 +2765,14 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
         out_sg = elem->out_sg;
         if (out_num < 1) {
             virtio_error(vdev, "virtio-net header not in first element");
-            virtqueue_detach_element(q->tx_vq, elem, 0);
-            g_free(elem);
-            return -EINVAL;
+            goto detach;
         }
 
         if (n->has_vnet_hdr) {
             if (iov_to_buf(out_sg, out_num, 0, &vhdr, n->guest_hdr_len) <
                 n->guest_hdr_len) {
                 virtio_error(vdev, "virtio-net header incorrect");
-                virtqueue_detach_element(q->tx_vq, elem, 0);
-                g_free(elem);
-                return -EINVAL;
+                goto detach;
             }
             if (n->needs_vnet_hdr_swap) {
                 virtio_net_hdr_swap(vdev, (void *) &vhdr);
@@ -2807,6 +2803,11 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q)
                              n->guest_hdr_len, -1);
             out_num = sg_num;
             out_sg = sg;
+
+            if (out_num < 1) {
+                virtio_error(vdev, "virtio-net nothing to send");
+                goto detach;
+            }
         }
 
         ret = qemu_sendv_packet_async(qemu_get_subqueue(n->nic, queue_index),
@@ -2827,6 +2828,11 @@ drop:
         }
     }
     return num_packets;
+
+detach:
+    virtqueue_detach_element(q->tx_vq, elem, 0);
+    g_free(elem);
+    return -EINVAL;
 }
 
 static void virtio_net_tx_timer(void *opaque);
-- 
2.43.0



^ permalink raw reply related	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2024-04-09 19:06 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-04-09 19:05 [PATCH 1/1] virtio-net: fix bug 1451 aka "assert(!virtio_net_get_subqueue(nc)->async_tx.elem); " Alexey Dobriyan
  -- strict thread matches above, loose matches on Subject: below --
2024-04-05 11:20 Alexey Dobriyan
2024-04-08  7:26 ` Jason Wang
2024-04-09  6:01   ` [PATCH 1/1] virtio-net: fix bug 1451 aka "assert(!virtio_net_get_subqueue(nc)->async_tx.elem);" Alexey Dobriyan
2024-04-09  6:49 ` Michael S. Tsirkin
2024-04-09  6:51 ` Michael S. Tsirkin
2024-04-09 16:37   ` Alexey Dobriyan
2024-04-09 16:41     ` Michael S. Tsirkin
2024-04-09 17:15       ` Alexey Dobriyan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).