Skip to content

Commit a239110

Browse files
pvVudentz
authored andcommitted
Bluetooth: hci_sync: always check if connection is alive before deleting
In hci_abort_conn_sync it is possible that conn is deleted concurrently by something else, also e.g. when waiting for hdev->lock. This causes double deletion of the conn, so UAF or conn_hash.list corruption. Fix by having all code paths check that the connection is still in conn_hash before deleting it, while holding hdev->lock which prevents any races. Log (when powering off while BAP streaming, occurs rarely): ======================================================================= kernel BUG at lib/list_debug.c:56! ... ? __list_del_entry_valid (lib/list_debug.c:56) hci_conn_del (net/bluetooth/hci_conn.c:154) bluetooth hci_abort_conn_sync (net/bluetooth/hci_sync.c:5415) bluetooth ? __pfx_hci_abort_conn_sync+0x10/0x10 [bluetooth] ? lock_release+0x1d5/0x3c0 ? hci_disconnect_all_sync.constprop.0+0xb2/0x230 [bluetooth] ? __pfx_lock_release+0x10/0x10 ? __kmem_cache_free+0x14d/0x2e0 hci_disconnect_all_sync.constprop.0+0xda/0x230 [bluetooth] ? __pfx_hci_disconnect_all_sync.constprop.0+0x10/0x10 [bluetooth] ? hci_clear_adv_sync+0x14f/0x170 [bluetooth] ? __pfx_set_powered_sync+0x10/0x10 [bluetooth] hci_set_powered_sync+0x293/0x450 [bluetooth] ======================================================================= Fixes: 94d9ba9 ("Bluetooth: hci_sync: Fix UAF in hci_disconnect_all_sync") Signed-off-by: Pauli Virtanen <[email protected]> Signed-off-by: Luiz Augusto von Dentz <[email protected]>
1 parent 1ffc6f8 commit a239110

File tree

1 file changed

+12
-14
lines changed

1 file changed

+12
-14
lines changed

net/bluetooth/hci_sync.c

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5369,6 +5369,7 @@ int hci_abort_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, u8 reason)
53695369
{
53705370
int err = 0;
53715371
u16 handle = conn->handle;
5372+
bool disconnect = false;
53725373
struct hci_conn *c;
53735374

53745375
switch (conn->state) {
@@ -5399,24 +5400,15 @@ int hci_abort_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, u8 reason)
53995400
hci_dev_unlock(hdev);
54005401
return 0;
54015402
case BT_BOUND:
5402-
hci_dev_lock(hdev);
5403-
hci_conn_failed(conn, reason);
5404-
hci_dev_unlock(hdev);
5405-
return 0;
5403+
break;
54065404
default:
5407-
hci_dev_lock(hdev);
5408-
conn->state = BT_CLOSED;
5409-
hci_disconn_cfm(conn, reason);
5410-
hci_conn_del(conn);
5411-
hci_dev_unlock(hdev);
5412-
return 0;
5405+
disconnect = true;
5406+
break;
54135407
}
54145408

54155409
hci_dev_lock(hdev);
54165410

5417-
/* Check if the connection hasn't been cleanup while waiting
5418-
* commands to complete.
5419-
*/
5411+
/* Check if the connection has been cleaned up concurrently */
54205412
c = hci_conn_hash_lookup_handle(hdev, handle);
54215413
if (!c || c != conn) {
54225414
err = 0;
@@ -5428,7 +5420,13 @@ int hci_abort_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, u8 reason)
54285420
* or in case of LE it was still scanning so it can be cleanup
54295421
* safely.
54305422
*/
5431-
hci_conn_failed(conn, reason);
5423+
if (disconnect) {
5424+
conn->state = BT_CLOSED;
5425+
hci_disconn_cfm(conn, reason);
5426+
hci_conn_del(conn);
5427+
} else {
5428+
hci_conn_failed(conn, reason);
5429+
}
54325430

54335431
unlock:
54345432
hci_dev_unlock(hdev);

0 commit comments

Comments
 (0)