Skip to content

Commit e8b367f

Browse files
bvanasschegregkh
authored andcommitted
IB/umad: Fix use-after-free on close
commit 60e1751 upstream. Avoid that closing /dev/infiniband/umad<n> or /dev/infiniband/issm<n> triggers a use-after-free. __fput() invokes f_op->release() before it invokes cdev_put(). Make sure that the ib_umad_device structure is freed by the cdev_put() call instead of f_op->release(). This avoids that changing the port mode from IB into Ethernet and back to IB followed by restarting opensmd triggers the following kernel oops: general protection fault: 0000 [#1] PREEMPT SMP RIP: 0010:[<ffffffff810cc65c>] [<ffffffff810cc65c>] module_put+0x2c/0x170 Call Trace: [<ffffffff81190f20>] cdev_put+0x20/0x30 [<ffffffff8118e2ce>] __fput+0x1ae/0x1f0 [<ffffffff8118e35e>] ____fput+0xe/0x10 [<ffffffff810723bc>] task_work_run+0xac/0xe0 [<ffffffff81002a9f>] do_notify_resume+0x9f/0xc0 [<ffffffff814b8398>] int_signal+0x12/0x17 Reference: https://bugzilla.kernel.org/show_bug.cgi?id=75051 Signed-off-by: Bart Van Assche <[email protected]> Reviewed-by: Yann Droneaud <[email protected]> Signed-off-by: Roland Dreier <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 90c4ccb commit e8b367f

File tree

1 file changed

+19
-11
lines changed

1 file changed

+19
-11
lines changed

drivers/infiniband/core/user_mad.c

+19-11
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ struct ib_umad_port {
9898

9999
struct ib_umad_device {
100100
int start_port, end_port;
101-
struct kref ref;
101+
struct kobject kobj;
102102
struct ib_umad_port port[0];
103103
};
104104

@@ -134,14 +134,18 @@ static DECLARE_BITMAP(dev_map, IB_UMAD_MAX_PORTS);
134134
static void ib_umad_add_one(struct ib_device *device);
135135
static void ib_umad_remove_one(struct ib_device *device);
136136

137-
static void ib_umad_release_dev(struct kref *ref)
137+
static void ib_umad_release_dev(struct kobject *kobj)
138138
{
139139
struct ib_umad_device *dev =
140-
container_of(ref, struct ib_umad_device, ref);
140+
container_of(kobj, struct ib_umad_device, kobj);
141141

142142
kfree(dev);
143143
}
144144

145+
static struct kobj_type ib_umad_dev_ktype = {
146+
.release = ib_umad_release_dev,
147+
};
148+
145149
static int hdr_size(struct ib_umad_file *file)
146150
{
147151
return file->use_pkey_index ? sizeof (struct ib_user_mad_hdr) :
@@ -812,7 +816,7 @@ static int ib_umad_open(struct inode *inode, struct file *filp)
812816
goto out;
813817
}
814818

815-
kref_get(&port->umad_dev->ref);
819+
kobject_get(&port->umad_dev->kobj);
816820

817821
out:
818822
mutex_unlock(&port->file_mutex);
@@ -851,7 +855,7 @@ static int ib_umad_close(struct inode *inode, struct file *filp)
851855
mutex_unlock(&file->port->file_mutex);
852856

853857
kfree(file);
854-
kref_put(&dev->ref, ib_umad_release_dev);
858+
kobject_put(&dev->kobj);
855859

856860
return 0;
857861
}
@@ -902,7 +906,7 @@ static int ib_umad_sm_open(struct inode *inode, struct file *filp)
902906
if (ret)
903907
goto err_clr_sm_cap;
904908

905-
kref_get(&port->umad_dev->ref);
909+
kobject_get(&port->umad_dev->kobj);
906910

907911
return 0;
908912

@@ -932,7 +936,7 @@ static int ib_umad_sm_close(struct inode *inode, struct file *filp)
932936

933937
up(&port->sm_sem);
934938

935-
kref_put(&port->umad_dev->ref, ib_umad_release_dev);
939+
kobject_put(&port->umad_dev->kobj);
936940

937941
return ret;
938942
}
@@ -1000,6 +1004,7 @@ static int find_overflow_devnum(void)
10001004
}
10011005

10021006
static int ib_umad_init_port(struct ib_device *device, int port_num,
1007+
struct ib_umad_device *umad_dev,
10031008
struct ib_umad_port *port)
10041009
{
10051010
int devnum;
@@ -1032,6 +1037,7 @@ static int ib_umad_init_port(struct ib_device *device, int port_num,
10321037

10331038
cdev_init(&port->cdev, &umad_fops);
10341039
port->cdev.owner = THIS_MODULE;
1040+
port->cdev.kobj.parent = &umad_dev->kobj;
10351041
kobject_set_name(&port->cdev.kobj, "umad%d", port->dev_num);
10361042
if (cdev_add(&port->cdev, base, 1))
10371043
goto err_cdev;
@@ -1050,6 +1056,7 @@ static int ib_umad_init_port(struct ib_device *device, int port_num,
10501056
base += IB_UMAD_MAX_PORTS;
10511057
cdev_init(&port->sm_cdev, &umad_sm_fops);
10521058
port->sm_cdev.owner = THIS_MODULE;
1059+
port->sm_cdev.kobj.parent = &umad_dev->kobj;
10531060
kobject_set_name(&port->sm_cdev.kobj, "issm%d", port->dev_num);
10541061
if (cdev_add(&port->sm_cdev, base, 1))
10551062
goto err_sm_cdev;
@@ -1143,15 +1150,16 @@ static void ib_umad_add_one(struct ib_device *device)
11431150
if (!umad_dev)
11441151
return;
11451152

1146-
kref_init(&umad_dev->ref);
1153+
kobject_init(&umad_dev->kobj, &ib_umad_dev_ktype);
11471154

11481155
umad_dev->start_port = s;
11491156
umad_dev->end_port = e;
11501157

11511158
for (i = s; i <= e; ++i) {
11521159
umad_dev->port[i - s].umad_dev = umad_dev;
11531160

1154-
if (ib_umad_init_port(device, i, &umad_dev->port[i - s]))
1161+
if (ib_umad_init_port(device, i, umad_dev,
1162+
&umad_dev->port[i - s]))
11551163
goto err;
11561164
}
11571165

@@ -1163,7 +1171,7 @@ static void ib_umad_add_one(struct ib_device *device)
11631171
while (--i >= s)
11641172
ib_umad_kill_port(&umad_dev->port[i - s]);
11651173

1166-
kref_put(&umad_dev->ref, ib_umad_release_dev);
1174+
kobject_put(&umad_dev->kobj);
11671175
}
11681176

11691177
static void ib_umad_remove_one(struct ib_device *device)
@@ -1177,7 +1185,7 @@ static void ib_umad_remove_one(struct ib_device *device)
11771185
for (i = 0; i <= umad_dev->end_port - umad_dev->start_port; ++i)
11781186
ib_umad_kill_port(&umad_dev->port[i]);
11791187

1180-
kref_put(&umad_dev->ref, ib_umad_release_dev);
1188+
kobject_put(&umad_dev->kobj);
11811189
}
11821190

11831191
static char *umad_devnode(struct device *dev, umode_t *mode)

0 commit comments

Comments
 (0)