Re: [PATCH] Bluetooth: Fix sleeping function called from invalid context in hci_cmd_sync_submit()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On Thu, Sep 7, 2023 at 5:22 AM Ziyang Xuan
<william.xuanziyang@xxxxxxxxxx> wrote:
>
> Syzbot reports a sleeping function called from invalid context problem.
>
> BUG: sleeping function called from invalid context at kernel/locking/mutex.c:580
> in_atomic(): 0, irqs_disabled(): 0, non_block: 0, pid: 4440, name: kworker/u5:1
> preempt_count: 0, expected: 0
> RCU nest depth: 1, expected: 0
> ...
> Call Trace:
>  <TASK>
>  __dump_stack lib/dump_stack.c:88 [inline]
>  dump_stack_lvl+0x125/0x1b0 lib/dump_stack.c:106
>  __might_resched+0x3c3/0x5e0 kernel/sched/core.c:10187
>  __mutex_lock_common kernel/locking/mutex.c:580 [inline]
>  __mutex_lock+0xee/0x1340 kernel/locking/mutex.c:747
>  hci_cmd_sync_submit+0x3f/0x340 net/bluetooth/hci_sync.c:699
>  hci_cmd_sync_queue+0x79/0xa0 net/bluetooth/hci_sync.c:739
>  hci_abort_conn+0x15b/0x330 net/bluetooth/hci_conn.c:2928
>  hci_disconnect+0xc4/0x220 net/bluetooth/hci_conn.c:258
>  hci_link_tx_to net/bluetooth/hci_core.c:3421 [inline]
>  __check_timeout net/bluetooth/hci_core.c:3567 [inline]
>  __check_timeout+0x331/0x4e0 net/bluetooth/hci_core.c:3547
>  hci_sched_le net/bluetooth/hci_core.c:3750 [inline]
>  hci_tx_work+0x818/0x1d30 net/bluetooth/hci_core.c:3828
>  process_one_work+0xaa2/0x16f0 kernel/workqueue.c:2600
>  worker_thread+0x687/0x1110 kernel/workqueue.c:2751
>  kthread+0x33a/0x430 kernel/kthread.c:389
>  ret_from_fork+0x2c/0x70 arch/x86/kernel/process.c:145
>  ret_from_fork_asm+0x11/0x20 arch/x86/entry/entry_64.S:304
>
> Use rcu_read_lock() to protect hci_conn_hash->list in hci_link_tx_to().
> rcu_read_lock() will disable preempt when CONFIG_PREEMPT_RCU is enabled.
> When it needs to abort connection, it will submit a command by
> hci_cmd_sync_submit() which has some sleeping functions like mutex_lock()
> and kmalloc(, GFP_KERNEL). That triggers the bug.
>
> Convert cmd_sync_work_lock and unregister_lock to spin_lock type and
> replace GFP_KERNEL with GFP_ATOMIC to fix the bug.

Seems like this was already fixed by:

https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git/commit/?id=d606d5f4024de697167584ad5136a3e56d7debcd

> Fixes: 1857c19941c8 ("Bluetooth: hci_sync: add lock to protect HCI_UNREGISTER")
> Fixes: 6a98e3836fa2 ("Bluetooth: Add helper for serialized HCI command execution")
> Reported-by: syzbot+e7be5be00de0c3c2d782@xxxxxxxxxxxxxxxxxxxxxxxxx
> Closes: https://syzkaller.appspot.com/bug?extid=e7be5be00de0c3c2d782
> Signed-off-by: Ziyang Xuan <william.xuanziyang@xxxxxxxxxx>
> ---
>  include/net/bluetooth/hci_core.h |  4 ++--
>  net/bluetooth/hci_core.c         |  4 ++--
>  net/bluetooth/hci_sync.c         | 28 +++++++++++++++-------------
>  3 files changed, 19 insertions(+), 17 deletions(-)
>
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index e6359f7346f1..7a046a80a1ac 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -514,8 +514,8 @@ struct hci_dev {
>         struct work_struct      error_reset;
>         struct work_struct      cmd_sync_work;
>         struct list_head        cmd_sync_work_list;
> -       struct mutex            cmd_sync_work_lock;
> -       struct mutex            unregister_lock;
> +       spinlock_t              cmd_sync_work_lock;
> +       spinlock_t              unregister_lock;
>         struct work_struct      cmd_sync_cancel_work;
>         struct work_struct      reenable_adv_work;
>
> diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
> index a5992f1b3c9b..c94bfde68228 100644
> --- a/net/bluetooth/hci_core.c
> +++ b/net/bluetooth/hci_core.c
> @@ -2716,9 +2716,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);
> +       spin_lock(&hdev->unregister_lock);
>         hci_dev_set_flag(hdev, HCI_UNREGISTER);
> -       mutex_unlock(&hdev->unregister_lock);
> +       spin_unlock(&hdev->unregister_lock);
>
>         write_lock(&hci_dev_list_lock);
>         list_del(&hdev->list);
> diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
> index 9b93653c6197..4356ee29cd5f 100644
> --- a/net/bluetooth/hci_sync.c
> +++ b/net/bluetooth/hci_sync.c
> @@ -286,13 +286,13 @@ static void hci_cmd_sync_work(struct work_struct *work)
>         while (1) {
>                 struct hci_cmd_sync_work_entry *entry;
>
> -               mutex_lock(&hdev->cmd_sync_work_lock);
> +               spin_lock(&hdev->cmd_sync_work_lock);
>                 entry = list_first_entry_or_null(&hdev->cmd_sync_work_list,
>                                                  struct hci_cmd_sync_work_entry,
>                                                  list);
>                 if (entry)
>                         list_del(&entry->list);
> -               mutex_unlock(&hdev->cmd_sync_work_lock);
> +               spin_unlock(&hdev->cmd_sync_work_lock);
>
>                 if (!entry)
>                         break;
> @@ -629,8 +629,8 @@ 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);
> +       spin_lock_init(&hdev->cmd_sync_work_lock);
> +       spin_lock_init(&hdev->unregister_lock);
>
>         INIT_WORK(&hdev->cmd_sync_cancel_work, hci_cmd_sync_cancel_work);
>         INIT_WORK(&hdev->reenable_adv_work, reenable_adv);
> @@ -646,15 +646,17 @@ void hci_cmd_sync_clear(struct hci_dev *hdev)
>         cancel_work_sync(&hdev->cmd_sync_work);
>         cancel_work_sync(&hdev->reenable_adv_work);
>
> -       mutex_lock(&hdev->cmd_sync_work_lock);
> +       spin_lock(&hdev->cmd_sync_work_lock);
>         list_for_each_entry_safe(entry, tmp, &hdev->cmd_sync_work_list, list) {
> +               list_del(&entry->list);
> +               spin_unlock(&hdev->cmd_sync_work_lock);
> +
>                 if (entry->destroy)
>                         entry->destroy(hdev, entry->data, -ECANCELED);
> -
> -               list_del(&entry->list);
>                 kfree(entry);
> +               spin_lock(&hdev->cmd_sync_work_lock);
>         }
> -       mutex_unlock(&hdev->cmd_sync_work_lock);
> +       spin_unlock(&hdev->cmd_sync_work_lock);
>  }
>
>  void __hci_cmd_sync_cancel(struct hci_dev *hdev, int err)
> @@ -696,13 +698,13 @@ int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
>         struct hci_cmd_sync_work_entry *entry;
>         int err = 0;
>
> -       mutex_lock(&hdev->unregister_lock);
> +       spin_lock(&hdev->unregister_lock);
>         if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
>                 err = -ENODEV;
>                 goto unlock;
>         }
>
> -       entry = kmalloc(sizeof(*entry), GFP_KERNEL);
> +       entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
>         if (!entry) {
>                 err = -ENOMEM;
>                 goto unlock;
> @@ -711,14 +713,14 @@ int hci_cmd_sync_submit(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
>         entry->data = data;
>         entry->destroy = destroy;
>
> -       mutex_lock(&hdev->cmd_sync_work_lock);
> +       spin_lock(&hdev->cmd_sync_work_lock);
>         list_add_tail(&entry->list, &hdev->cmd_sync_work_list);
> -       mutex_unlock(&hdev->cmd_sync_work_lock);
> +       spin_unlock(&hdev->cmd_sync_work_lock);
>
>         queue_work(hdev->req_workqueue, &hdev->cmd_sync_work);
>
>  unlock:
> -       mutex_unlock(&hdev->unregister_lock);
> +       spin_unlock(&hdev->unregister_lock);
>         return err;
>  }
>  EXPORT_SYMBOL(hci_cmd_sync_submit);
> --
> 2.25.1
>


-- 
Luiz Augusto von Dentz




[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux