mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
vduse: fix compat handling for VDUSE_IOTLB_GET_FD/VDUSE_VQ_GET_INFO
These two ioctls are incompatible on 32-bit x86 userspace, because the data structures are shorter than they are on 64-bit. Add a proper .compat_ioctl handler for x86 that reads the structures with the smaller padding before calling the internal handlers. On all other architectures, CONFIG_COMPAT_FOR_U64_ALIGNMENT is disabled and no special handling is required. Fixes:ad146355bf("vduse: Support querying information of IOVA regions") Fixes:c8a6153b6c("vduse: Introduce VDUSE - vDPA Device in Userspace") Acked-by: Eugenio Pérez <eperezma@redhat.com> Signed-off-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Message-ID: <20260213154051.4172275-1-arnd@kernel.org>
This commit is contained in:
parent
ec6177dfe9
commit
455a2a1af9
|
|
@ -1641,6 +1641,127 @@ static long vduse_dev_ioctl(struct file *file, unsigned int cmd,
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT_FOR_U64_ALIGNMENT
|
||||
/*
|
||||
* i386 has different alignment constraints than x86_64,
|
||||
* so there are only 3 bytes of padding instead of 7.
|
||||
*/
|
||||
struct compat_vduse_iotlb_entry {
|
||||
compat_u64 offset;
|
||||
compat_u64 start;
|
||||
compat_u64 last;
|
||||
__u8 perm;
|
||||
__u8 padding[3];
|
||||
};
|
||||
#define COMPAT_VDUSE_IOTLB_GET_FD _IOWR(VDUSE_BASE, 0x10, struct compat_vduse_iotlb_entry)
|
||||
|
||||
struct compat_vduse_vq_info {
|
||||
__u32 index;
|
||||
__u32 num;
|
||||
compat_u64 desc_addr;
|
||||
compat_u64 driver_addr;
|
||||
compat_u64 device_addr;
|
||||
union {
|
||||
struct vduse_vq_state_split split;
|
||||
struct vduse_vq_state_packed packed;
|
||||
};
|
||||
__u8 ready;
|
||||
__u8 padding[3];
|
||||
};
|
||||
#define COMPAT_VDUSE_VQ_GET_INFO _IOWR(VDUSE_BASE, 0x15, struct compat_vduse_vq_info)
|
||||
|
||||
static long vduse_dev_compat_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct vduse_dev *dev = file->private_data;
|
||||
void __user *argp = (void __user *)arg;
|
||||
int ret;
|
||||
|
||||
if (unlikely(dev->broken))
|
||||
return -EPERM;
|
||||
|
||||
switch (cmd) {
|
||||
case COMPAT_VDUSE_IOTLB_GET_FD: {
|
||||
struct vduse_iotlb_entry_v2 entry = {0};
|
||||
struct file *f = NULL;
|
||||
|
||||
ret = -EFAULT;
|
||||
if (copy_from_user(&entry, argp, _IOC_SIZE(cmd)))
|
||||
break;
|
||||
|
||||
ret = vduse_dev_iotlb_entry(dev, &entry, &f, NULL);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (!f)
|
||||
break;
|
||||
|
||||
ret = copy_to_user(argp, &entry, _IOC_SIZE(cmd));
|
||||
if (ret) {
|
||||
ret = -EFAULT;
|
||||
fput(f);
|
||||
break;
|
||||
}
|
||||
ret = receive_fd(f, NULL, perm_to_file_flags(entry.perm));
|
||||
fput(f);
|
||||
break;
|
||||
}
|
||||
case COMPAT_VDUSE_VQ_GET_INFO: {
|
||||
struct vduse_vq_info vq_info = {};
|
||||
struct vduse_virtqueue *vq;
|
||||
u32 index;
|
||||
|
||||
ret = -EFAULT;
|
||||
if (copy_from_user(&vq_info, argp,
|
||||
sizeof(struct compat_vduse_vq_info)))
|
||||
break;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (vq_info.index >= dev->vq_num)
|
||||
break;
|
||||
|
||||
index = array_index_nospec(vq_info.index, dev->vq_num);
|
||||
vq = dev->vqs[index];
|
||||
vq_info.desc_addr = vq->desc_addr;
|
||||
vq_info.driver_addr = vq->driver_addr;
|
||||
vq_info.device_addr = vq->device_addr;
|
||||
vq_info.num = vq->num;
|
||||
|
||||
if (dev->driver_features & BIT_ULL(VIRTIO_F_RING_PACKED)) {
|
||||
vq_info.packed.last_avail_counter =
|
||||
vq->state.packed.last_avail_counter;
|
||||
vq_info.packed.last_avail_idx =
|
||||
vq->state.packed.last_avail_idx;
|
||||
vq_info.packed.last_used_counter =
|
||||
vq->state.packed.last_used_counter;
|
||||
vq_info.packed.last_used_idx =
|
||||
vq->state.packed.last_used_idx;
|
||||
} else
|
||||
vq_info.split.avail_index =
|
||||
vq->state.split.avail_index;
|
||||
|
||||
vq_info.ready = vq->ready;
|
||||
|
||||
ret = -EFAULT;
|
||||
if (copy_to_user(argp, &vq_info,
|
||||
sizeof(struct compat_vduse_vq_info)))
|
||||
break;
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = -ENOIOCTLCMD;
|
||||
break;
|
||||
}
|
||||
|
||||
return vduse_dev_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
|
||||
}
|
||||
#else
|
||||
#define vduse_dev_compat_ioctl compat_ptr_ioctl
|
||||
#endif
|
||||
|
||||
static int vduse_dev_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct vduse_dev *dev = file->private_data;
|
||||
|
|
@ -1694,7 +1815,7 @@ static const struct file_operations vduse_dev_fops = {
|
|||
.write_iter = vduse_dev_write_iter,
|
||||
.poll = vduse_dev_poll,
|
||||
.unlocked_ioctl = vduse_dev_ioctl,
|
||||
.compat_ioctl = compat_ptr_ioctl,
|
||||
.compat_ioctl = vduse_dev_compat_ioctl,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user