virtio: refresh vring region cache after updating a virtqueue size
When a virtqueue size is changed by the guest via
virtio_queue_set_num(), its region cache is not automatically updated.
If the size was increased, this could lead to accessing the cache out
of bounds. For example, in vring_get_used_event():
    static inline uint16_t vring_get_used_event(VirtQueue *vq)
    {
        return vring_avail_ring(vq, vq->vring.num);
    }
    static inline uint16_t vring_avail_ring(VirtQueue *vq, int i)
    {
        VRingMemoryRegionCaches *caches = vring_get_region_caches(vq);
        hwaddr pa = offsetof(VRingAvail, ring[i]);
        if (!caches) {
            return 0;
        }
        return virtio_lduw_phys_cached(vq->vdev, &caches->avail, pa);
    }
vq->vring.num will be greater than caches->avail.len, which will
trigger a failed assertion down the call path of
virtio_lduw_phys_cached().
Fix this by calling virtio_init_region_cache() after
virtio_queue_set_num() if we are not already calling
virtio_queue_set_rings(). In the legacy path this is already done by
virtio_queue_update_rings().
Signed-off-by: Carlos López <clopez@suse.de>
Message-Id: <20230317002749.27379-1-clopez@suse.de>
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
Acked-by: Halil Pasic <pasic@linux.ibm.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
			
			
This commit is contained in:
		
							parent
							
								
									c1eb2ddf0f
								
							
						
					
					
						commit
						f0d634ea19
					
				@ -237,6 +237,7 @@ static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info,
 | 
			
		||||
                return -EINVAL;
 | 
			
		||||
            }
 | 
			
		||||
            virtio_queue_set_num(vdev, index, num);
 | 
			
		||||
            virtio_init_region_cache(vdev, index);
 | 
			
		||||
        } else if (virtio_queue_get_num(vdev, index) > num) {
 | 
			
		||||
            /* Fail if we don't have a big enough queue. */
 | 
			
		||||
            return -EINVAL;
 | 
			
		||||
 | 
			
		||||
@ -354,6 +354,7 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value,
 | 
			
		||||
        if (proxy->legacy) {
 | 
			
		||||
            virtio_queue_update_rings(vdev, vdev->queue_sel);
 | 
			
		||||
        } else {
 | 
			
		||||
            virtio_init_region_cache(vdev, vdev->queue_sel);
 | 
			
		||||
            proxy->vqs[vdev->queue_sel].num = value;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
@ -1554,6 +1554,7 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr,
 | 
			
		||||
        proxy->vqs[vdev->queue_sel].num = val;
 | 
			
		||||
        virtio_queue_set_num(vdev, vdev->queue_sel,
 | 
			
		||||
                             proxy->vqs[vdev->queue_sel].num);
 | 
			
		||||
        virtio_init_region_cache(vdev, vdev->queue_sel);
 | 
			
		||||
        break;
 | 
			
		||||
    case VIRTIO_PCI_COMMON_Q_MSIX:
 | 
			
		||||
        vector = virtio_queue_vector(vdev, vdev->queue_sel);
 | 
			
		||||
 | 
			
		||||
@ -226,7 +226,7 @@ static void virtio_virtqueue_reset_region_cache(struct VirtQueue *vq)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void virtio_init_region_cache(VirtIODevice *vdev, int n)
 | 
			
		||||
void virtio_init_region_cache(VirtIODevice *vdev, int n)
 | 
			
		||||
{
 | 
			
		||||
    VirtQueue *vq = &vdev->vq[n];
 | 
			
		||||
    VRingMemoryRegionCaches *old = vq->vring.caches;
 | 
			
		||||
 | 
			
		||||
@ -309,6 +309,7 @@ int virtio_get_num_queues(VirtIODevice *vdev);
 | 
			
		||||
void virtio_queue_set_rings(VirtIODevice *vdev, int n, hwaddr desc,
 | 
			
		||||
                            hwaddr avail, hwaddr used);
 | 
			
		||||
void virtio_queue_update_rings(VirtIODevice *vdev, int n);
 | 
			
		||||
void virtio_init_region_cache(VirtIODevice *vdev, int n);
 | 
			
		||||
void virtio_queue_set_align(VirtIODevice *vdev, int n, int align);
 | 
			
		||||
void virtio_queue_notify(VirtIODevice *vdev, int n);
 | 
			
		||||
uint16_t virtio_queue_vector(VirtIODevice *vdev, int n);
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user