__u64 xxx;
};
Suggested-by: Alex Williamson <alex.williamson@xxxxxxxxxx>
Signed-off-by: Yi Liu <yi.l.liu@xxxxxxxxx>
---
drivers/vfio/device_cdev.c | 81 +++++++++++++-------------------------
drivers/vfio/vfio.h | 18 +++++++++
drivers/vfio/vfio_main.c | 55 ++++++++++++++++++++++++++
3 files changed, 100 insertions(+), 54 deletions(-)
diff --git a/drivers/vfio/device_cdev.c b/drivers/vfio/device_cdev.c
index 4519f482e212..35c7664b9a97 100644
--- a/drivers/vfio/device_cdev.c
+++ b/drivers/vfio/device_cdev.c
@@ -159,40 +159,33 @@ void vfio_df_unbind_iommufd(struct vfio_device_file *df)
vfio_device_unblock_group(device);
}
+#define VFIO_ATTACH_FLAGS_MASK VFIO_DEVICE_ATTACH_PASID
+static unsigned long
+vfio_attach_xends[ilog2(VFIO_ATTACH_FLAGS_MASK) + 1] = {
+ XEND_SIZE(VFIO_DEVICE_ATTACH_PASID,
+ struct vfio_device_attach_iommufd_pt, pasid),
+};
+
+#define VFIO_DETACH_FLAGS_MASK VFIO_DEVICE_DETACH_PASID
+static unsigned long
+vfio_detach_xends[ilog2(VFIO_DETACH_FLAGS_MASK) + 1] = {
+ XEND_SIZE(VFIO_DEVICE_DETACH_PASID,
+ struct vfio_device_detach_iommufd_pt, pasid),
+};
+
int vfio_df_ioctl_attach_pt(struct vfio_device_file *df,
struct vfio_device_attach_iommufd_pt __user *arg)
{
struct vfio_device_attach_iommufd_pt attach;
struct vfio_device *device = df->device;
- unsigned long minsz, xend = 0;
int ret;
- minsz = offsetofend(struct vfio_device_attach_iommufd_pt, pt_id);
-
- if (copy_from_user(&attach, arg, minsz))
- return -EFAULT;
-
- if (attach.argsz < minsz)
- return -EINVAL;
-
- if (attach.flags & (~VFIO_DEVICE_ATTACH_PASID))
- return -EINVAL;
-
- if (attach.flags & VFIO_DEVICE_ATTACH_PASID)
- xend = offsetofend(struct vfio_device_attach_iommufd_pt, pasid);
-
- /*
- * xend may be equal to minsz if a flag is defined for reusing a
- * reserved field or a special usage of an existing field.
- */
- if (xend > minsz) {
- if (attach.argsz < xend)
- return -EINVAL;
-
- if (copy_from_user((void *)&attach + minsz,
- (void __user *)arg + minsz, xend - minsz))
- return -EFAULT;
- }
+ ret = vfio_copy_user_data((void __user *)arg, &attach,
+ struct vfio_device_attach_iommufd_pt,
+ pt_id, VFIO_ATTACH_FLAGS_MASK,
+ vfio_attach_xends);
+ if (ret)
+ return ret;
if ((attach.flags & VFIO_DEVICE_ATTACH_PASID) &&
!device->ops->pasid_attach_ioas)
@@ -227,34 +220,14 @@ int vfio_df_ioctl_detach_pt(struct vfio_device_file *df,
{
struct vfio_device_detach_iommufd_pt detach;
struct vfio_device *device = df->device;
- unsigned long minsz, xend = 0;
-
- minsz = offsetofend(struct vfio_device_detach_iommufd_pt, flags);
-
- if (copy_from_user(&detach, arg, minsz))
- return -EFAULT;
-
- if (detach.argsz < minsz)
- return -EINVAL;
-
- if (detach.flags & (~VFIO_DEVICE_DETACH_PASID))
- return -EINVAL;
-
- if (detach.flags & VFIO_DEVICE_DETACH_PASID)
- xend = offsetofend(struct vfio_device_detach_iommufd_pt, pasid);
-
- /*
- * xend may be equal to minsz if a flag is defined for reusing a
- * reserved field or a special usage of an existing field.
- */
- if (xend > minsz) {
- if (detach.argsz < xend)
- return -EINVAL;
+ int ret;
- if (copy_from_user((void *)&detach + minsz,
- (void __user *)arg + minsz, xend - minsz))
- return -EFAULT;
- }
+ ret = vfio_copy_user_data((void __user *)arg, &detach,
+ struct vfio_device_detach_iommufd_pt,
+ flags, VFIO_DETACH_FLAGS_MASK,
+ vfio_detach_xends);
+ if (ret)
+ return ret;
if ((detach.flags & VFIO_DEVICE_DETACH_PASID) &&
!device->ops->pasid_detach_ioas)
diff --git a/drivers/vfio/vfio.h b/drivers/vfio/vfio.h
index 50128da18bca..87bed550c46e 100644
--- a/drivers/vfio/vfio.h
+++ b/drivers/vfio/vfio.h
@@ -34,6 +34,24 @@ void vfio_df_close(struct vfio_device_file *df);
struct vfio_device_file *
vfio_allocate_device_file(struct vfio_device *device);
+int vfio_copy_from_user(void *buffer, void __user *arg,
+ unsigned long minsz, u32 flags_mask,
+ unsigned long *xend_array);
+
+#define vfio_copy_user_data(_arg, _local_buffer, _struct, _min_last, \
+ _flags_mask, _xend_array) \
+ vfio_copy_from_user(_local_buffer, _arg, \
+ offsetofend(_struct, _min_last) + \
+ BUILD_BUG_ON_ZERO(offsetof(_struct, argsz) != \
+ 0) + \
+ BUILD_BUG_ON_ZERO(offsetof(_struct, flags) != \
+ sizeof(u32)), \
+ _flags_mask, _xend_array)
+
+#define XEND_SIZE(_flag, _struct, _xlast) \
+ [ilog2(_flag)] = offsetofend(_struct, _xlast) + \
+ BUILD_BUG_ON_ZERO(_flag == 0) \
+
extern const struct file_operations vfio_device_fops;
#ifdef CONFIG_VFIO_NOIOMMU
diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c
index a5a62d9d963f..c61336ea5123 100644
--- a/drivers/vfio/vfio_main.c
+++ b/drivers/vfio/vfio_main.c
@@ -1694,6 +1694,61 @@ int vfio_dma_rw(struct vfio_device *device, dma_addr_t iova, void *data,
}
EXPORT_SYMBOL(vfio_dma_rw);
+/**
+ * vfio_copy_from_user - Copy the user struct that may have extended fields
+ *
+ * @buffer: The local buffer to store the data copied from user
+ * @arg: The user buffer pointer
+ * @minsz: The minimum size of the user struct
+ * @flags_mask: The combination of all the falgs defined
+ * @xend_array: The array that stores the xend size for set flags.
+ *
+ * This helper requires the user struct put the argsz and flags fields in
+ * the first 8 bytes.
+ *
+ * Return 0 for success, otherwise -errno
+ */
+int vfio_copy_from_user(void *buffer, void __user *arg,