]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
platform/x86: lenovo-wmi-other: Balance IDA id allocation and free
authorRong Zhang <i@rong.moe>
Sun, 10 May 2026 04:25:32 +0000 (04:25 +0000)
committerIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
Mon, 11 May 2026 11:29:00 +0000 (14:29 +0300)
Currently, the IDA id is only freed on wmi-other device removal or
failure to create firmware-attributes device, kset, or attributes. It
leaks IDA ids if the wmi-other device is bound multiple times, as the
unbind callback never frees the previously allocated IDA id.
Additionally, if the wmi-other device has failed to create a
firmware-attributes device before it gets removed, the wmi-device
removal callback double frees the same IDA id.

These bugs were found by sashiko.dev [1].

Fix them by moving ida_free() into lwmi_om_fw_attr_remove() so it is
balanced with ida_alloc() in lwmi_om_fw_attr_add(). With them fixed,
properly set and utilize the validity of priv->ida_id to balance
firmware-attributes registration and removal, without relying on
propagating the registration error to the component framework, which is
more reliable and aligns with the hwmon device registration and removal
sequences.

No functional change intended.

Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Fixes: edc4b183b794 ("platform/x86: Add Lenovo Other Mode WMI Driver")
Cc: stable@vger.kernel.org
Link: https://sashiko.dev/#/patchset/20260331181208.421552-1-derekjohn.clark%40gmail.com
Signed-off-by: Rong Zhang <i@rong.moe>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
Link: https://patch.msgid.link/20260510042546.436874-3-derekjohn.clark@gmail.com
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
drivers/platform/x86/lenovo/wmi-other.c

index 6c2febe1a595caa8a3dd85a91d4233e3c0240738..ebef3649d0a78dfe45b4655cc3a7f143c0cc2d6c 100644 (file)
@@ -959,17 +959,17 @@ static struct capdata01_attr_group cd01_attr_groups[] = {
 /**
  * lwmi_om_fw_attr_add() - Register all firmware_attributes_class members
  * @priv: The Other Mode driver data.
- *
- * Return: Either 0, or an error code.
  */
-static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
+static void lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
 {
        unsigned int i;
        int err;
 
-       priv->ida_id = ida_alloc(&lwmi_om_ida, GFP_KERNEL);
-       if (priv->ida_id < 0)
-               return priv->ida_id;
+       err = ida_alloc(&lwmi_om_ida, GFP_KERNEL);
+       if (err < 0)
+               goto err_no_ida;
+
+       priv->ida_id = err;
 
        priv->fw_attr_dev = device_create(&firmware_attributes_class, NULL,
                                          MKDEV(0, 0), NULL, "%s-%u",
@@ -995,7 +995,7 @@ static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
 
                cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
        }
-       return 0;
+       return;
 
 err_remove_groups:
        while (i--)
@@ -1009,7 +1009,12 @@ err_destroy_classdev:
 
 err_free_ida:
        ida_free(&lwmi_om_ida, priv->ida_id);
-       return err;
+
+err_no_ida:
+       priv->ida_id = -EIDRM;
+
+       dev_warn(&priv->wdev->dev,
+                "failed to register firmware-attributes device: %d\n", err);
 }
 
 /**
@@ -1018,12 +1023,17 @@ err_free_ida:
  */
 static void lwmi_om_fw_attr_remove(struct lwmi_om_priv *priv)
 {
+       if (priv->ida_id < 0)
+               return;
+
        for (unsigned int i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++)
                sysfs_remove_group(&priv->fw_attr_kset->kobj,
                                   cd01_attr_groups[i].attr_group);
 
        kset_unregister(priv->fw_attr_kset);
        device_unregister(priv->fw_attr_dev);
+       ida_free(&lwmi_om_ida, priv->ida_id);
+       priv->ida_id = -EIDRM;
 }
 
 /* ======== Self (master: lenovo-wmi-other) ======== */
@@ -1065,7 +1075,9 @@ static int lwmi_om_master_bind(struct device *dev)
 
        lwmi_om_fan_info_collect_cd00(priv);
 
-       return lwmi_om_fw_attr_add(priv);
+       lwmi_om_fw_attr_add(priv);
+
+       return 0;
 }
 
 /**
@@ -1117,13 +1129,7 @@ static int lwmi_other_probe(struct wmi_device *wdev, const void *context)
 
 static void lwmi_other_remove(struct wmi_device *wdev)
 {
-       struct lwmi_om_priv *priv = dev_get_drvdata(&wdev->dev);
-
        component_master_del(&wdev->dev, &lwmi_om_master_ops);
-
-       /* No IDA to free if the driver is never bound to its components. */
-       if (priv->ida_id >= 0)
-               ida_free(&lwmi_om_ida, priv->ida_id);
 }
 
 static const struct wmi_device_id lwmi_other_id_table[] = {