]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
vfio: Adapt drivers to use the core helper vfio_check_precopy_ioctl
authorYishai Hadas <yishaih@nvidia.com>
Tue, 17 Mar 2026 16:17:50 +0000 (18:17 +0200)
committerAlex Williamson <alex@shazbot.org>
Thu, 19 Mar 2026 18:32:09 +0000 (12:32 -0600)
Introduce a core helper function for VFIO_MIG_GET_PRECOPY_INFO and adapt
all drivers to use it.

It centralizes the common code and ensures that output flags are cleared
on entry, in case user opts in to VFIO_DEVICE_FEATURE_MIG_PRECOPY_INFOv2.
This preventing any unintended echoing of userspace data back to
userspace.

Signed-off-by: Yishai Hadas <yishaih@nvidia.com>
Link: https://lore.kernel.org/r/20260317161753.18964-4-yishaih@nvidia.com
Signed-off-by: Alex Williamson <alex@shazbot.org>
drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c
drivers/vfio/pci/mlx5/main.c
drivers/vfio/pci/qat/main.c
drivers/vfio/pci/virtio/migrate.c
include/linux/vfio.h
samples/vfio-mdev/mtty.c

index 1d367cff7dcf7bd25fda71e213503e5bb25aab85..bb121f635b9f7fd555c201e052cea9ca94a0dc10 100644 (file)
@@ -857,18 +857,12 @@ static long hisi_acc_vf_precopy_ioctl(struct file *filp,
        struct hisi_acc_vf_core_device *hisi_acc_vdev = migf->hisi_acc_vdev;
        loff_t *pos = &filp->f_pos;
        struct vfio_precopy_info info;
-       unsigned long minsz;
        int ret;
 
-       if (cmd != VFIO_MIG_GET_PRECOPY_INFO)
-               return -ENOTTY;
-
-       minsz = offsetofend(struct vfio_precopy_info, dirty_bytes);
-
-       if (copy_from_user(&info, (void __user *)arg, minsz))
-               return -EFAULT;
-       if (info.argsz < minsz)
-               return -EINVAL;
+       ret = vfio_check_precopy_ioctl(&hisi_acc_vdev->core_device.vdev, cmd,
+                                      arg, &info);
+       if (ret)
+               return ret;
 
        mutex_lock(&hisi_acc_vdev->state_mutex);
        if (hisi_acc_vdev->mig_state != VFIO_DEVICE_STATE_PRE_COPY) {
@@ -893,7 +887,8 @@ static long hisi_acc_vf_precopy_ioctl(struct file *filp,
        mutex_unlock(&migf->lock);
        mutex_unlock(&hisi_acc_vdev->state_mutex);
 
-       return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0;
+       return copy_to_user((void __user *)arg, &info,
+               offsetofend(struct vfio_precopy_info, dirty_bytes)) ? -EFAULT : 0;
 out:
        mutex_unlock(&migf->lock);
        mutex_unlock(&hisi_acc_vdev->state_mutex);
index dbba6173894b3f3f232daa4a02778c4d3465caf9..fb541c17c71284e9dcee1bd6a55c4c31e3878df5 100644 (file)
@@ -463,21 +463,14 @@ static long mlx5vf_precopy_ioctl(struct file *filp, unsigned int cmd,
        struct mlx5_vhca_data_buffer *buf;
        struct vfio_precopy_info info = {};
        loff_t *pos = &filp->f_pos;
-       unsigned long minsz;
        size_t inc_length = 0;
        bool end_of_data = false;
        int ret;
 
-       if (cmd != VFIO_MIG_GET_PRECOPY_INFO)
-               return -ENOTTY;
-
-       minsz = offsetofend(struct vfio_precopy_info, dirty_bytes);
-
-       if (copy_from_user(&info, (void __user *)arg, minsz))
-               return -EFAULT;
-
-       if (info.argsz < minsz)
-               return -EINVAL;
+       ret = vfio_check_precopy_ioctl(&mvdev->core_device.vdev, cmd, arg,
+                                      &info);
+       if (ret)
+               return ret;
 
        mutex_lock(&mvdev->state_mutex);
        if (mvdev->mig_state != VFIO_DEVICE_STATE_PRE_COPY &&
@@ -545,7 +538,8 @@ static long mlx5vf_precopy_ioctl(struct file *filp, unsigned int cmd,
 
 done:
        mlx5vf_state_mutex_unlock(mvdev);
-       if (copy_to_user((void __user *)arg, &info, minsz))
+       if (copy_to_user((void __user *)arg, &info,
+                        offsetofend(struct vfio_precopy_info, dirty_bytes)))
                return -EFAULT;
        return 0;
 
index b982d4ae666cfa7457b7d16604d5d026ad656057..b3a4b7a55696999c6a74ad60f37cc42737a40677 100644 (file)
@@ -121,18 +121,12 @@ static long qat_vf_precopy_ioctl(struct file *filp, unsigned int cmd,
        struct qat_mig_dev *mig_dev = qat_vdev->mdev;
        struct vfio_precopy_info info;
        loff_t *pos = &filp->f_pos;
-       unsigned long minsz;
        int ret = 0;
 
-       if (cmd != VFIO_MIG_GET_PRECOPY_INFO)
-               return -ENOTTY;
-
-       minsz = offsetofend(struct vfio_precopy_info, dirty_bytes);
-
-       if (copy_from_user(&info, (void __user *)arg, minsz))
-               return -EFAULT;
-       if (info.argsz < minsz)
-               return -EINVAL;
+       ret = vfio_check_precopy_ioctl(&qat_vdev->core_device.vdev, cmd, arg,
+                                      &info);
+       if (ret)
+               return ret;
 
        mutex_lock(&qat_vdev->state_mutex);
        if (qat_vdev->mig_state != VFIO_DEVICE_STATE_PRE_COPY &&
@@ -160,7 +154,8 @@ out:
        mutex_unlock(&qat_vdev->state_mutex);
        if (ret)
                return ret;
-       return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0;
+       return copy_to_user((void __user *)arg, &info,
+               offsetofend(struct vfio_precopy_info, dirty_bytes)) ? -EFAULT : 0;
 }
 
 static ssize_t qat_vf_save_read(struct file *filp, char __user *buf,
index 35fa2d6ed61173f6e9bcc7fad2798a3af99f9a29..7e11834ad5129a302820794618329c697d6e7d99 100644 (file)
@@ -443,19 +443,13 @@ static long virtiovf_precopy_ioctl(struct file *filp, unsigned int cmd,
        struct vfio_precopy_info info = {};
        loff_t *pos = &filp->f_pos;
        bool end_of_data = false;
-       unsigned long minsz;
        u32 ctx_size = 0;
        int ret;
 
-       if (cmd != VFIO_MIG_GET_PRECOPY_INFO)
-               return -ENOTTY;
-
-       minsz = offsetofend(struct vfio_precopy_info, dirty_bytes);
-       if (copy_from_user(&info, (void __user *)arg, minsz))
-               return -EFAULT;
-
-       if (info.argsz < minsz)
-               return -EINVAL;
+       ret = vfio_check_precopy_ioctl(&virtvdev->core_device.vdev, cmd, arg,
+                                      &info);
+       if (ret)
+               return ret;
 
        mutex_lock(&virtvdev->state_mutex);
        if (virtvdev->mig_state != VFIO_DEVICE_STATE_PRE_COPY &&
@@ -514,7 +508,8 @@ static long virtiovf_precopy_ioctl(struct file *filp, unsigned int cmd,
 
 done:
        virtiovf_state_mutex_unlock(virtvdev);
-       if (copy_to_user((void __user *)arg, &info, minsz))
+       if (copy_to_user((void __user *)arg, &info,
+                        offsetofend(struct vfio_precopy_info, dirty_bytes)))
                return -EFAULT;
        return 0;
 
index 7c1d33283e042bc226671f4ef031119f73c22eb5..50b474334a1973c89dc6946485a1a8dd944b154e 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/cdev.h>
 #include <uapi/linux/vfio.h>
 #include <linux/iova_bitmap.h>
+#include <linux/uaccess.h>
 
 struct kvm;
 struct iommufd_ctx;
@@ -285,6 +286,44 @@ static inline int vfio_check_feature(u32 flags, size_t argsz, u32 supported_ops,
        return 1;
 }
 
+/**
+ * vfio_check_precopy_ioctl - Validate user input for the VFIO_MIG_GET_PRECOPY_INFO ioctl
+ * @vdev: The vfio device
+ * @cmd: Cmd from the ioctl
+ * @arg: Arg from the ioctl
+ * @info: Driver pointer to hold the userspace input to the ioctl
+ *
+ * For use in a driver's get_precopy_info. Checks that the inputs to the
+ * VFIO_MIG_GET_PRECOPY_INFO ioctl are correct.
+
+ * Returns 0 on success, otherwise errno.
+ */
+
+static inline int
+vfio_check_precopy_ioctl(struct vfio_device *vdev, unsigned int cmd,
+                        unsigned long arg, struct vfio_precopy_info *info)
+{
+       unsigned long minsz;
+
+       if (cmd != VFIO_MIG_GET_PRECOPY_INFO)
+               return -ENOTTY;
+
+       minsz = offsetofend(struct vfio_precopy_info, dirty_bytes);
+
+       if (copy_from_user(info, (void __user *)arg, minsz))
+               return -EFAULT;
+
+       if (info->argsz < minsz)
+               return -EINVAL;
+
+       /* keep v1 behaviour as is for compatibility reasons */
+       if (vdev->precopy_info_v2)
+               /* flags are output, set its initial value to 0 */
+               info->flags = 0;
+
+       return 0;
+}
+
 struct vfio_device *_vfio_alloc_device(size_t size, struct device *dev,
                                       const struct vfio_device_ops *ops);
 #define vfio_alloc_device(dev_struct, member, dev, ops)                                \
index 01a9db84c4ab4989c23f962ee32bdde15f37692a..69b6d9defbce973cd544ed02e5ccbae341c7fc7b 100644 (file)
@@ -840,18 +840,11 @@ static long mtty_precopy_ioctl(struct file *filp, unsigned int cmd,
        struct mdev_state *mdev_state = migf->mdev_state;
        loff_t *pos = &filp->f_pos;
        struct vfio_precopy_info info = {};
-       unsigned long minsz;
        int ret;
 
-       if (cmd != VFIO_MIG_GET_PRECOPY_INFO)
-               return -ENOTTY;
-
-       minsz = offsetofend(struct vfio_precopy_info, dirty_bytes);
-
-       if (copy_from_user(&info, (void __user *)arg, minsz))
-               return -EFAULT;
-       if (info.argsz < minsz)
-               return -EINVAL;
+       ret = vfio_check_precopy_ioctl(&mdev_state->vdev, cmd, arg, &info);
+       if (ret)
+               return ret;
 
        mutex_lock(&mdev_state->state_mutex);
        if (mdev_state->state != VFIO_DEVICE_STATE_PRE_COPY &&
@@ -878,7 +871,8 @@ static long mtty_precopy_ioctl(struct file *filp, unsigned int cmd,
        info.initial_bytes = migf->filled_size - *pos;
        mutex_unlock(&migf->lock);
 
-       ret = copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0;
+       ret = copy_to_user((void __user *)arg, &info,
+               offsetofend(struct vfio_precopy_info, dirty_bytes)) ? -EFAULT : 0;
 unlock:
        mtty_state_mutex_unlock(mdev_state);
        return ret;