Make sure virtio notifications are written within their allocated
buffer.

Signed-off-by: Andrew Scull <asc...@google.com>
Reviewed-by: Bin Meng <bmeng...@gmail.com>
---
 drivers/virtio/virtio_pci_modern.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/drivers/virtio/virtio_pci_modern.c 
b/drivers/virtio/virtio_pci_modern.c
index bcf9f18997..7dd58aa0f4 100644
--- a/drivers/virtio/virtio_pci_modern.c
+++ b/drivers/virtio/virtio_pci_modern.c
@@ -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
@@ -101,6 +102,7 @@
 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;
@@ -372,12 +374,20 @@ static int virtio_pci_notify(struct udevice *udev, struct 
virtqueue *vq)
        /* get offset of notification word for this 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.
-- 
2.35.1.1021.g381101b075-goog

Reply via email to