staging: next: configfs: fix release link
authorChristian Gromm <christian.gromm@microchip.com>
Thu, 23 Jan 2020 15:38:22 +0000 (16:38 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 24 Jan 2020 09:08:41 +0000 (10:08 +0100)
The functions link_destroy and link_release are both deleting list items.
link_release, however, does not check whether a certain link has already
been deleted from the list by function link_destroy. By fixing this
this patch prevents a kernel crash when removing the configuration
directory of a link that already has been destroyed.

Signed-off-by: Christian Gromm <christian.gromm@microchip.com>
Link: https://lore.kernel.org/r/1579793906-5054-7-git-send-email-christian.gromm@microchip.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/most/configfs.c

index 034ab96..9a96122 100644 (file)
@@ -128,6 +128,8 @@ static ssize_t mdev_link_create_link_store(struct config_item *item,
                return ret;
        list_add_tail(&mdev_link->list, &mdev_link_list);
        mdev_link->create_link = tmp;
+       mdev_link->destroy_link = false;
+
        return count;
 }
 
@@ -143,13 +145,16 @@ static ssize_t mdev_link_destroy_link_store(struct config_item *item,
                return ret;
        if (!tmp)
                return count;
-       mdev_link->destroy_link = tmp;
+
        ret = most_remove_link(mdev_link->device, mdev_link->channel,
                               mdev_link->comp);
        if (ret)
                return ret;
        if (!list_empty(&mdev_link_list))
                list_del(&mdev_link->list);
+
+       mdev_link->destroy_link = tmp;
+
        return count;
 }
 
@@ -378,13 +383,20 @@ static void mdev_link_release(struct config_item *item)
        struct mdev_link *mdev_link = to_mdev_link(item);
        int ret;
 
-       if (!list_empty(&mdev_link_list)) {
-               ret = most_remove_link(mdev_link->device, mdev_link->channel,
-                                      mdev_link->comp);
-               if (ret && (ret != -ENODEV))
-                       pr_err("Removing link failed.\n");
-               list_del(&mdev_link->list);
+       if (mdev_link->destroy_link)
+               goto free_item;
+
+       ret = most_remove_link(mdev_link->device, mdev_link->channel,
+                              mdev_link->comp);
+       if (ret) {
+               pr_err("Removing link failed.\n");
+               goto free_item;
        }
+
+       if (!list_empty(&mdev_link_list))
+               list_del(&mdev_link->list);
+
+free_item:
        kfree(to_mdev_link(item));
 }