Bluetooth: mgmt: hold reference for hci_conn in mgmt_pending_cmds

[ Upstream commit da55f57019 ]

Dereferencing RCU-protected pointers outside critical sections is
invalid and may lead to UAF.  Use of hci_conn in hci_sync callbacks also
needs to hold refcount to avoid UAF.

Take appropriate locks for hci_conn lookups, and take refcount for
hci_conn pointers stored in mgmt_pending_cmd so that the pointer stays
valid.

When accessing conn->state, ensure hdev->lock is held to avoid data
race.

Fixes: 7b445e220d ("Bluetooth: MGMT: Fix holding hci_conn reference while command is queued")
Signed-off-by: Pauli Virtanen <pav@iki.fi>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Pauli Virtanen
2026-08-03 11:15:15 +02:00
committed by Greg Kroah-Hartman
parent 8bc83f9ef6
commit b56f2ecafc
+22 -4
View File
@@ -7417,6 +7417,9 @@ static void get_conn_info_complete(struct hci_dev *hdev, void *data, int err)
rp.max_tx_power = HCI_TX_POWER_INVALID;
}
if (conn)
hci_conn_put(conn);
mgmt_cmd_complete(cmd->sk, cmd->hdev->id, MGMT_OP_GET_CONN_INFO, status,
&rp, sizeof(rp));
@@ -7431,6 +7434,8 @@ static int get_conn_info_sync(struct hci_dev *hdev, void *data)
int err;
__le16 handle;
hci_dev_lock(hdev);
/* Make sure we are still connected */
if (cp->addr.type == BDADDR_BREDR)
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
@@ -7438,12 +7443,16 @@ static int get_conn_info_sync(struct hci_dev *hdev, void *data)
else
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
if (!conn || conn->state != BT_CONNECTED)
if (!conn || conn->state != BT_CONNECTED) {
hci_dev_unlock(hdev);
return MGMT_STATUS_NOT_CONNECTED;
}
cmd->user_data = conn;
cmd->user_data = hci_conn_get(conn);
handle = cpu_to_le16(conn->handle);
hci_dev_unlock(hdev);
/* Refresh RSSI each time */
err = hci_read_rssi_sync(hdev, handle);
@@ -7577,6 +7586,9 @@ static void get_clock_info_complete(struct hci_dev *hdev, void *data, int err)
}
complete:
if (conn)
hci_conn_put(conn);
mgmt_cmd_complete(cmd->sk, cmd->hdev->id, cmd->opcode, status, &rp,
sizeof(rp));
@@ -7593,15 +7605,21 @@ static int get_clock_info_sync(struct hci_dev *hdev, void *data)
memset(&hci_cp, 0, sizeof(hci_cp));
hci_read_clock_sync(hdev, &hci_cp);
hci_dev_lock(hdev);
/* Make sure connection still exists */
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
if (!conn || conn->state != BT_CONNECTED)
if (!conn || conn->state != BT_CONNECTED) {
hci_dev_unlock(hdev);
return MGMT_STATUS_NOT_CONNECTED;
}
cmd->user_data = conn;
cmd->user_data = hci_conn_get(conn);
hci_cp.handle = cpu_to_le16(conn->handle);
hci_cp.which = 0x01; /* Piconet clock */
hci_dev_unlock(hdev);
return hci_read_clock_sync(hdev, &hci_cp);
}