]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
virtio_pci: add check for common cfg size
authorXuan Zhuo <xuanzhuo@linux.alibaba.com>
Thu, 19 Oct 2023 03:49:02 +0000 (11:49 +0800)
committerMichael S. Tsirkin <mst@redhat.com>
Wed, 1 Nov 2023 13:19:59 +0000 (09:19 -0400)
Some buggy devices, the common cfg size may not match the features.

This patch checks the common cfg size for the
features(VIRTIO_F_NOTIF_CONFIG_DATA, VIRTIO_F_RING_RESET). When the
common cfg size does not match the corresponding feature, we fail the
probe and print error message.

Signed-off-by: Xuan Zhuo <xuanzhuo@linux.alibaba.com>
Acked-by: Jason Wang <jasowang@redhat.com>
Message-Id: <20231019034902.7346-1-xuanzhuo@linux.alibaba.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
drivers/virtio/virtio_pci_modern.c
drivers/virtio/virtio_pci_modern_dev.c
include/linux/virtio_pci_modern.h

index d6bb68ba84e54b120236b40f173965efb1c9aff9..ee6a386d250b168bdd59153a62ddceb361c0af93 100644 (file)
@@ -39,6 +39,39 @@ static void vp_transport_features(struct virtio_device *vdev, u64 features)
                __virtio_set_bit(vdev, VIRTIO_F_RING_RESET);
 }
 
+static int __vp_check_common_size_one_feature(struct virtio_device *vdev, u32 fbit,
+                                           u32 offset, const char *fname)
+{
+       struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+
+       if (!__virtio_test_bit(vdev, fbit))
+               return 0;
+
+       if (likely(vp_dev->mdev.common_len >= offset))
+               return 0;
+
+       dev_err(&vdev->dev,
+               "virtio: common cfg size(%zu) does not match the feature %s\n",
+               vp_dev->mdev.common_len, fname);
+
+       return -EINVAL;
+}
+
+#define vp_check_common_size_one_feature(vdev, fbit, field) \
+       __vp_check_common_size_one_feature(vdev, fbit, \
+               offsetofend(struct virtio_pci_modern_common_cfg, field), #fbit)
+
+static int vp_check_common_size(struct virtio_device *vdev)
+{
+       if (vp_check_common_size_one_feature(vdev, VIRTIO_F_NOTIF_CONFIG_DATA, queue_notify_data))
+               return -EINVAL;
+
+       if (vp_check_common_size_one_feature(vdev, VIRTIO_F_RING_RESET, queue_reset))
+               return -EINVAL;
+
+       return 0;
+}
+
 /* virtio config->finalize_features() implementation */
 static int vp_finalize_features(struct virtio_device *vdev)
 {
@@ -57,6 +90,9 @@ static int vp_finalize_features(struct virtio_device *vdev)
                return -EINVAL;
        }
 
+       if (vp_check_common_size(vdev))
+               return -EINVAL;
+
        vp_modern_set_features(&vp_dev->mdev, vdev->features);
 
        return 0;
index d312443d6569020874a358396a1f081520890245..e2a1fe7bb66cc9c4da102f1559da648173c3376f 100644 (file)
@@ -296,7 +296,7 @@ int vp_modern_probe(struct virtio_pci_modern_device *mdev)
        mdev->common = vp_modern_map_capability(mdev, common,
                                      sizeof(struct virtio_pci_common_cfg), 4,
                                      0, sizeof(struct virtio_pci_modern_common_cfg),
-                                     NULL, NULL);
+                                     &mdev->common_len, NULL);
        if (!mdev->common)
                goto err_map_common;
        mdev->isr = vp_modern_map_capability(mdev, isr, sizeof(u8), 1,
index a38c729d19734719b5e53bd56f78d0a8610c5adf..d0f2797420f7044616c7c7ef9faccc956acf5a7c 100644 (file)
@@ -45,6 +45,7 @@ struct virtio_pci_modern_device {
 
        size_t notify_len;
        size_t device_len;
+       size_t common_len;
 
        int notify_map_cap;