On Thu, 8 Sep 2022 15:44:59 -0300 Jason Gunthorpe <jgg@xxxxxxxxxx> wrote: > __vfio_register_dev() has a bit of code to sanity check if an (existing) > group is not corrupted by having two copies of the same struct device in > it. This should be impossible. > > It then has some complicated error unwind to uncreate the group. > > Instead check if the existing group is sane at the same time we locate > it. If a bug is found then there is no error unwind, just simply fail > allocation. > > Signed-off-by: Jason Gunthorpe <jgg@xxxxxxxxxx> > --- > drivers/vfio/vfio_main.c | 79 ++++++++++++++++++---------------------- > 1 file changed, 36 insertions(+), 43 deletions(-) > > diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c > index 4ab13808b536e1..ba8b6bed12c7e7 100644 > --- a/drivers/vfio/vfio_main.c > +++ b/drivers/vfio/vfio_main.c > @@ -306,15 +306,15 @@ static void vfio_container_put(struct vfio_container *container) > * Group objects - create, release, get, put, search > */ > static struct vfio_group * > -__vfio_group_get_from_iommu(struct iommu_group *iommu_group) > +vfio_group_find_from_iommu(struct iommu_group *iommu_group) > { > struct vfio_group *group; > > + lockdep_assert_held(&vfio.group_lock); > + > list_for_each_entry(group, &vfio.group_list, vfio_next) { > - if (group->iommu_group == iommu_group) { > - vfio_group_get(group); > + if (group->iommu_group == iommu_group) > return group; > - } > } > return NULL; > } > @@ -365,11 +365,27 @@ static struct vfio_group *vfio_group_alloc(struct iommu_group *iommu_group, > return group; > } > > +static bool vfio_group_has_device(struct vfio_group *group, struct device *dev) > +{ > + struct vfio_device *device; > + > + mutex_lock(&group->device_lock); > + list_for_each_entry(device, &group->device_list, group_next) { > + if (device->dev == dev) { > + mutex_unlock(&group->device_lock); > + return true; > + } > + } > + mutex_unlock(&group->device_lock); > + return false; > +} > + > /* > * Return a struct vfio_group * for the given iommu_group. If no vfio_group > * already exists then create a new one. > */ > -static struct vfio_group *vfio_get_group(struct iommu_group *iommu_group, > +static struct vfio_group *vfio_get_group(struct device *dev, > + struct iommu_group *iommu_group, > enum vfio_group_type type) > { > struct vfio_group *group; > @@ -378,13 +394,20 @@ static struct vfio_group *vfio_get_group(struct iommu_group *iommu_group, > > mutex_lock(&vfio.group_lock); > > - ret = __vfio_group_get_from_iommu(iommu_group); > - if (ret) > - goto err_unlock; > + ret = vfio_group_find_from_iommu(iommu_group); > + if (ret) { > + if (WARN_ON(vfio_group_has_device(ret, dev))) { > + ret = ERR_PTR(-EINVAL); > + goto out_unlock; > + } This still looks racy. We only know within vfio_group_has_device() that the device is not present in the group, what prevents a race between here and when we finally do add it to group->device_list? We can't make any guarantees if we drop group->device_lock between test and add. The semantics of vfio_get_group() are also rather strange, 'return a vfio_group for this iommu_group, but make sure it doesn't include this device' :-\ Thanks, Alex