diff options
author | Martin Radev <martin.b.radev@gmail.com> | 2022-05-09 23:39:39 +0300 |
---|---|---|
committer | Will Deacon <will@kernel.org> | 2022-05-20 21:36:32 +0100 |
commit | 31e0eacca520f60ac02dfaaaeaeddfcc132095c0 (patch) | |
tree | 54e1cf049707d70f623c8c7d235c114740d25e1d | |
parent | e47302846cc538386c6cb62e41da485876f8bed0 (diff) | |
download | kvmtool-31e0eacca520f60ac02dfaaaeaeddfcc132095c0.tar.gz |
virtio: Check for overflows in QUEUE_NOTIFY and QUEUE_SEL
This patch checks for overflows in QUEUE_NOTIFY and QUEUE_SEL in
the PCI and MMIO operation handling paths. Further, the return
value type of get_vq_count is changed from int to uint since negative
doesn't carry any semantic meaning.
Reviewed-by: Alexandru Elisei <alexandru.elisei@arm.com>
Signed-off-by: Martin Radev <martin.b.radev@gmail.com>
Link: https://lore.kernel.org/r/20220509203940.754644-6-martin.b.radev@gmail.com
Signed-off-by: Will Deacon <will@kernel.org>
-rw-r--r-- | include/kvm/virtio.h | 2 | ||||
-rw-r--r-- | virtio/9p.c | 2 | ||||
-rw-r--r-- | virtio/balloon.c | 2 | ||||
-rw-r--r-- | virtio/blk.c | 2 | ||||
-rw-r--r-- | virtio/console.c | 2 | ||||
-rw-r--r-- | virtio/mmio.c | 16 | ||||
-rw-r--r-- | virtio/net.c | 2 | ||||
-rw-r--r-- | virtio/pci.c | 17 | ||||
-rw-r--r-- | virtio/rng.c | 2 | ||||
-rw-r--r-- | virtio/scsi.c | 2 | ||||
-rw-r--r-- | virtio/vsock.c | 2 |
11 files changed, 39 insertions, 12 deletions
diff --git a/include/kvm/virtio.h b/include/kvm/virtio.h index 3880e74a..ad274acf 100644 --- a/include/kvm/virtio.h +++ b/include/kvm/virtio.h @@ -187,7 +187,7 @@ struct virtio_ops { size_t (*get_config_size)(struct kvm *kvm, void *dev); u32 (*get_host_features)(struct kvm *kvm, void *dev); void (*set_guest_features)(struct kvm *kvm, void *dev, u32 features); - int (*get_vq_count)(struct kvm *kvm, void *dev); + unsigned int (*get_vq_count)(struct kvm *kvm, void *dev); int (*init_vq)(struct kvm *kvm, void *dev, u32 vq, u32 page_size, u32 align, u32 pfn); void (*exit_vq)(struct kvm *kvm, void *dev, u32 vq); diff --git a/virtio/9p.c b/virtio/9p.c index 57cd6d0c..7c9d7925 100644 --- a/virtio/9p.c +++ b/virtio/9p.c @@ -1469,7 +1469,7 @@ static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size) return size; } -static int get_vq_count(struct kvm *kvm, void *dev) +static unsigned int get_vq_count(struct kvm *kvm, void *dev) { return NUM_VIRT_QUEUES; } diff --git a/virtio/balloon.c b/virtio/balloon.c index 655a6617..f398ce47 100644 --- a/virtio/balloon.c +++ b/virtio/balloon.c @@ -256,7 +256,7 @@ static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size) return size; } -static int get_vq_count(struct kvm *kvm, void *dev) +static unsigned int get_vq_count(struct kvm *kvm, void *dev) { return NUM_VIRT_QUEUES; } diff --git a/virtio/blk.c b/virtio/blk.c index af71c0ca..46ee0281 100644 --- a/virtio/blk.c +++ b/virtio/blk.c @@ -291,7 +291,7 @@ static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size) return size; } -static int get_vq_count(struct kvm *kvm, void *dev) +static unsigned int get_vq_count(struct kvm *kvm, void *dev) { return NUM_VIRT_QUEUES; } diff --git a/virtio/console.c b/virtio/console.c index dae6034f..83158082 100644 --- a/virtio/console.c +++ b/virtio/console.c @@ -216,7 +216,7 @@ static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size) return size; } -static int get_vq_count(struct kvm *kvm, void *dev) +static unsigned int get_vq_count(struct kvm *kvm, void *dev) { return VIRTIO_CONSOLE_NUM_QUEUES; } diff --git a/virtio/mmio.c b/virtio/mmio.c index 53519c18..d08da1eb 100644 --- a/virtio/mmio.c +++ b/virtio/mmio.c @@ -169,13 +169,22 @@ static void virtio_mmio_config_out(struct kvm_cpu *vcpu, { struct virtio_mmio *vmmio = vdev->virtio; struct kvm *kvm = vmmio->kvm; + unsigned int vq_count = vdev->ops->get_vq_count(kvm, vmmio->dev); u32 val = 0; switch (addr) { case VIRTIO_MMIO_HOST_FEATURES_SEL: case VIRTIO_MMIO_GUEST_FEATURES_SEL: + val = ioport__read32(data); + *(u32 *)(((void *)&vmmio->hdr) + addr) = val; + break; case VIRTIO_MMIO_QUEUE_SEL: val = ioport__read32(data); + if (val >= vq_count) { + WARN_ONCE(1, "QUEUE_SEL value (%u) is larger than VQ count (%u)\n", + val, vq_count); + break; + } *(u32 *)(((void *)&vmmio->hdr) + addr) = val; break; case VIRTIO_MMIO_STATUS: @@ -221,6 +230,11 @@ static void virtio_mmio_config_out(struct kvm_cpu *vcpu, break; case VIRTIO_MMIO_QUEUE_NOTIFY: val = ioport__read32(data); + if (val >= vq_count) { + WARN_ONCE(1, "QUEUE_NOTIFY value (%u) is larger than VQ count (%u)\n", + val, vq_count); + break; + } vdev->ops->notify_vq(vmmio->kvm, vmmio->dev, val); break; case VIRTIO_MMIO_INTERRUPT_ACK: @@ -340,7 +354,7 @@ int virtio_mmio_init(struct kvm *kvm, void *dev, struct virtio_device *vdev, int virtio_mmio_reset(struct kvm *kvm, struct virtio_device *vdev) { - int vq; + unsigned int vq; struct virtio_mmio *vmmio = vdev->virtio; for (vq = 0; vq < vdev->ops->get_vq_count(kvm, vmmio->dev); vq++) diff --git a/virtio/net.c b/virtio/net.c index ec5dc1f3..67070d65 100644 --- a/virtio/net.c +++ b/virtio/net.c @@ -755,7 +755,7 @@ static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size) return size; } -static int get_vq_count(struct kvm *kvm, void *dev) +static unsigned int get_vq_count(struct kvm *kvm, void *dev) { struct net_dev *ndev = dev; diff --git a/virtio/pci.c b/virtio/pci.c index 050cfea9..23831d5a 100644 --- a/virtio/pci.c +++ b/virtio/pci.c @@ -320,9 +320,11 @@ static bool virtio_pci__data_out(struct kvm_cpu *vcpu, struct virtio_device *vde struct virtio_pci *vpci; struct kvm *kvm; u32 val; + unsigned int vq_count; kvm = vcpu->kvm; vpci = vdev->virtio; + vq_count = vdev->ops->get_vq_count(kvm, vpci->dev); switch (offset) { case VIRTIO_PCI_GUEST_FEATURES: @@ -342,10 +344,21 @@ static bool virtio_pci__data_out(struct kvm_cpu *vcpu, struct virtio_device *vde } break; case VIRTIO_PCI_QUEUE_SEL: - vpci->queue_selector = ioport__read16(data); + val = ioport__read16(data); + if (val >= vq_count) { + WARN_ONCE(1, "QUEUE_SEL value (%u) is larger than VQ count (%u)\n", + val, vq_count); + return false; + } + vpci->queue_selector = val; break; case VIRTIO_PCI_QUEUE_NOTIFY: val = ioport__read16(data); + if (val >= vq_count) { + WARN_ONCE(1, "QUEUE_SEL value (%u) is larger than VQ count (%u)\n", + val, vq_count); + return false; + } vdev->ops->notify_vq(kvm, vpci->dev, val); break; case VIRTIO_PCI_STATUS: @@ -638,7 +651,7 @@ int virtio_pci__init(struct kvm *kvm, void *dev, struct virtio_device *vdev, int virtio_pci__reset(struct kvm *kvm, struct virtio_device *vdev) { - int vq; + unsigned int vq; struct virtio_pci *vpci = vdev->virtio; for (vq = 0; vq < vdev->ops->get_vq_count(kvm, vpci->dev); vq++) diff --git a/virtio/rng.c b/virtio/rng.c index c7835a0e..75b682e3 100644 --- a/virtio/rng.c +++ b/virtio/rng.c @@ -147,7 +147,7 @@ static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size) return size; } -static int get_vq_count(struct kvm *kvm, void *dev) +static unsigned int get_vq_count(struct kvm *kvm, void *dev) { return NUM_VIRT_QUEUES; } diff --git a/virtio/scsi.c b/virtio/scsi.c index 8f1c3484..60432cc8 100644 --- a/virtio/scsi.c +++ b/virtio/scsi.c @@ -176,7 +176,7 @@ static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size) return size; } -static int get_vq_count(struct kvm *kvm, void *dev) +static unsigned int get_vq_count(struct kvm *kvm, void *dev) { return NUM_VIRT_QUEUES; } diff --git a/virtio/vsock.c b/virtio/vsock.c index 34397b64..64b4e95a 100644 --- a/virtio/vsock.c +++ b/virtio/vsock.c @@ -204,7 +204,7 @@ static void notify_vq_gsi(struct kvm *kvm, void *dev, u32 vq, u32 gsi) die_perror("VHOST_SET_VRING_CALL failed"); } -static int get_vq_count(struct kvm *kvm, void *dev) +static unsigned int get_vq_count(struct kvm *kvm, void *dev) { return VSOCK_VQ_MAX; } |