Skip to content

Commit

Permalink
IB/umad: Fix use-after-free on close
Browse files Browse the repository at this point in the history
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 <bvanassche@acm.org>
Reviewed-by: Yann Droneaud <ydroneaud@opteya.com>
Signed-off-by: Roland Dreier <roland@purestorage.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
bvanassche authored and gregkh committed Jul 7, 2014
1 parent 90c4ccb commit e8b367f
Showing 1 changed file with 19 additions and 11 deletions.
30 changes: 19 additions & 11 deletions drivers/infiniband/core/user_mad.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ struct ib_umad_port {

struct ib_umad_device {
int start_port, end_port;
struct kref ref;
struct kobject kobj;
struct ib_umad_port port[0];
};

Expand Down Expand Up @@ -134,14 +134,18 @@ static DECLARE_BITMAP(dev_map, IB_UMAD_MAX_PORTS);
static void ib_umad_add_one(struct ib_device *device);
static void ib_umad_remove_one(struct ib_device *device);

static void ib_umad_release_dev(struct kref *ref)
static void ib_umad_release_dev(struct kobject *kobj)
{
struct ib_umad_device *dev =
container_of(ref, struct ib_umad_device, ref);
container_of(kobj, struct ib_umad_device, kobj);

kfree(dev);
}

static struct kobj_type ib_umad_dev_ktype = {
.release = ib_umad_release_dev,
};

static int hdr_size(struct ib_umad_file *file)
{
return file->use_pkey_index ? sizeof (struct ib_user_mad_hdr) :
Expand Down Expand Up @@ -812,7 +816,7 @@ static int ib_umad_open(struct inode *inode, struct file *filp)
goto out;
}

kref_get(&port->umad_dev->ref);
kobject_get(&port->umad_dev->kobj);

out:
mutex_unlock(&port->file_mutex);
Expand Down Expand Up @@ -851,7 +855,7 @@ static int ib_umad_close(struct inode *inode, struct file *filp)
mutex_unlock(&file->port->file_mutex);

kfree(file);
kref_put(&dev->ref, ib_umad_release_dev);
kobject_put(&dev->kobj);

return 0;
}
Expand Down Expand Up @@ -902,7 +906,7 @@ static int ib_umad_sm_open(struct inode *inode, struct file *filp)
if (ret)
goto err_clr_sm_cap;

kref_get(&port->umad_dev->ref);
kobject_get(&port->umad_dev->kobj);

return 0;

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

up(&port->sm_sem);

kref_put(&port->umad_dev->ref, ib_umad_release_dev);
kobject_put(&port->umad_dev->kobj);

return ret;
}
Expand Down Expand Up @@ -1000,6 +1004,7 @@ static int find_overflow_devnum(void)
}

static int ib_umad_init_port(struct ib_device *device, int port_num,
struct ib_umad_device *umad_dev,
struct ib_umad_port *port)
{
int devnum;
Expand Down Expand Up @@ -1032,6 +1037,7 @@ static int ib_umad_init_port(struct ib_device *device, int port_num,

cdev_init(&port->cdev, &umad_fops);
port->cdev.owner = THIS_MODULE;
port->cdev.kobj.parent = &umad_dev->kobj;
kobject_set_name(&port->cdev.kobj, "umad%d", port->dev_num);
if (cdev_add(&port->cdev, base, 1))
goto err_cdev;
Expand All @@ -1050,6 +1056,7 @@ static int ib_umad_init_port(struct ib_device *device, int port_num,
base += IB_UMAD_MAX_PORTS;
cdev_init(&port->sm_cdev, &umad_sm_fops);
port->sm_cdev.owner = THIS_MODULE;
port->sm_cdev.kobj.parent = &umad_dev->kobj;
kobject_set_name(&port->sm_cdev.kobj, "issm%d", port->dev_num);
if (cdev_add(&port->sm_cdev, base, 1))
goto err_sm_cdev;
Expand Down Expand Up @@ -1143,15 +1150,16 @@ static void ib_umad_add_one(struct ib_device *device)
if (!umad_dev)
return;

kref_init(&umad_dev->ref);
kobject_init(&umad_dev->kobj, &ib_umad_dev_ktype);

umad_dev->start_port = s;
umad_dev->end_port = e;

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

if (ib_umad_init_port(device, i, &umad_dev->port[i - s]))
if (ib_umad_init_port(device, i, umad_dev,
&umad_dev->port[i - s]))
goto err;
}

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

kref_put(&umad_dev->ref, ib_umad_release_dev);
kobject_put(&umad_dev->kobj);
}

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

kref_put(&umad_dev->ref, ib_umad_release_dev);
kobject_put(&umad_dev->kobj);
}

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

0 comments on commit e8b367f

Please sign in to comment.