Skip to content

Commit a881b49

Browse files
stefanhaRHawilliam
authored andcommitted
vfio: align capability structures
The VFIO_DEVICE_GET_INFO, VFIO_DEVICE_GET_REGION_INFO, and VFIO_IOMMU_GET_INFO ioctls fill in an info struct followed by capability structs: +------+---------+---------+-----+ | info | caps[0] | caps[1] | ... | +------+---------+---------+-----+ Both the info and capability struct sizes are not always multiples of sizeof(u64), leaving u64 fields in later capability structs misaligned. Userspace applications currently need to handle misalignment manually in order to support CPU architectures and programming languages with strict alignment requirements. Make life easier for userspace by ensuring alignment in the kernel. This is done by padding info struct definitions and by copying out zeroes after capability structs that are not aligned. The new layout is as follows: +------+---------+---+---------+-----+ | info | caps[0] | 0 | caps[1] | ... | +------+---------+---+---------+-----+ In this example caps[0] has a size that is not multiples of sizeof(u64), so zero padding is added to align the subsequent structure. Adding zero padding between structs does not break the uapi. The memory layout is specified by the info.cap_offset and caps[i].next fields filled in by the kernel. Applications use these field values to locate structs and are therefore unaffected by the addition of zero padding. Note that code that copies out info structs with padding is updated to always zero the struct and copy out as many bytes as userspace requested. This makes the code shorter and avoids potential information leaks by ensuring padding is initialized. Originally-by: Alex Williamson <[email protected]> Signed-off-by: Stefan Hajnoczi <[email protected]> Reviewed-by: Kevin Tian <[email protected]> Acked-by: Jason Gunthorpe <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alex Williamson <[email protected]>
1 parent cd24e2a commit a881b49

File tree

5 files changed

+14
-18
lines changed

5 files changed

+14
-18
lines changed

drivers/iommu/iommufd/vfio_compat.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,8 @@ static int iommufd_vfio_iommu_get_info(struct iommufd_ctx *ictx,
483483
rc = cap_size;
484484
goto out_put;
485485
}
486+
cap_size = ALIGN(cap_size, sizeof(u64));
487+
486488
if (last_cap && info.argsz >= total_cap_size &&
487489
put_user(total_cap_size, &last_cap->next)) {
488490
rc = -EFAULT;

drivers/vfio/pci/vfio_pci_core.c

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -958,24 +958,17 @@ static int vfio_pci_ioctl_get_info(struct vfio_pci_core_device *vdev,
958958
struct vfio_device_info __user *arg)
959959
{
960960
unsigned long minsz = offsetofend(struct vfio_device_info, num_irqs);
961-
struct vfio_device_info info;
961+
struct vfio_device_info info = {};
962962
struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
963-
unsigned long capsz;
964963
int ret;
965964

966-
/* For backward compatibility, cannot require this */
967-
capsz = offsetofend(struct vfio_iommu_type1_info, cap_offset);
968-
969965
if (copy_from_user(&info, arg, minsz))
970966
return -EFAULT;
971967

972968
if (info.argsz < minsz)
973969
return -EINVAL;
974970

975-
if (info.argsz >= capsz) {
976-
minsz = capsz;
977-
info.cap_offset = 0;
978-
}
971+
minsz = min_t(size_t, info.argsz, sizeof(info));
979972

980973
info.flags = VFIO_DEVICE_FLAGS_PCI;
981974

drivers/vfio/vfio_iommu_type1.c

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2762,27 +2762,20 @@ static int vfio_iommu_dma_avail_build_caps(struct vfio_iommu *iommu,
27622762
static int vfio_iommu_type1_get_info(struct vfio_iommu *iommu,
27632763
unsigned long arg)
27642764
{
2765-
struct vfio_iommu_type1_info info;
2765+
struct vfio_iommu_type1_info info = {};
27662766
unsigned long minsz;
27672767
struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
2768-
unsigned long capsz;
27692768
int ret;
27702769

27712770
minsz = offsetofend(struct vfio_iommu_type1_info, iova_pgsizes);
27722771

2773-
/* For backward compatibility, cannot require this */
2774-
capsz = offsetofend(struct vfio_iommu_type1_info, cap_offset);
2775-
27762772
if (copy_from_user(&info, (void __user *)arg, minsz))
27772773
return -EFAULT;
27782774

27792775
if (info.argsz < minsz)
27802776
return -EINVAL;
27812777

2782-
if (info.argsz >= capsz) {
2783-
minsz = capsz;
2784-
info.cap_offset = 0; /* output, no-recopy necessary */
2785-
}
2778+
minsz = min_t(size_t, info.argsz, sizeof(info));
27862779

27872780
mutex_lock(&iommu->lock);
27882781
info.flags = VFIO_IOMMU_INFO_PGSIZES;

drivers/vfio/vfio_main.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,6 +1409,9 @@ struct vfio_info_cap_header *vfio_info_cap_add(struct vfio_info_cap *caps,
14091409
void *buf;
14101410
struct vfio_info_cap_header *header, *tmp;
14111411

1412+
/* Ensure that the next capability struct will be aligned */
1413+
size = ALIGN(size, sizeof(u64));
1414+
14121415
buf = krealloc(caps->buf, caps->size + size, GFP_KERNEL);
14131416
if (!buf) {
14141417
kfree(caps->buf);
@@ -1442,6 +1445,9 @@ void vfio_info_cap_shift(struct vfio_info_cap *caps, size_t offset)
14421445
struct vfio_info_cap_header *tmp;
14431446
void *buf = (void *)caps->buf;
14441447

1448+
/* Capability structs should start with proper alignment */
1449+
WARN_ON(!IS_ALIGNED(offset, sizeof(u64)));
1450+
14451451
for (tmp = buf; tmp->next; tmp = buf + tmp->next - offset)
14461452
tmp->next += offset;
14471453
}

include/uapi/linux/vfio.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ struct vfio_device_info {
217217
__u32 num_regions; /* Max region index + 1 */
218218
__u32 num_irqs; /* Max IRQ index + 1 */
219219
__u32 cap_offset; /* Offset within info struct of first cap */
220+
__u32 pad;
220221
};
221222
#define VFIO_DEVICE_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 7)
222223

@@ -1444,6 +1445,7 @@ struct vfio_iommu_type1_info {
14441445
#define VFIO_IOMMU_INFO_CAPS (1 << 1) /* Info supports caps */
14451446
__u64 iova_pgsizes; /* Bitmap of supported page sizes */
14461447
__u32 cap_offset; /* Offset within info struct of first cap */
1448+
__u32 pad;
14471449
};
14481450

14491451
/*

0 commit comments

Comments
 (0)