]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
Revert "Bluetooth: hci_core: Fix sleeping function called from invalid context"
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Tue, 4 Mar 2025 15:06:10 +0000 (10:06 -0500)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Fri, 7 Mar 2025 18:03:05 +0000 (13:03 -0500)
This reverts commit 4d94f05558271654670d18c26c912da0c1c15549 which has
problems (see [1]) and is no longer needed since 581dd2dc168f
("Bluetooth: hci_event: Fix using rcu_read_(un)lock while iterating")
has reworked the code where the original bug has been found.

Link: https://lore.kernel.org/linux-bluetooth/877c55ci1r.wl-tiwai@suse.de/T/#t
Fixes: 4d94f0555827 ("Bluetooth: hci_core: Fix sleeping function called from invalid context")
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/iso.c
net/bluetooth/l2cap_core.c
net/bluetooth/rfcomm/core.c
net/bluetooth/sco.c

index f756fac95488af41f88bd6ba814ea8fa81adca26..6281063cbd8e44a91cf5b82e3e1184f8d93a4ac2 100644 (file)
@@ -804,6 +804,7 @@ struct hci_conn_params {
 extern struct list_head hci_dev_list;
 extern struct list_head hci_cb_list;
 extern rwlock_t hci_dev_list_lock;
+extern struct mutex hci_cb_list_lock;
 
 #define hci_dev_set_flag(hdev, nr)             set_bit((nr), (hdev)->dev_flags)
 #define hci_dev_clear_flag(hdev, nr)           clear_bit((nr), (hdev)->dev_flags)
@@ -2010,47 +2011,24 @@ struct hci_cb {
 
        char *name;
 
-       bool (*match)           (struct hci_conn *conn);
        void (*connect_cfm)     (struct hci_conn *conn, __u8 status);
        void (*disconn_cfm)     (struct hci_conn *conn, __u8 status);
        void (*security_cfm)    (struct hci_conn *conn, __u8 status,
-                                __u8 encrypt);
+                                                               __u8 encrypt);
        void (*key_change_cfm)  (struct hci_conn *conn, __u8 status);
        void (*role_switch_cfm) (struct hci_conn *conn, __u8 status, __u8 role);
 };
 
-static inline void hci_cb_lookup(struct hci_conn *conn, struct list_head *list)
-{
-       struct hci_cb *cb, *cpy;
-
-       rcu_read_lock();
-       list_for_each_entry_rcu(cb, &hci_cb_list, list) {
-               if (cb->match && cb->match(conn)) {
-                       cpy = kmalloc(sizeof(*cpy), GFP_ATOMIC);
-                       if (!cpy)
-                               break;
-
-                       *cpy = *cb;
-                       INIT_LIST_HEAD(&cpy->list);
-                       list_add_rcu(&cpy->list, list);
-               }
-       }
-       rcu_read_unlock();
-}
-
 static inline void hci_connect_cfm(struct hci_conn *conn, __u8 status)
 {
-       struct list_head list;
-       struct hci_cb *cb, *tmp;
-
-       INIT_LIST_HEAD(&list);
-       hci_cb_lookup(conn, &list);
+       struct hci_cb *cb;
 
-       list_for_each_entry_safe(cb, tmp, &list, list) {
+       mutex_lock(&hci_cb_list_lock);
+       list_for_each_entry(cb, &hci_cb_list, list) {
                if (cb->connect_cfm)
                        cb->connect_cfm(conn, status);
-               kfree(cb);
        }
+       mutex_unlock(&hci_cb_list_lock);
 
        if (conn->connect_cfm_cb)
                conn->connect_cfm_cb(conn, status);
@@ -2058,43 +2036,22 @@ static inline void hci_connect_cfm(struct hci_conn *conn, __u8 status)
 
 static inline void hci_disconn_cfm(struct hci_conn *conn, __u8 reason)
 {
-       struct list_head list;
-       struct hci_cb *cb, *tmp;
-
-       INIT_LIST_HEAD(&list);
-       hci_cb_lookup(conn, &list);
+       struct hci_cb *cb;
 
-       list_for_each_entry_safe(cb, tmp, &list, list) {
+       mutex_lock(&hci_cb_list_lock);
+       list_for_each_entry(cb, &hci_cb_list, list) {
                if (cb->disconn_cfm)
                        cb->disconn_cfm(conn, reason);
-               kfree(cb);
        }
+       mutex_unlock(&hci_cb_list_lock);
 
        if (conn->disconn_cfm_cb)
                conn->disconn_cfm_cb(conn, reason);
 }
 
-static inline void hci_security_cfm(struct hci_conn *conn, __u8 status,
-                                   __u8 encrypt)
-{
-       struct list_head list;
-       struct hci_cb *cb, *tmp;
-
-       INIT_LIST_HEAD(&list);
-       hci_cb_lookup(conn, &list);
-
-       list_for_each_entry_safe(cb, tmp, &list, list) {
-               if (cb->security_cfm)
-                       cb->security_cfm(conn, status, encrypt);
-               kfree(cb);
-       }
-
-       if (conn->security_cfm_cb)
-               conn->security_cfm_cb(conn, status);
-}
-
 static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
 {
+       struct hci_cb *cb;
        __u8 encrypt;
 
        if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags))
@@ -2102,11 +2059,20 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
 
        encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00;
 
-       hci_security_cfm(conn, status, encrypt);
+       mutex_lock(&hci_cb_list_lock);
+       list_for_each_entry(cb, &hci_cb_list, list) {
+               if (cb->security_cfm)
+                       cb->security_cfm(conn, status, encrypt);
+       }
+       mutex_unlock(&hci_cb_list_lock);
+
+       if (conn->security_cfm_cb)
+               conn->security_cfm_cb(conn, status);
 }
 
 static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status)
 {
+       struct hci_cb *cb;
        __u8 encrypt;
 
        if (conn->state == BT_CONFIG) {
@@ -2133,38 +2099,40 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status)
                        conn->sec_level = conn->pending_sec_level;
        }
 
-       hci_security_cfm(conn, status, encrypt);
+       mutex_lock(&hci_cb_list_lock);
+       list_for_each_entry(cb, &hci_cb_list, list) {
+               if (cb->security_cfm)
+                       cb->security_cfm(conn, status, encrypt);
+       }
+       mutex_unlock(&hci_cb_list_lock);
+
+       if (conn->security_cfm_cb)
+               conn->security_cfm_cb(conn, status);
 }
 
 static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status)
 {
-       struct list_head list;
-       struct hci_cb *cb, *tmp;
-
-       INIT_LIST_HEAD(&list);
-       hci_cb_lookup(conn, &list);
+       struct hci_cb *cb;
 
-       list_for_each_entry_safe(cb, tmp, &list, list) {
+       mutex_lock(&hci_cb_list_lock);
+       list_for_each_entry(cb, &hci_cb_list, list) {
                if (cb->key_change_cfm)
                        cb->key_change_cfm(conn, status);
-               kfree(cb);
        }
+       mutex_unlock(&hci_cb_list_lock);
 }
 
 static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status,
                                                                __u8 role)
 {
-       struct list_head list;
-       struct hci_cb *cb, *tmp;
-
-       INIT_LIST_HEAD(&list);
-       hci_cb_lookup(conn, &list);
+       struct hci_cb *cb;
 
-       list_for_each_entry_safe(cb, tmp, &list, list) {
+       mutex_lock(&hci_cb_list_lock);
+       list_for_each_entry(cb, &hci_cb_list, list) {
                if (cb->role_switch_cfm)
                        cb->role_switch_cfm(conn, status, role);
-               kfree(cb);
        }
+       mutex_unlock(&hci_cb_list_lock);
 }
 
 static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type)
index e7ec12437c8b1687e84c339b7b473918ca5ffa08..012fc107901a6e4502fbad269034ad43450561dc 100644 (file)
@@ -57,6 +57,7 @@ DEFINE_RWLOCK(hci_dev_list_lock);
 
 /* HCI callback list */
 LIST_HEAD(hci_cb_list);
+DEFINE_MUTEX(hci_cb_list_lock);
 
 /* HCI ID Numbering */
 static DEFINE_IDA(hci_index_ida);
@@ -2972,7 +2973,9 @@ int hci_register_cb(struct hci_cb *cb)
 {
        BT_DBG("%p name %s", cb, cb->name);
 
-       list_add_tail_rcu(&cb->list, &hci_cb_list);
+       mutex_lock(&hci_cb_list_lock);
+       list_add_tail(&cb->list, &hci_cb_list);
+       mutex_unlock(&hci_cb_list_lock);
 
        return 0;
 }
@@ -2982,8 +2985,9 @@ int hci_unregister_cb(struct hci_cb *cb)
 {
        BT_DBG("%p name %s", cb, cb->name);
 
-       list_del_rcu(&cb->list);
-       synchronize_rcu();
+       mutex_lock(&hci_cb_list_lock);
+       list_del(&cb->list);
+       mutex_unlock(&hci_cb_list_lock);
 
        return 0;
 }
index 44acddf58a0cd9f7fa2cd865383d21b1a5778b69..0cb52a3308bae489f0d36f5b4a3c406865e604c8 100644 (file)
@@ -2187,11 +2187,6 @@ done:
        return HCI_LM_ACCEPT;
 }
 
-static bool iso_match(struct hci_conn *hcon)
-{
-       return hcon->type == ISO_LINK || hcon->type == LE_LINK;
-}
-
 static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
 {
        if (hcon->type != ISO_LINK) {
@@ -2373,7 +2368,6 @@ drop:
 
 static struct hci_cb iso_cb = {
        .name           = "ISO",
-       .match          = iso_match,
        .connect_cfm    = iso_connect_cfm,
        .disconn_cfm    = iso_disconn_cfm,
 };
index b22078b679726db0599000dcb4f6e2c3e753cff1..c27ea70f71e1e18d52f943231de3883d3000297c 100644 (file)
@@ -7182,11 +7182,6 @@ static struct l2cap_chan *l2cap_global_fixed_chan(struct l2cap_chan *c,
        return NULL;
 }
 
-static bool l2cap_match(struct hci_conn *hcon)
-{
-       return hcon->type == ACL_LINK || hcon->type == LE_LINK;
-}
-
 static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
 {
        struct hci_dev *hdev = hcon->hdev;
@@ -7194,6 +7189,9 @@ static void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
        struct l2cap_chan *pchan;
        u8 dst_type;
 
+       if (hcon->type != ACL_LINK && hcon->type != LE_LINK)
+               return;
+
        BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
 
        if (status) {
@@ -7258,6 +7256,9 @@ int l2cap_disconn_ind(struct hci_conn *hcon)
 
 static void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
 {
+       if (hcon->type != ACL_LINK && hcon->type != LE_LINK)
+               return;
+
        BT_DBG("hcon %p reason %d", hcon, reason);
 
        l2cap_conn_del(hcon, bt_to_errno(reason));
@@ -7565,7 +7566,6 @@ unlock:
 
 static struct hci_cb l2cap_cb = {
        .name           = "L2CAP",
-       .match          = l2cap_match,
        .connect_cfm    = l2cap_connect_cfm,
        .disconn_cfm    = l2cap_disconn_cfm,
        .security_cfm   = l2cap_security_cfm,
index 4c56ca5a216c6f8af48fac08f4d1512d0a8f677c..ad5177e3a69b7732b330ce67329ba143efac9b92 100644 (file)
@@ -2134,11 +2134,6 @@ static int rfcomm_run(void *unused)
        return 0;
 }
 
-static bool rfcomm_match(struct hci_conn *hcon)
-{
-       return hcon->type == ACL_LINK;
-}
-
 static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
 {
        struct rfcomm_session *s;
@@ -2185,7 +2180,6 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
 
 static struct hci_cb rfcomm_cb = {
        .name           = "RFCOMM",
-       .match          = rfcomm_match,
        .security_cfm   = rfcomm_security_cfm
 };
 
index ed6846864ea939450dbcf6ab9350734d3a162bfe..5d1bc0d6aee031aa9b6fd34cc688335f3f3ac798 100644 (file)
@@ -1407,13 +1407,11 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
        return lm;
 }
 
-static bool sco_match(struct hci_conn *hcon)
-{
-       return hcon->type == SCO_LINK || hcon->type == ESCO_LINK;
-}
-
 static void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
 {
+       if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK)
+               return;
+
        BT_DBG("hcon %p bdaddr %pMR status %u", hcon, &hcon->dst, status);
 
        if (!status) {
@@ -1430,6 +1428,9 @@ static void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
 
 static void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason)
 {
+       if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK)
+               return;
+
        BT_DBG("hcon %p reason %d", hcon, reason);
 
        sco_conn_del(hcon, bt_to_errno(reason));
@@ -1455,7 +1456,6 @@ drop:
 
 static struct hci_cb sco_cb = {
        .name           = "SCO",
-       .match          = sco_match,
        .connect_cfm    = sco_connect_cfm,
        .disconn_cfm    = sco_disconn_cfm,
 };