Bluetooth: hci_conn: fail SCO/ISO via hci_conn_failed if ACL gone early

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: ca1fd42e7d ("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>
This commit is contained in:
Pauli Virtanen 2023-08-19 16:33:36 +03:00 committed by Luiz Augusto von Dentz
parent db08722fc7
commit 3344d31833

View 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;