Bluetooth: hci_sync: hold conn in hci_connect_acl/le_sync() callbacks

There is theoretical UAF if the conn is freed while the hci_sync task
is running.

Hold refcount to avoid that.

Fixes: 881559af5f ("Bluetooth: hci_sync: Attempt to dequeue connection attempt")
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 2026-07-25 12:59:18 +03:00 committed by Luiz Augusto von Dentz
parent 5761d003da
commit 2f5d635ad5

View File

@ -7152,12 +7152,23 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
return err; return err;
} }
static void hci_acl_create_conn_sync_complete(struct hci_dev *hdev, void *data,
int err)
{
struct hci_conn *conn = data;
hci_conn_put(conn);
}
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn) int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
{ {
int err; int err;
err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn, err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync,
NULL); hci_conn_get(conn),
hci_acl_create_conn_sync_complete);
if (err)
hci_conn_put(conn);
return (err == -EEXIST) ? 0 : err; return (err == -EEXIST) ? 0 : err;
} }
@ -7168,36 +7179,41 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
bt_dev_dbg(hdev, "err %d", err); bt_dev_dbg(hdev, "err %d", err);
if (err == -ECANCELED) if (err == -ECANCELED)
return; goto done;
hci_dev_lock(hdev); hci_dev_lock(hdev);
if (!hci_conn_valid(hdev, conn)) if (!hci_conn_valid(hdev, conn))
goto done; goto unlock;
if (!err) { if (!err) {
hci_connect_le_scan_cleanup(conn, 0x00); hci_connect_le_scan_cleanup(conn, 0x00);
goto done; goto unlock;
} }
/* Check if connection is still pending */ /* Check if connection is still pending */
if (conn != hci_lookup_le_connect(hdev)) if (conn != hci_lookup_le_connect(hdev))
goto done; goto unlock;
/* Flush to make sure we send create conn cancel command if needed */ /* Flush to make sure we send create conn cancel command if needed */
flush_delayed_work(&conn->le_conn_timeout); flush_delayed_work(&conn->le_conn_timeout);
hci_conn_failed(conn, bt_status(err)); hci_conn_failed(conn, bt_status(err));
done: unlock:
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
done:
hci_conn_put(conn);
} }
int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn) int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
{ {
int err; int err;
err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn, err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync,
hci_conn_get(conn),
create_le_conn_complete); create_le_conn_complete);
if (err)
hci_conn_put(conn);
return (err == -EEXIST) ? 0 : err; return (err == -EEXIST) ? 0 : err;
} }