Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Bluetooth: use hdev->workqueue when queuing hdev->{cmd,ncmd}_timer works

syzbot is reporting attempt to schedule hdev->cmd_work work from system_wq
WQ into hdev->workqueue WQ which is under draining operation [1], for
commit c8efcc2589464ac7 ("workqueue: allow chained queueing during
destruction") does not allow such operation.

The check introduced by commit 877afadad2dce8aa ("Bluetooth: When HCI work
queue is drained, only queue chained work") was incomplete.

Use hdev->workqueue WQ when queuing hdev->{cmd,ncmd}_timer works because
hci_{cmd,ncmd}_timeout() calls queue_work(hdev->workqueue). Also, protect
the queuing operation with RCU read lock in order to avoid calling
queue_delayed_work() after cancel_delayed_work() completed.

Link: https://syzkaller.appspot.com/bug?extid=243b7d89777f90f7613b [1]
Reported-by: syzbot <syzbot+243b7d89777f90f7613b@syzkaller.appspotmail.com>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Fixes: 877afadad2dce8aa ("Bluetooth: When HCI work queue is drained, only queue chained work")
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

authored by

Tetsuo Handa and committed by
Luiz Augusto von Dentz
deee93d1 2d2cb306

+17 -4
+13 -2
net/bluetooth/hci_core.c
··· 597 597 598 598 /* Cancel these to avoid queueing non-chained pending work */ 599 599 hci_dev_set_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE); 600 + /* Wait for 601 + * 602 + * if (!hci_dev_test_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE)) 603 + * queue_delayed_work(&hdev->{cmd,ncmd}_timer) 604 + * 605 + * inside RCU section to see the flag or complete scheduling. 606 + */ 607 + synchronize_rcu(); 608 + /* Explicitly cancel works in case scheduled after setting the flag. */ 600 609 cancel_delayed_work(&hdev->cmd_timer); 601 610 cancel_delayed_work(&hdev->ncmd_timer); 602 611 ··· 4072 4063 if (res < 0) 4073 4064 __hci_cmd_sync_cancel(hdev, -res); 4074 4065 4066 + rcu_read_lock(); 4075 4067 if (test_bit(HCI_RESET, &hdev->flags) || 4076 4068 hci_dev_test_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE)) 4077 4069 cancel_delayed_work(&hdev->cmd_timer); 4078 4070 else 4079 - schedule_delayed_work(&hdev->cmd_timer, 4080 - HCI_CMD_TIMEOUT); 4071 + queue_delayed_work(hdev->workqueue, &hdev->cmd_timer, 4072 + HCI_CMD_TIMEOUT); 4073 + rcu_read_unlock(); 4081 4074 } else { 4082 4075 skb_queue_head(&hdev->cmd_q, skb); 4083 4076 queue_work(hdev->workqueue, &hdev->cmd_work);
+4 -2
net/bluetooth/hci_event.c
··· 3767 3767 { 3768 3768 cancel_delayed_work(&hdev->cmd_timer); 3769 3769 3770 + rcu_read_lock(); 3770 3771 if (!test_bit(HCI_RESET, &hdev->flags)) { 3771 3772 if (ncmd) { 3772 3773 cancel_delayed_work(&hdev->ncmd_timer); 3773 3774 atomic_set(&hdev->cmd_cnt, 1); 3774 3775 } else { 3775 3776 if (!hci_dev_test_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE)) 3776 - schedule_delayed_work(&hdev->ncmd_timer, 3777 - HCI_NCMD_TIMEOUT); 3777 + queue_delayed_work(hdev->workqueue, &hdev->ncmd_timer, 3778 + HCI_NCMD_TIMEOUT); 3778 3779 } 3779 3780 } 3781 + rcu_read_unlock(); 3780 3782 } 3781 3783 3782 3784 static u8 hci_cc_le_read_buffer_size_v2(struct hci_dev *hdev, void *data,