summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Scull <ascull@google.com>2022-04-21 16:11:04 +0000
committerTom Rini <trini@konsulko.com>2022-05-03 15:50:45 -0400
commit6a8cb878a24257ea64dfbd0f5493f07759eb5a0d (patch)
tree81293cefd32276c68e5b291a05c592811965408d
parentf2c1ef1b6dfe93e9912d5d5dfae1e527da511c3f (diff)
virtio: pci: Check virtio capability is in bounds
Ensure the virtio PCI capabilities are contained within the bounds of the device's configuration space. The expected size of the capability is passed when searching for the capability to enforce this check. Signed-off-by: Andrew Scull <ascull@google.com> Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
-rw-r--r--drivers/virtio/virtio_pci_modern.c20
1 files changed, 16 insertions, 4 deletions
diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c
index 2c1b0ebfce..6fe5e76572 100644
--- a/drivers/virtio/virtio_pci_modern.c
+++ b/drivers/virtio/virtio_pci_modern.c
@@ -397,18 +397,27 @@ static int virtio_pci_notify(struct udevice *udev, struct virtqueue *vq)
*
* @udev: the transport device
* @cfg_type: the VIRTIO_PCI_CAP_* value we seek
+ * @cap_size: expected size of the capability
*
* Return: offset of the configuration structure
*/
-static int virtio_pci_find_capability(struct udevice *udev, u8 cfg_type)
+static int virtio_pci_find_capability(struct udevice *udev, u8 cfg_type,
+ size_t cap_size)
{
int pos;
int offset;
u8 type, bar;
+ assert(cap_size >= sizeof(struct virtio_pci_cap));
+ assert(cap_size <= PCI_CFG_SPACE_SIZE);
+
for (pos = dm_pci_find_capability(udev, PCI_CAP_ID_VNDR);
pos > 0;
pos = dm_pci_find_next_capability(udev, pos, PCI_CAP_ID_VNDR)) {
+ /* Ensure the capability is within bounds */
+ if (PCI_CFG_SPACE_SIZE - cap_size < pos)
+ return 0;
+
offset = pos + offsetof(struct virtio_pci_cap, cfg_type);
dm_pci_read_config8(udev, offset, &type);
offset = pos + offsetof(struct virtio_pci_cap, bar);
@@ -496,7 +505,8 @@ static int virtio_pci_probe(struct udevice *udev)
uc_priv->vendor = subvendor;
/* Check for a common config: if not, use legacy mode (bar 0) */
- common = virtio_pci_find_capability(udev, VIRTIO_PCI_CAP_COMMON_CFG);
+ common = virtio_pci_find_capability(udev, VIRTIO_PCI_CAP_COMMON_CFG,
+ sizeof(struct virtio_pci_cap));
if (!common) {
printf("(%s): leaving for legacy driver\n", udev->name);
return -ENODEV;
@@ -510,7 +520,8 @@ static int virtio_pci_probe(struct udevice *udev)
}
/* If common is there, notify should be too */
- notify = virtio_pci_find_capability(udev, VIRTIO_PCI_CAP_NOTIFY_CFG);
+ notify = virtio_pci_find_capability(udev, VIRTIO_PCI_CAP_NOTIFY_CFG,
+ sizeof(struct virtio_pci_notify_cap));
if (!notify) {
printf("(%s): missing capabilities %i/%i\n", udev->name,
common, notify);
@@ -524,7 +535,8 @@ static int virtio_pci_probe(struct udevice *udev)
* Device capability is only mandatory for devices that have
* device-specific configuration.
*/
- device = virtio_pci_find_capability(udev, VIRTIO_PCI_CAP_DEVICE_CFG);
+ device = virtio_pci_find_capability(udev, VIRTIO_PCI_CAP_DEVICE_CFG,
+ sizeof(struct virtio_pci_cap));
if (device) {
offset = device + offsetof(struct virtio_pci_cap, length);
dm_pci_read_config32(udev, offset, &priv->device_len);