On Fri, 11 Sept 2026 at 10:01, Michael S. Tsirkin <[email protected]> wrote:
>
> When QEMU restores pending virtio requests from a migration stream, it
> calls qemu_get_virtqueue_element(), which calls virtqueue_map() and
> virtqueue_map_iovec() to re-map the saved DMA fragment addresses into
> the destination address space.  If the addresses in the migration stream
> are corrupted, and as a result dma_memory_map() returns NULL or a
> shorter-than-expected length, virtqueue_map_iovec() calls exit(1),
> terminating the destination QEMU process instead of failing the
> migration cleanly.
>
> Convert virtqueue_map_iovec() and virtqueue_map() from void to bool.
> On failure, virtqueue_map_iovec() unmaps any entries it has already
> mapped and returns false; virtqueue_map() similarly cleans up the in_sg
> entries if out_sg mapping fails.  qemu_get_virtqueue_element() now
> checks the return value, frees the element and returns NULL on failure,
> allowing the migration restore path to propagate a clean error.  The
> callers in virtio-blk, virtio-serial-bus and virtio-scsi that invoke
> qemu_get_virtqueue_element() during load are updated to check for NULL
> and return an error code.
>

Hi; Coverity points out an error in the error handling code
added here (CID 1686764):

> -static void virtqueue_map_iovec(VirtIODevice *vdev, struct iovec *sg,
> +static bool virtqueue_map_iovec(VirtIODevice *vdev, struct iovec *sg,
>                                  hwaddr *addr, unsigned int num_sg,
>                                  bool is_write)
>  {
>      unsigned int i;
>      hwaddr len;
> +    DMADirection dir = is_write ? DMA_DIRECTION_FROM_DEVICE :
> +                                 DMA_DIRECTION_TO_DEVICE;
>
>      for (i = 0; i < num_sg; i++) {
>          len = sg[i].iov_len;
> -        sg[i].iov_base = dma_memory_map(vdev->dma_as,
> -                                        addr[i], &len, is_write ?
> -                                        DMA_DIRECTION_FROM_DEVICE :
> -                                        DMA_DIRECTION_TO_DEVICE,
> -                                        MEMTXATTRS_UNSPECIFIED);
> +        sg[i].iov_base = dma_memory_map(vdev->dma_as, addr[i], &len,
> +                                        dir, MEMTXATTRS_UNSPECIFIED);
>          if (!sg[i].iov_base) {
>              error_report("virtio: error trying to map MMIO memory");
> -            exit(1);
> +            goto err_undo_map;

If dma_memory_map() fails on the first iteration, we will jump
to err_undo_map, with i == 0....

>          }
>          if (len != sg[i].iov_len) {
>              error_report("virtio: unexpected memory split");
> -            exit(1);
> +            dma_memory_unmap(vdev->dma_as, sg[i].iov_base, len, dir, 0);
> +            goto err_undo_map;
>          }
>      }
> +    return true;
> +
> +err_undo_map:
> +    while (i-- > 0) {

...but since 'i' is an unsigned type, that will mean that "i--"
here underflows to an extremely large value. This loop will
then iterate many times unmapping random things.

> +        dma_memory_unmap(vdev->dma_as, sg[i].iov_base, sg[i].iov_len,
> +                         dir, 0);
> +    }
> +    return false;
>  }

thanks
-- PMM

Reply via email to