Bluetooth: MGMT: free the HCI command when it is cancelled

mgmt_hci_cmd_sync() queues the pending command with a NULL destroy
callback, so it is only freed if send_hci_cmd_sync() runs. A cancelled
entry 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. Nothing
else reclaims it either: mgmt_pending_new() does not put the command on
hdev->mgmt_pending.

The leak also pins the socket reference taken by mgmt_pending_new(), so
the mgmt socket is never released.

Free the command from a destroy callback. The now-empty done label is
replaced by a direct return.

Fixes: 827af4787e ("Bluetooth: MGMT: Add initial implementation of MGMT_OP_HCI_CMD_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:57 +08:00
committed by Luiz Augusto von Dentz
parent 3c742feda8
commit 414b365ece

View File

@@ -2653,7 +2653,7 @@ static int send_hci_cmd_sync(struct hci_dev *hdev, void *data)
if (IS_ERR(skb)) {
mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_HCI_CMD_SYNC,
mgmt_status(PTR_ERR(skb)));
goto done;
return 0;
}
mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_HCI_CMD_SYNC, 0,
@@ -2661,12 +2661,14 @@ static int send_hci_cmd_sync(struct hci_dev *hdev, void *data)
kfree_skb(skb);
done:
mgmt_pending_free(cmd);
return 0;
}
static void send_hci_cmd_sync_destroy(struct hci_dev *hdev, void *data, int err)
{
mgmt_pending_free(data);
}
static int mgmt_hci_cmd_sync(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
@@ -2684,7 +2686,8 @@ static int mgmt_hci_cmd_sync(struct sock *sk, struct hci_dev *hdev,
if (!cmd)
err = -ENOMEM;
else
err = hci_cmd_sync_queue(hdev, send_hci_cmd_sync, cmd, NULL);
err = hci_cmd_sync_queue(hdev, send_hci_cmd_sync, cmd,
send_hci_cmd_sync_destroy);
if (err < 0) {
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_HCI_CMD_SYNC,