]> git.ipfire.org Git - thirdparty/kernel/stable.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)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 22 Mar 2025 19:54:11 +0000 (12:54 -0700)
[ Upstream commit ab6ab707a4d060a51c45fc13e3b2228d5f7c0b87 ]

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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
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 ba7b52584770d7b29c1d54c4849f500a6f84f731..c95f7e6ba2551403a174746cad0519f3e6cc50a8 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)
@@ -2006,47 +2007,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);
@@ -2054,43 +2032,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))
@@ -2098,11 +2055,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) {
@@ -2129,38 +2095,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 b5553c08e73162050e0e373904425b9a4e7fb0d3..72439764186ed2892559f231273cefcd03266598 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);
@@ -2992,7 +2993,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;
 }
@@ -3002,8 +3005,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 bda2f2da7d731138ee52d38fe9613ccae683e101..644b606743e212bce8077efcf5865fbbea8553c4 100644 (file)
@@ -2137,11 +2137,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) {
@@ -2323,7 +2318,6 @@ drop:
 
 static struct hci_cb iso_cb = {
        .name           = "ISO",
-       .match          = iso_match,
        .connect_cfm    = iso_connect_cfm,
        .disconn_cfm    = iso_disconn_cfm,
 };
index 728a5ce9b505878879accd9d14e8b158f6c2d6a2..4db2d6363bbb55f7919bc22028c4342a4b84a786 100644 (file)
@@ -7222,11 +7222,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;
@@ -7234,6 +7229,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) {
@@ -7298,6 +7296,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));
@@ -7576,7 +7577,6 @@ drop:
 
 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 071c404c790af98a6b4df25a72419e002e211522..b872a2ca3ff38bbd80b4f58d42e241463a02d8ff 100644 (file)
@@ -1355,13 +1355,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) {
@@ -1376,6 +1374,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));
@@ -1401,7 +1402,6 @@ drop:
 
 static struct hci_cb sco_cb = {
        .name           = "SCO",
-       .match          = sco_match,
        .connect_cfm    = sco_connect_cfm,
        .disconn_cfm    = sco_disconn_cfm,
 };