vfio: Reject a second cdev open before mutating shared device state

The cdev single-open check lives in vfio_df_open(), which runs at the
end of the bind ioctl, after vfio_df_ioctl_bind_iommufd() has already
updated state shared across all opens: vfio_df_check_token() can set
the PF vf_token and vfio_df_get_kvm_safe() records the caller's KVM
pointer in device->kvm and takes a reference.

A second cdev bind of an already-open device runs both, only to be
rejected in vfio_df_open().  The error path clears device->kvm and
drops the reference, tearing down the current opener's KVM association
and potentially resulting in an unbalanced reference on close or
premature release, while the vf_token remains clobbered.

Move the single-open check into vfio_df_ioctl_bind_iommufd() ahead of
both mutations, so a bind that cannot complete leaves the current
opener's state untouched.  df->group is NULL on this path, so a
non-zero open_count is exactly what vfio_df_open() rejected.  The test
in vfio_df_open() becomes redundant and is removed.

Return -EBUSY rather than -EINVAL here.  The arguments are not invalid,
the device is in use, which could be a transient condition due to a
delayed fput if the prior user is terminated.  This provides
compatibility with the group path, where a group open returns -EBUSY,
and users may choose bounded polling to detect such a transient
condition.

Fixes: 839e692fa4 ("vfio: Make vfio_df_open() single open for device cdev path")
Fixes: 5fcc26969a ("vfio: Add VFIO_DEVICE_BIND_IOMMUFD")
Fixes: 86624ba3b5 ("vfio/pci: Do vf_token checks for VFIO_DEVICE_BIND_IOMMUFD")
Assisted-by: LLM
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Alex Williamson <alex.williamson@nvidia.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Link: https://lore.kernel.org/r/20260911170429.1642480-2-alex.williamson@nvidia.com
Signed-off-by: Alex Williamson <alex@shazbot.org>
This commit is contained in:
Alex Williamson 2026-09-11 11:04:24 -06:00 committed by Alex Williamson
parent b1d0f0d97a
commit 258ba46543
2 changed files with 12 additions and 7 deletions

View File

@ -130,6 +130,18 @@ long vfio_df_ioctl_bind_iommufd(struct vfio_device_file *df,
goto out_unlock;
}
/*
* The cdev path allows only a single open. Reject a second open here,
* before the VF token and device->kvm updates below would clobber the
* current opener's state on a bind that cannot complete. Return -EBUSY
* rather than -EINVAL since a delayed release of the prior opener can
* make this transient.
*/
if (device->open_count) {
ret = -EBUSY;
goto out_unlock;
}
ret = vfio_df_check_token(device, &bind);
if (ret)
goto out_unlock;

View File

@ -603,13 +603,6 @@ int vfio_df_open(struct vfio_device_file *df)
lockdep_assert_held(&device->dev_set->lock);
/*
* Only the group path allows the device to be opened multiple
* times. The device cdev path doesn't have a secure way for it.
*/
if (device->open_count != 0 && !df->group)
return -EINVAL;
device->open_count++;
if (device->open_count == 1) {
ret = vfio_df_device_first_open(df);