OSDN Git Service

Bluetooth: hci_conn: fail SCO/ISO via hci_conn_failed if ACL gone early
authorPauli Virtanen <pav@iki.fi>
Sat, 19 Aug 2023 13:33:36 +0000 (16:33 +0300)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Thu, 24 Aug 2023 19:22:35 +0000 (12:22 -0700)
Not calling hci_(dis)connect_cfm before deleting conn referred to by a
socket generally results to use-after-free.

When cleaning up SCO connections when the parent ACL is deleted too
early, use hci_conn_failed to do the connection cleanup properly.

We also need to clean up ISO connections in a similar situation when
connecting has started but LE Create CIS is not yet sent, so do it too
here.

Fixes: ca1fd42e7dbf ("Bluetooth: Fix potential double free caused by hci_conn_unlink")
Reported-by: syzbot+cf54c1da6574b6c1b049@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/linux-bluetooth/00000000000013b93805fbbadc50@google.com/
Signed-off-by: Pauli Virtanen <pav@iki.fi>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
net/bluetooth/hci_conn.c

index 8b0c8e6..9d5057c 100644 (file)
@@ -1044,6 +1044,29 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
        return conn;
 }
 
+static void hci_conn_cleanup_child(struct hci_conn *conn, u8 reason)
+{
+       if (!reason)
+               reason = HCI_ERROR_REMOTE_USER_TERM;
+
+       /* Due to race, SCO/ISO conn might be not established yet at this point,
+        * and nothing else will clean it up. In other cases it is done via HCI
+        * events.
+        */
+       switch (conn->type) {
+       case SCO_LINK:
+       case ESCO_LINK:
+               if (HCI_CONN_HANDLE_UNSET(conn->handle))
+                       hci_conn_failed(conn, reason);
+               break;
+       case ISO_LINK:
+               if (conn->state != BT_CONNECTED &&
+                   !test_bit(HCI_CONN_CREATE_CIS, &conn->flags))
+                       hci_conn_failed(conn, reason);
+               break;
+       }
+}
+
 static void hci_conn_unlink(struct hci_conn *conn)
 {
        struct hci_dev *hdev = conn->hdev;
@@ -1066,14 +1089,7 @@ static void hci_conn_unlink(struct hci_conn *conn)
                        if (!test_bit(HCI_UP, &hdev->flags))
                                continue;
 
-                       /* Due to race, SCO connection might be not established
-                        * yet at this point. Delete it now, otherwise it is
-                        * possible for it to be stuck and can't be deleted.
-                        */
-                       if ((child->type == SCO_LINK ||
-                            child->type == ESCO_LINK) &&
-                           HCI_CONN_HANDLE_UNSET(child->handle))
-                               hci_conn_del(child);
+                       hci_conn_cleanup_child(child, conn->abort_reason);
                }
 
                return;