]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
IB/mlx4: Fix refcount leak in add_port() error path
authorGuangshuo Li <lgs201920130244@gmail.com>
Mon, 18 May 2026 02:19:10 +0000 (10:19 +0800)
committerJason Gunthorpe <jgg@nvidia.com>
Mon, 25 May 2026 14:25:17 +0000 (11:25 -0300)
After kobject_init_and_add(), the lifetime of the embedded struct
kobject is expected to be managed through the kobject core reference
counting.

In add_port(), failure paths after kobject_init_and_add() must not free
struct mlx4_port directly, because the embedded kobject is then managed
by the kobject core. Freeing it directly leaves the kobject reference
counting unbalanced and can lead to incorrect lifetime handling.

Allocate the pkey and gid attribute arrays before kobject_init_and_add(),
so failures before kobject initialization can be handled by directly
freeing the allocated memory. Once kobject_init_and_add() has been
called, unwind later failures by removing any successfully created sysfs
groups, calling kobject_del(), and then releasing the embedded kobject
with kobject_put().

Fixes: c1e7e466120b ("IB/mlx4: Add iov directory in sysfs under the ib device")
Link: https://patch.msgid.link/r/20260518021910.972900-1-lgs201920130244@gmail.com
Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
drivers/infiniband/hw/mlx4/sysfs.c

index b8fa4ecfc961060cd72c0947d7cf0877438867e3..e688ad66a895fe5307ff87a8397f3a5cf3c3fbd8 100644 (file)
@@ -636,12 +636,6 @@ static int add_port(struct mlx4_ib_dev *dev, int port_num, int slave)
        p->port_num = port_num;
        p->slave = slave;
 
-       ret = kobject_init_and_add(&p->kobj, &port_type,
-                                  kobject_get(dev->dev_ports_parent[slave]),
-                                  "%d", port_num);
-       if (ret)
-               goto err_alloc;
-
        p->pkey_group.name  = "pkey_idx";
        p->pkey_group.attrs =
                alloc_group_attrs(show_port_pkey,
@@ -649,13 +643,9 @@ static int add_port(struct mlx4_ib_dev *dev, int port_num, int slave)
                                  dev->dev->caps.pkey_table_len[port_num]);
        if (!p->pkey_group.attrs) {
                ret = -ENOMEM;
-               goto err_alloc;
+               goto err_free_port;
        }
 
-       ret = sysfs_create_group(&p->kobj, &p->pkey_group);
-       if (ret)
-               goto err_free_pkey;
-
        p->gid_group.name  = "gid_idx";
        p->gid_group.attrs = alloc_group_attrs(show_port_gid_idx, NULL, 1);
        if (!p->gid_group.attrs) {
@@ -663,28 +653,47 @@ static int add_port(struct mlx4_ib_dev *dev, int port_num, int slave)
                goto err_free_pkey;
        }
 
+       ret = kobject_init_and_add(&p->kobj, &port_type,
+                                  kobject_get(dev->dev_ports_parent[slave]),
+                                  "%d", port_num);
+       if (ret)
+               goto err_put;
+
+       ret = sysfs_create_group(&p->kobj, &p->pkey_group);
+       if (ret)
+               goto err_del;
+
        ret = sysfs_create_group(&p->kobj, &p->gid_group);
        if (ret)
-               goto err_free_gid;
+               goto err_remove_pkey;
 
        ret = add_vf_smi_entries(p);
        if (ret)
-               goto err_free_gid;
+               goto err_remove_gid;
 
        list_add_tail(&p->kobj.entry, &dev->pkeys.pkey_port_list[slave]);
        return 0;
 
-err_free_gid:
-       kfree(p->gid_group.attrs[0]);
-       kfree(p->gid_group.attrs);
+err_remove_gid:
+       sysfs_remove_group(&p->kobj, &p->gid_group);
+
+err_remove_pkey:
+       sysfs_remove_group(&p->kobj, &p->pkey_group);
+
+err_del:
+       kobject_del(&p->kobj);
+
+err_put:
+       kobject_put(dev->dev_ports_parent[slave]);
+       kobject_put(&p->kobj);
+       return ret;
 
 err_free_pkey:
        for (i = 0; i < dev->dev->caps.pkey_table_len[port_num]; ++i)
                kfree(p->pkey_group.attrs[i]);
        kfree(p->pkey_group.attrs);
 
-err_alloc:
-       kobject_put(dev->dev_ports_parent[slave]);
+err_free_port:
        kfree(p);
        return ret;
 }