virtio: pci: Bounds check notification writes
authorAndrew Scull <ascull@google.com>
Thu, 21 Apr 2022 16:11:02 +0000 (16:11 +0000)
committerTom Rini <trini@konsulko.com>
Tue, 3 May 2022 19:50:45 +0000 (15:50 -0400)
Make sure virtio notifications are written within their allocated
buffer.

Signed-off-by: Andrew Scull <ascull@google.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
drivers/virtio/virtio_pci_modern.c

index bcf9f18..7dd58aa 100644 (file)
@@ -94,6 +94,7 @@
  *
  * @common: pci transport device common register block base
  * @notify_base: pci transport device notify register block base
+ * @notify_len: pci transport device notify register block length
  * @device: pci transport device device-specific register block base
  * @device_len: pci transport device device-specific register block length
  * @notify_offset_multiplier: multiply queue_notify_off by this value
 struct virtio_pci_priv {
        struct virtio_pci_common_cfg __iomem *common;
        void __iomem *notify_base;
+       u32 notify_len;
        void __iomem *device;
        u32 device_len;
        u32 notify_offset_multiplier;
@@ -373,11 +375,19 @@ static int virtio_pci_notify(struct udevice *udev, struct virtqueue *vq)
        off = ioread16(&priv->common->queue_notify_off);
 
        /*
+        * Check the effective offset is in bounds and leaves space for the
+        * notification, which is just a single 16-bit value since
+        * VIRTIO_F_NOTIFICATION_DATA isn't negotiated by the drivers.
+        */
+       off *= priv->notify_offset_multiplier;
+       if (off > priv->notify_len - sizeof(u16))
+               return -EIO;
+
+       /*
         * We write the queue's selector into the notification register
         * to signal the other end
         */
-       iowrite16(vq->index,
-                 priv->notify_base + off * priv->notify_offset_multiplier);
+       iowrite16(vq->index, priv->notify_base + off);
 
        return 0;
 }
@@ -499,6 +509,9 @@ static int virtio_pci_probe(struct udevice *udev)
                return -EINVAL;
        }
 
+       offset = notify + offsetof(struct virtio_pci_cap, length);
+       dm_pci_read_config32(udev, offset, &priv->notify_len);
+
        /*
         * Device capability is only mandatory for devices that have
         * device-specific configuration.