Skip to content

Commit 35d4a0b

Browse files
yishaihdledford
authored andcommitted
IB/uverbs: Fix race between ib_uverbs_open and remove_one
Fixes: 2a72f21 ("IB/uverbs: Remove dev_table") Before this commit there was a device look-up table that was protected by a spin_lock used by ib_uverbs_open and by ib_uverbs_remove_one. When it was dropped and container_of was used instead, it enabled the race with remove_one as dev might be freed just after: dev = container_of(inode->i_cdev, struct ib_uverbs_device, cdev) but before the kref_get. In addition, this buggy patch added some dead code as container_of(x,y,z) can never be NULL and so dev can never be NULL. As a result the comment above ib_uverbs_open saying "the open method will either immediately run -ENXIO" is wrong as it can never happen. The solution follows Jason Gunthorpe suggestion from below URL: https://www.mail-archive.com/linux-rdma@vger.kernel.org/msg25692.html cdev will hold a kref on the parent (the containing structure, ib_uverbs_device) and only when that kref is released it is guaranteed that open will never be called again. In addition, fixes the active count scheme to use an atomic not a kref to prevent WARN_ON as pointed by above comment from Jason. Signed-off-by: Yishai Hadas <yishaih@mellanox.com> Signed-off-by: Shachar Raindel <raindel@mellanox.com> Reviewed-by: Jason Gunthorpe <jgunthorpe@obsidianresearch.com> Signed-off-by: Doug Ledford <dledford@redhat.com>
1 parent 03c4044 commit 35d4a0b

File tree

2 files changed

+32
-14
lines changed

2 files changed

+32
-14
lines changed

drivers/infiniband/core/uverbs.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@
8585
*/
8686

8787
struct ib_uverbs_device {
88-
struct kref ref;
88+
atomic_t refcount;
8989
int num_comp_vectors;
9090
struct completion comp;
9191
struct device *dev;
@@ -94,6 +94,7 @@ struct ib_uverbs_device {
9494
struct cdev cdev;
9595
struct rb_root xrcd_tree;
9696
struct mutex xrcd_tree_mutex;
97+
struct kobject kobj;
9798
};
9899

99100
struct ib_uverbs_event_file {

drivers/infiniband/core/uverbs_main.c

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,18 @@ static int (*uverbs_ex_cmd_table[])(struct ib_uverbs_file *file,
130130
static void ib_uverbs_add_one(struct ib_device *device);
131131
static void ib_uverbs_remove_one(struct ib_device *device, void *client_data);
132132

133-
static void ib_uverbs_release_dev(struct kref *ref)
133+
static void ib_uverbs_release_dev(struct kobject *kobj)
134134
{
135135
struct ib_uverbs_device *dev =
136-
container_of(ref, struct ib_uverbs_device, ref);
136+
container_of(kobj, struct ib_uverbs_device, kobj);
137137

138-
complete(&dev->comp);
138+
kfree(dev);
139139
}
140140

141+
static struct kobj_type ib_uverbs_dev_ktype = {
142+
.release = ib_uverbs_release_dev,
143+
};
144+
141145
static void ib_uverbs_release_event_file(struct kref *ref)
142146
{
143147
struct ib_uverbs_event_file *file =
@@ -303,13 +307,19 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
303307
return context->device->dealloc_ucontext(context);
304308
}
305309

310+
static void ib_uverbs_comp_dev(struct ib_uverbs_device *dev)
311+
{
312+
complete(&dev->comp);
313+
}
314+
306315
static void ib_uverbs_release_file(struct kref *ref)
307316
{
308317
struct ib_uverbs_file *file =
309318
container_of(ref, struct ib_uverbs_file, ref);
310319

311320
module_put(file->device->ib_dev->owner);
312-
kref_put(&file->device->ref, ib_uverbs_release_dev);
321+
if (atomic_dec_and_test(&file->device->refcount))
322+
ib_uverbs_comp_dev(file->device);
313323

314324
kfree(file);
315325
}
@@ -775,9 +785,7 @@ static int ib_uverbs_open(struct inode *inode, struct file *filp)
775785
int ret;
776786

777787
dev = container_of(inode->i_cdev, struct ib_uverbs_device, cdev);
778-
if (dev)
779-
kref_get(&dev->ref);
780-
else
788+
if (!atomic_inc_not_zero(&dev->refcount))
781789
return -ENXIO;
782790

783791
if (!try_module_get(dev->ib_dev->owner)) {
@@ -798,27 +806,32 @@ static int ib_uverbs_open(struct inode *inode, struct file *filp)
798806
mutex_init(&file->mutex);
799807

800808
filp->private_data = file;
809+
kobject_get(&dev->kobj);
801810

802811
return nonseekable_open(inode, filp);
803812

804813
err_module:
805814
module_put(dev->ib_dev->owner);
806815

807816
err:
808-
kref_put(&dev->ref, ib_uverbs_release_dev);
817+
if (atomic_dec_and_test(&dev->refcount))
818+
ib_uverbs_comp_dev(dev);
819+
809820
return ret;
810821
}
811822

812823
static int ib_uverbs_close(struct inode *inode, struct file *filp)
813824
{
814825
struct ib_uverbs_file *file = filp->private_data;
826+
struct ib_uverbs_device *dev = file->device;
815827

816828
ib_uverbs_cleanup_ucontext(file, file->ucontext);
817829

818830
if (file->async_file)
819831
kref_put(&file->async_file->ref, ib_uverbs_release_event_file);
820832

821833
kref_put(&file->ref, ib_uverbs_release_file);
834+
kobject_put(&dev->kobj);
822835

823836
return 0;
824837
}
@@ -914,10 +927,11 @@ static void ib_uverbs_add_one(struct ib_device *device)
914927
if (!uverbs_dev)
915928
return;
916929

917-
kref_init(&uverbs_dev->ref);
930+
atomic_set(&uverbs_dev->refcount, 1);
918931
init_completion(&uverbs_dev->comp);
919932
uverbs_dev->xrcd_tree = RB_ROOT;
920933
mutex_init(&uverbs_dev->xrcd_tree_mutex);
934+
kobject_init(&uverbs_dev->kobj, &ib_uverbs_dev_ktype);
921935

922936
spin_lock(&map_lock);
923937
devnum = find_first_zero_bit(dev_map, IB_UVERBS_MAX_DEVICES);
@@ -944,6 +958,7 @@ static void ib_uverbs_add_one(struct ib_device *device)
944958
cdev_init(&uverbs_dev->cdev, NULL);
945959
uverbs_dev->cdev.owner = THIS_MODULE;
946960
uverbs_dev->cdev.ops = device->mmap ? &uverbs_mmap_fops : &uverbs_fops;
961+
uverbs_dev->cdev.kobj.parent = &uverbs_dev->kobj;
947962
kobject_set_name(&uverbs_dev->cdev.kobj, "uverbs%d", uverbs_dev->devnum);
948963
if (cdev_add(&uverbs_dev->cdev, base, 1))
949964
goto err_cdev;
@@ -974,9 +989,10 @@ static void ib_uverbs_add_one(struct ib_device *device)
974989
clear_bit(devnum, overflow_map);
975990

976991
err:
977-
kref_put(&uverbs_dev->ref, ib_uverbs_release_dev);
992+
if (atomic_dec_and_test(&uverbs_dev->refcount))
993+
ib_uverbs_comp_dev(uverbs_dev);
978994
wait_for_completion(&uverbs_dev->comp);
979-
kfree(uverbs_dev);
995+
kobject_put(&uverbs_dev->kobj);
980996
return;
981997
}
982998

@@ -996,9 +1012,10 @@ static void ib_uverbs_remove_one(struct ib_device *device, void *client_data)
9961012
else
9971013
clear_bit(uverbs_dev->devnum - IB_UVERBS_MAX_DEVICES, overflow_map);
9981014

999-
kref_put(&uverbs_dev->ref, ib_uverbs_release_dev);
1015+
if (atomic_dec_and_test(&uverbs_dev->refcount))
1016+
ib_uverbs_comp_dev(uverbs_dev);
10001017
wait_for_completion(&uverbs_dev->comp);
1001-
kfree(uverbs_dev);
1018+
kobject_put(&uverbs_dev->kobj);
10021019
}
10031020

10041021
static char *uverbs_devnode(struct device *dev, umode_t *mode)

0 commit comments

Comments
 (0)