[PATCH bluetooth 1/4] Bluetooth: hci_conn: fix the SCO setup context lifetime
Linmao Li <[email protected]>
| Newsgroups | org.kernel.vger.linux-bluetooth,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]>
---
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 b1f911fd4ad6a..19b7629b1cc10 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;
}
--
2.25.1