Bluetooth: hci_sync: add lock to protect HCI_UNREGISTER
authorZhengping Jiang <jiangzp@google.com>
Thu, 25 May 2023 00:11:58 +0000 (17:11 -0700)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Tue, 6 Jun 2023 00:13:14 +0000 (17:13 -0700)
When the HCI_UNREGISTER flag is set, no jobs should be scheduled. Fix
potential race when HCI_UNREGISTER is set after the flag is tested in
hci_cmd_sync_queue.

Fixes: 0b94f2651f56 ("Bluetooth: hci_sync: Fix queuing commands when HCI_UNREGISTER is set")
Signed-off-by: Zhengping Jiang <jiangzp@google.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
include/net/bluetooth/hci_core.h
net/bluetooth/hci_core.c
net/bluetooth/hci_sync.c

index 8baf346..fe38938 100644 (file)
@@ -515,6 +515,7 @@ struct hci_dev {
        struct work_struct      cmd_sync_work;
        struct list_head        cmd_sync_work_list;
        struct mutex            cmd_sync_work_lock;
+       struct mutex            unregister_lock;
        struct work_struct      cmd_sync_cancel_work;
        struct work_struct      reenable_adv_work;
 
index 0164b56..48917c6 100644 (file)
@@ -2686,7 +2686,9 @@ void hci_unregister_dev(struct hci_dev *hdev)
 {
        BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
 
+       mutex_lock(&hdev->unregister_lock);
        hci_dev_set_flag(hdev, HCI_UNREGISTER);
+       mutex_unlock(&hdev->unregister_lock);
 
        write_lock(&hci_dev_list_lock);
        list_del(&hdev->list);
index 647a8ce..a59695f 100644 (file)
@@ -629,6 +629,7 @@ void hci_cmd_sync_init(struct hci_dev *hdev)
        INIT_WORK(&hdev->cmd_sync_work, hci_cmd_sync_work);
        INIT_LIST_HEAD(&hdev->cmd_sync_work_list);
        mutex_init(&hdev->cmd_sync_work_lock);
+       mutex_init(&hdev->unregister_lock);
 
        INIT_WORK(&hdev->cmd_sync_cancel_work, hci_cmd_sync_cancel_work);
        INIT_WORK(&hdev->reenable_adv_work, reenable_adv);
@@ -692,14 +693,19 @@ int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
                        void *data, hci_cmd_sync_work_destroy_t destroy)
 {
        struct hci_cmd_sync_work_entry *entry;
+       int err = 0;
 
-       if (hci_dev_test_flag(hdev, HCI_UNREGISTER))
-               return -ENODEV;
+       mutex_lock(&hdev->unregister_lock);
+       if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
+               err = -ENODEV;
+               goto unlock;
+       }
 
        entry = kmalloc(sizeof(*entry), GFP_KERNEL);
-       if (!entry)
-               return -ENOMEM;
-
+       if (!entry) {
+               err = -ENOMEM;
+               goto unlock;
+       }
        entry->func = func;
        entry->data = data;
        entry->destroy = destroy;
@@ -710,7 +716,9 @@ int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
 
        queue_work(hdev->req_workqueue, &hdev->cmd_sync_work);
 
-       return 0;
+unlock:
+       mutex_unlock(&hdev->unregister_lock);
+       return err;
 }
 EXPORT_SYMBOL(hci_cmd_sync_submit);