[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PULL 1/1] virtio-pci: fix use of a released vector
From: |
Peter Maydell |
Subject: |
Re: [PULL 1/1] virtio-pci: fix use of a released vector |
Date: |
Tue, 16 Apr 2024 11:01:36 +0100 |
On Mon, 15 Apr 2024 at 11:52, Michael S. Tsirkin <mst@redhat.com> wrote:
>
> From: Cindy Lu <lulu@redhat.com>
>
> During the booting process of the non-standard image, the behavior of the
> called function in qemu is as follows:
>
> 1. vhost_net_stop() was triggered by guest image. This will call the function
> virtio_pci_set_guest_notifiers() with assgin= false,
> virtio_pci_set_guest_notifiers() will release the irqfd for vector 0
>
> 2. virtio_reset() was triggered, this will set configure vector to
> VIRTIO_NO_VECTOR
>
> 3.vhost_net_start() was called (at this time, the configure vector is
> still VIRTIO_NO_VECTOR) and then call virtio_pci_set_guest_notifiers() with
> assgin=true, so the irqfd for vector 0 is still not "init" during this process
>
> 4. The system continues to boot and sets the vector back to 0. After that
> msix_fire_vector_notifier() was triggered to unmask the vector 0 and meet
> the crash
>
> To fix the issue, we need to support changing the vector after
> VIRTIO_CONFIG_S_DRIVER_OK is set.
>
Hi; Coverity points out what it thinks is a problem in this commit
(CID 1543938):
> diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
> index cb6940fc0e..cb159fd078 100644
> --- a/hw/virtio/virtio-pci.c
> +++ b/hw/virtio/virtio-pci.c
> @@ -1424,6 +1424,38 @@ static int virtio_pci_add_mem_cap(VirtIOPCIProxy
> *proxy,
> return offset;
> }
>
> +static void virtio_pci_set_vector(VirtIODevice *vdev,
> + VirtIOPCIProxy *proxy,
> + int queue_no, uint16_t old_vector,
> + uint16_t new_vector)
> +{
> + bool kvm_irqfd = (vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) &&
> + msix_enabled(&proxy->pci_dev) && kvm_msi_via_irqfd_enabled();
> +
> + if (new_vector == old_vector) {
> + return;
> + }
> +
> + /*
> + * If the device uses irqfd and the vector changes after DRIVER_OK is
> + * set, we need to release the old vector and set up the new one.
> + * Otherwise just need to set the new vector on the device.
> + */
> + if (kvm_irqfd && old_vector != VIRTIO_NO_VECTOR) {
> + kvm_virtio_pci_vector_release_one(proxy, queue_no);
> + }
> + /* Set the new vector on the device. */
> + if (queue_no == VIRTIO_CONFIG_IRQ_IDX) {
> + vdev->config_vector = new_vector;
> + } else {
> + virtio_queue_set_vector(vdev, queue_no, new_vector);
> + }
Here queue_no can be VIRTIO_CONFIG_IRQ_IDX, which is -1.
> + /* If the new vector changed need to set it up. */
> + if (kvm_irqfd && new_vector != VIRTIO_NO_VECTOR) {
> + kvm_virtio_pci_vector_use_one(proxy, queue_no);
Here we pass that through to kvm_virtio_pci_vector_use_one().
In kvm_virtio_pci_vector_use_one()'s error-exit path ("undo")
it does
vector = virtio_queue_vector(vdev, queue_no);
and in virtio_queue_vector() it does:
return n < VIRTIO_QUEUE_MAX ? vdev->vq[n].vector :
VIRTIO_NO_VECTOR;
where 'n' is an int, so if we can get here with queue_no being
VIRTIO_CONFIG_IRQ_IDX then we'll index off the front of the
vdev->vq[] array.
Maybe this is a "can't happen" case, but it does seem odd that
virtio_queue_vector() only bounds-checks the "too big" case
for its argument and not the "too small" case and/or it
doesn't have a special case for VIRTIO_CONFIG_IRQ_IDX.
> + }
> +}
> +
thanks
-- PMM