]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
Bluetooth: vhci: Prevent use-after-free by removing debugfs files early
authorIvan Pravdin <ipravdin.official@gmail.com>
Wed, 27 Aug 2025 14:53:25 +0000 (10:53 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 9 Sep 2025 16:58:07 +0000 (18:58 +0200)
[ Upstream commit 28010791193a4503f054e8d69a950ef815deb539 ]

Move the creation of debugfs files into a dedicated function, and ensure
they are explicitly removed during vhci_release(), before associated
data structures are freed.

Previously, debugfs files such as "force_suspend", "force_wakeup", and
others were created under hdev->debugfs but not removed in
vhci_release(). Since vhci_release() frees the backing vhci_data
structure, any access to these files after release would result in
use-after-free errors.

Although hdev->debugfs is later freed in hci_release_dev(), user can
access files after vhci_data is freed but before hdev->debugfs is
released.

Fixes: ab4e4380d4e1 ("Bluetooth: Add vhci devcoredump support")
Signed-off-by: Ivan Pravdin <ipravdin.official@gmail.com>
Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/bluetooth/hci_vhci.c

index 9ac22e4a070befa13f6d6ac45e7b35452698475e..59872e73c1878fa8f49acaa2f8e8d5d88a507d96 100644 (file)
@@ -380,6 +380,28 @@ static const struct file_operations force_devcoredump_fops = {
        .write          = force_devcd_write,
 };
 
+static void vhci_debugfs_init(struct vhci_data *data)
+{
+       struct hci_dev *hdev = data->hdev;
+
+       debugfs_create_file("force_suspend", 0644, hdev->debugfs, data,
+                           &force_suspend_fops);
+
+       debugfs_create_file("force_wakeup", 0644, hdev->debugfs, data,
+                           &force_wakeup_fops);
+
+       if (IS_ENABLED(CONFIG_BT_MSFTEXT))
+               debugfs_create_file("msft_opcode", 0644, hdev->debugfs, data,
+                                   &msft_opcode_fops);
+
+       if (IS_ENABLED(CONFIG_BT_AOSPEXT))
+               debugfs_create_file("aosp_capable", 0644, hdev->debugfs, data,
+                                   &aosp_capable_fops);
+
+       debugfs_create_file("force_devcoredump", 0644, hdev->debugfs, data,
+                           &force_devcoredump_fops);
+}
+
 static int __vhci_create_device(struct vhci_data *data, __u8 opcode)
 {
        struct hci_dev *hdev;
@@ -433,22 +455,8 @@ static int __vhci_create_device(struct vhci_data *data, __u8 opcode)
                return -EBUSY;
        }
 
-       debugfs_create_file("force_suspend", 0644, hdev->debugfs, data,
-                           &force_suspend_fops);
-
-       debugfs_create_file("force_wakeup", 0644, hdev->debugfs, data,
-                           &force_wakeup_fops);
-
-       if (IS_ENABLED(CONFIG_BT_MSFTEXT))
-               debugfs_create_file("msft_opcode", 0644, hdev->debugfs, data,
-                                   &msft_opcode_fops);
-
-       if (IS_ENABLED(CONFIG_BT_AOSPEXT))
-               debugfs_create_file("aosp_capable", 0644, hdev->debugfs, data,
-                                   &aosp_capable_fops);
-
-       debugfs_create_file("force_devcoredump", 0644, hdev->debugfs, data,
-                           &force_devcoredump_fops);
+       if (!IS_ERR_OR_NULL(hdev->debugfs))
+               vhci_debugfs_init(data);
 
        hci_skb_pkt_type(skb) = HCI_VENDOR_PKT;
 
@@ -650,6 +658,21 @@ static int vhci_open(struct inode *inode, struct file *file)
        return 0;
 }
 
+static void vhci_debugfs_remove(struct hci_dev *hdev)
+{
+       debugfs_lookup_and_remove("force_suspend", hdev->debugfs);
+
+       debugfs_lookup_and_remove("force_wakeup", hdev->debugfs);
+
+       if (IS_ENABLED(CONFIG_BT_MSFTEXT))
+               debugfs_lookup_and_remove("msft_opcode", hdev->debugfs);
+
+       if (IS_ENABLED(CONFIG_BT_AOSPEXT))
+               debugfs_lookup_and_remove("aosp_capable", hdev->debugfs);
+
+       debugfs_lookup_and_remove("force_devcoredump", hdev->debugfs);
+}
+
 static int vhci_release(struct inode *inode, struct file *file)
 {
        struct vhci_data *data = file->private_data;
@@ -661,6 +684,8 @@ static int vhci_release(struct inode *inode, struct file *file)
        hdev = data->hdev;
 
        if (hdev) {
+               if (!IS_ERR_OR_NULL(hdev->debugfs))
+                       vhci_debugfs_remove(hdev);
                hci_unregister_dev(hdev);
                hci_free_dev(hdev);
        }