Skip to content

Commit e044809

Browse files
Tetsuo Handatorvalds
authored andcommitted
Bluetooth: defer cleanup of resources in hci_unregister_dev()
syzbot is hitting might_sleep() warning at hci_sock_dev_event() due to calling lock_sock() with rw spinlock held [1]. It seems that history of this locking problem is a trial and error. Commit b40df57 ("[PATCH] bluetooth: fix socket locking in hci_sock_dev_event()") in 2.6.21-rc4 changed bh_lock_sock() to lock_sock() as an attempt to fix lockdep warning. Then, commit 4ce61d1 ("[BLUETOOTH]: Fix locking in hci_sock_dev_event().") in 2.6.22-rc2 changed lock_sock() to local_bh_disable() + bh_lock_sock_nested() as an attempt to fix the sleep in atomic context warning. Then, commit 4b5dd69 ("Bluetooth: Remove local_bh_disable() from hci_sock.c") in 3.3-rc1 removed local_bh_disable(). Then, commit e305509 ("Bluetooth: use correct lock to prevent UAF of hdev object") in 5.13-rc5 again changed bh_lock_sock_nested() to lock_sock() as an attempt to fix CVE-2021-3573. This difficulty comes from current implementation that hci_sock_dev_event(HCI_DEV_UNREG) is responsible for dropping all references from sockets because hci_unregister_dev() immediately reclaims resources as soon as returning from hci_sock_dev_event(HCI_DEV_UNREG). But the history suggests that hci_sock_dev_event(HCI_DEV_UNREG) was not doing what it should do. Therefore, instead of trying to detach sockets from device, let's accept not detaching sockets from device at hci_sock_dev_event(HCI_DEV_UNREG), by moving actual cleanup of resources from hci_unregister_dev() to hci_cleanup_dev() which is called by bt_host_release() when all references to this unregistered device (which is a kobject) are gone. Since hci_sock_dev_event(HCI_DEV_UNREG) no longer resets hci_pi(sk)->hdev, we need to check whether this device was unregistered and return an error based on HCI_UNREGISTER flag. There might be subtle behavioral difference in "monitor the hdev" functionality; please report if you found something went wrong due to this patch. Link: https://syzkaller.appspot.com/bug?extid=a5df189917e79d5e59c9 [1] Reported-by: syzbot <[email protected]> Suggested-by: Linus Torvalds <[email protected]> Signed-off-by: Tetsuo Handa <[email protected]> Fixes: e305509 ("Bluetooth: use correct lock to prevent UAF of hdev object") Acked-by: Luiz Augusto von Dentz <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 0b53abf commit e044809

File tree

4 files changed

+45
-24
lines changed

4 files changed

+45
-24
lines changed

include/net/bluetooth/hci_core.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,6 +1230,7 @@ struct hci_dev *hci_alloc_dev(void);
12301230
void hci_free_dev(struct hci_dev *hdev);
12311231
int hci_register_dev(struct hci_dev *hdev);
12321232
void hci_unregister_dev(struct hci_dev *hdev);
1233+
void hci_cleanup_dev(struct hci_dev *hdev);
12331234
int hci_suspend_dev(struct hci_dev *hdev);
12341235
int hci_resume_dev(struct hci_dev *hdev);
12351236
int hci_reset_dev(struct hci_dev *hdev);

net/bluetooth/hci_core.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3996,14 +3996,10 @@ EXPORT_SYMBOL(hci_register_dev);
39963996
/* Unregister HCI device */
39973997
void hci_unregister_dev(struct hci_dev *hdev)
39983998
{
3999-
int id;
4000-
40013999
BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
40024000

40034001
hci_dev_set_flag(hdev, HCI_UNREGISTER);
40044002

4005-
id = hdev->id;
4006-
40074003
write_lock(&hci_dev_list_lock);
40084004
list_del(&hdev->list);
40094005
write_unlock(&hci_dev_list_lock);
@@ -4038,7 +4034,14 @@ void hci_unregister_dev(struct hci_dev *hdev)
40384034
}
40394035

40404036
device_del(&hdev->dev);
4037+
/* Actual cleanup is deferred until hci_cleanup_dev(). */
4038+
hci_dev_put(hdev);
4039+
}
4040+
EXPORT_SYMBOL(hci_unregister_dev);
40414041

4042+
/* Cleanup HCI device */
4043+
void hci_cleanup_dev(struct hci_dev *hdev)
4044+
{
40424045
debugfs_remove_recursive(hdev->debugfs);
40434046
kfree_const(hdev->hw_info);
40444047
kfree_const(hdev->fw_info);
@@ -4063,11 +4066,8 @@ void hci_unregister_dev(struct hci_dev *hdev)
40634066
hci_blocked_keys_clear(hdev);
40644067
hci_dev_unlock(hdev);
40654068

4066-
hci_dev_put(hdev);
4067-
4068-
ida_simple_remove(&hci_index_ida, id);
4069+
ida_simple_remove(&hci_index_ida, hdev->id);
40694070
}
4070-
EXPORT_SYMBOL(hci_unregister_dev);
40714071

40724072
/* Suspend HCI device */
40734073
int hci_suspend_dev(struct hci_dev *hdev)

net/bluetooth/hci_sock.c

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,17 @@ struct hci_pinfo {
5959
char comm[TASK_COMM_LEN];
6060
};
6161

62+
static struct hci_dev *hci_hdev_from_sock(struct sock *sk)
63+
{
64+
struct hci_dev *hdev = hci_pi(sk)->hdev;
65+
66+
if (!hdev)
67+
return ERR_PTR(-EBADFD);
68+
if (hci_dev_test_flag(hdev, HCI_UNREGISTER))
69+
return ERR_PTR(-EPIPE);
70+
return hdev;
71+
}
72+
6273
void hci_sock_set_flag(struct sock *sk, int nr)
6374
{
6475
set_bit(nr, &hci_pi(sk)->flags);
@@ -759,19 +770,13 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event)
759770
if (event == HCI_DEV_UNREG) {
760771
struct sock *sk;
761772

762-
/* Detach sockets from device */
773+
/* Wake up sockets using this dead device */
763774
read_lock(&hci_sk_list.lock);
764775
sk_for_each(sk, &hci_sk_list.head) {
765-
lock_sock(sk);
766776
if (hci_pi(sk)->hdev == hdev) {
767-
hci_pi(sk)->hdev = NULL;
768777
sk->sk_err = EPIPE;
769-
sk->sk_state = BT_OPEN;
770778
sk->sk_state_change(sk);
771-
772-
hci_dev_put(hdev);
773779
}
774-
release_sock(sk);
775780
}
776781
read_unlock(&hci_sk_list.lock);
777782
}
@@ -930,10 +935,10 @@ static int hci_sock_reject_list_del(struct hci_dev *hdev, void __user *arg)
930935
static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
931936
unsigned long arg)
932937
{
933-
struct hci_dev *hdev = hci_pi(sk)->hdev;
938+
struct hci_dev *hdev = hci_hdev_from_sock(sk);
934939

935-
if (!hdev)
936-
return -EBADFD;
940+
if (IS_ERR(hdev))
941+
return PTR_ERR(hdev);
937942

938943
if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL))
939944
return -EBUSY;
@@ -1103,6 +1108,18 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
11031108

11041109
lock_sock(sk);
11051110

1111+
/* Allow detaching from dead device and attaching to alive device, if
1112+
* the caller wants to re-bind (instead of close) this socket in
1113+
* response to hci_sock_dev_event(HCI_DEV_UNREG) notification.
1114+
*/
1115+
hdev = hci_pi(sk)->hdev;
1116+
if (hdev && hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
1117+
hci_pi(sk)->hdev = NULL;
1118+
sk->sk_state = BT_OPEN;
1119+
hci_dev_put(hdev);
1120+
}
1121+
hdev = NULL;
1122+
11061123
if (sk->sk_state == BT_BOUND) {
11071124
err = -EALREADY;
11081125
goto done;
@@ -1379,9 +1396,9 @@ static int hci_sock_getname(struct socket *sock, struct sockaddr *addr,
13791396

13801397
lock_sock(sk);
13811398

1382-
hdev = hci_pi(sk)->hdev;
1383-
if (!hdev) {
1384-
err = -EBADFD;
1399+
hdev = hci_hdev_from_sock(sk);
1400+
if (IS_ERR(hdev)) {
1401+
err = PTR_ERR(hdev);
13851402
goto done;
13861403
}
13871404

@@ -1743,9 +1760,9 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
17431760
goto done;
17441761
}
17451762

1746-
hdev = hci_pi(sk)->hdev;
1747-
if (!hdev) {
1748-
err = -EBADFD;
1763+
hdev = hci_hdev_from_sock(sk);
1764+
if (IS_ERR(hdev)) {
1765+
err = PTR_ERR(hdev);
17491766
goto done;
17501767
}
17511768

net/bluetooth/hci_sysfs.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ void hci_conn_del_sysfs(struct hci_conn *conn)
8383
static void bt_host_release(struct device *dev)
8484
{
8585
struct hci_dev *hdev = to_hci_dev(dev);
86+
87+
if (hci_dev_test_flag(hdev, HCI_UNREGISTER))
88+
hci_cleanup_dev(hdev);
8689
kfree(hdev);
8790
module_put(THIS_MODULE);
8891
}

0 commit comments

Comments
 (0)