Bluetooth: hci_conn: fix the SCO setup context lifetime

hci_setup_sync() queues a conn_handle_t with a NULL destroy callback, so
the context is only freed if hci_enhanced_setup_sync() actually runs. An
entry that is cancelled instead is leaked, as
_hci_cmd_sync_cancel_entry() does not release entry->data when there is
no destroy callback, and hci_cmd_sync_clear() cancels every pending entry
when the controller is unregistered.

The context also stores a bare hci_conn pointer, so the connection can be
freed while the work is queued. The dequeue in hci_conn_del() does not
cover it either, as it matches on entry->data == conn and entry->data is
the wrapper here. Same problem as commit 2f5d635ad5 ("Bluetooth:
hci_sync: hold conn in hci_connect_acl/le_sync() callbacks").

Hold the connection and release both from a destroy callback. The
submission failure path drops both, since hci_cmd_sync_submit() does not
call the destroy callback when it fails to queue.

Fixes: e07a06b4eb ("Bluetooth: Convert SCO configure_datapath to hci_sync")
Signed-off-by: Linmao Li <lilinmao@kylinos.cn>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
Linmao Li
2026-08-06 20:59:54 +08:00
committed by Luiz Augusto von Dentz
parent f57b399c4f
commit 42de40abe2

View File

@@ -283,8 +283,6 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data)
struct hci_cp_enhanced_setup_sync_conn cp;
const struct sco_param *param;
kfree(conn_handle);
if (!hci_conn_valid(hdev, conn))
return -ECANCELED;
@@ -453,6 +451,15 @@ static bool hci_setup_sync_conn(struct hci_conn *conn, __u16 handle)
return true;
}
static void hci_enhanced_setup_sync_destroy(struct hci_dev *hdev, void *data,
int err)
{
struct conn_handle_t *conn_handle = data;
hci_conn_put(conn_handle->conn);
kfree(conn_handle);
}
bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
{
int result;
@@ -464,12 +471,15 @@ bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
if (!conn_handle)
return false;
conn_handle->conn = conn;
conn_handle->conn = hci_conn_get(conn);
conn_handle->handle = handle;
result = hci_cmd_sync_queue(conn->hdev, hci_enhanced_setup_sync,
conn_handle, NULL);
if (result < 0)
conn_handle,
hci_enhanced_setup_sync_destroy);
if (result < 0) {
hci_conn_put(conn);
kfree(conn_handle);
}
return result == 0;
}