From 42de40abe25db9211107af8896d0fd741f10648d Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Thu, 6 Aug 2026 20:59:54 +0800 Subject: [PATCH] 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 2f5d635ad590 ("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: e07a06b4eb41 ("Bluetooth: Convert SCO configure_datapath to hci_sync") Signed-off-by: Linmao Li Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/hci_conn.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index b1f911fd4ad6..19b7629b1cc1 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -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; }