Merge tag 'for-net-2026-07-13' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth

Luiz Augusto von Dentz says:

====================
bluetooth pull request for net:

 - hci_sync: hold hdev->lock for hci_conn_params lookups
 - hci_sync: extend conn_hash lookup critical sections
 - hci_qca: Clear memdump state on invalid dump size
 - MGMT: revalidate LOAD_CONN_PARAM queued update
 - MGMT: Translate HCI reason in Device Disconnected event
 - MGMT: fix locking in unpair_device/disconnect_sync
 - MGMT: hold reference for hci_conn in mgmt_pending_cmds
 - btrtl: validate firmware patch bounds
 - qca: fix NVM tag length underflow in TLV parser

* tag 'for-net-2026-07-13' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth:
  Bluetooth: mgmt: Translate HCI reason in Device Disconnected event
  Bluetooth: hci_qca: Clear memdump state on invalid dump size
  Bluetooth: hci_sync: hold hdev->lock for hci_conn_params lookups
  Bluetooth: mgmt: hold reference for hci_conn in mgmt_pending_cmds
  Bluetooth: mgmt: fix locking in unpair_device/disconnect_sync
  Bluetooth: hci_sync: extend conn_hash lookup critical sections
  Bluetooth: btrtl: validate firmware patch bounds
  Bluetooth: MGMT: revalidate LOAD_CONN_PARAM queued update
  Bluetooth: qca: fix NVM tag length underflow in TLV parser
====================

Link: https://patch.msgid.link/20260713141940.954317-1-luiz.dentz@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni
2026-07-17 11:27:43 +02:00
7 changed files with 161 additions and 42 deletions

View File

@@ -415,7 +415,7 @@ static int qca_tlv_check_data(struct hci_dev *hdev,
idx = 0;
data = tlv->data;
while (idx < length - sizeof(struct tlv_type_nvm)) {
while (idx + sizeof(struct tlv_type_nvm) <= length) {
tlv_nvm = (struct tlv_type_nvm *)(data + idx);
tag_id = le16_to_cpu(tlv_nvm->tag_id);

View File

@@ -797,8 +797,9 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
}
BT_DBG("length=%x offset=%x index %d", patch_length, patch_offset, i);
min_size = patch_offset + patch_length;
if (btrtl_dev->fw_len < min_size)
if (patch_length < sizeof(epatch_info->fw_version) ||
patch_offset > btrtl_dev->fw_len ||
patch_length > btrtl_dev->fw_len - patch_offset)
return -EINVAL;
/* Copy the firmware into a new buffer and write the version at

View File

@@ -1087,6 +1087,10 @@ static void qca_controller_memdump(struct work_struct *work)
if (!(qca_memdump->ram_dump_size)) {
bt_dev_err(hu->hdev, "Rx invalid memdump size");
kfree(qca_memdump);
qca->qca_memdump = NULL;
qca->memdump_state = QCA_MEMDUMP_COLLECTED;
clear_and_wake_up_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
clear_bit(QCA_IBS_DISABLED, &qca->flags);
kfree_skb(skb);
mutex_unlock(&qca->hci_memdump_lock);
return;

View File

@@ -2430,6 +2430,7 @@ void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
bool persistent);
void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
u8 *name, u8 name_len);
u8 hci_to_mgmt_reason(u8 err);
void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type, u8 reason,
bool mgmt_connected);

View File

@@ -2763,7 +2763,7 @@ static void hci_cs_disconnect(struct hci_dev *hdev, u8 status)
}
mgmt_device_disconnected(hdev, &conn->dst, conn->type, conn->dst_type,
cp->reason, mgmt_conn);
hci_to_mgmt_reason(cp->reason), mgmt_conn);
hci_disconn_cfm(conn, cp->reason);
@@ -3381,22 +3381,6 @@ static void hci_conn_request_evt(struct hci_dev *hdev, void *data,
hci_dev_unlock(hdev);
}
static u8 hci_to_mgmt_reason(u8 err)
{
switch (err) {
case HCI_ERROR_CONNECTION_TIMEOUT:
return MGMT_DEV_DISCONN_TIMEOUT;
case HCI_ERROR_REMOTE_USER_TERM:
case HCI_ERROR_REMOTE_LOW_RESOURCES:
case HCI_ERROR_REMOTE_POWER_OFF:
return MGMT_DEV_DISCONN_REMOTE;
case HCI_ERROR_LOCAL_HOST_TERM:
return MGMT_DEV_DISCONN_LOCAL_HOST;
default:
return MGMT_DEV_DISCONN_UNKNOWN;
}
}
static void hci_disconn_complete_evt(struct hci_dev *hdev, void *data,
struct sk_buff *skb)
{

View File

@@ -1054,14 +1054,19 @@ static int hci_set_random_addr_sync(struct hci_dev *hdev, bdaddr_t *rpa)
* In this kind of scenario skip the update and let the random
* address be updated at the next cycle.
*/
rcu_read_lock();
if (bacmp(&hdev->random_addr, BDADDR_ANY) &&
(hci_dev_test_flag(hdev, HCI_LE_ADV) ||
hci_lookup_le_connect(hdev))) {
bt_dev_dbg(hdev, "Deferring random address update");
hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
rcu_read_unlock();
return 0;
}
rcu_read_unlock();
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_RANDOM_ADDR,
6, rpa, HCI_CMD_TIMEOUT);
}
@@ -2647,12 +2652,17 @@ static int hci_pause_addr_resolution(struct hci_dev *hdev)
/* Cannot disable addr resolution if scanning is enabled or
* when initiating an LE connection.
*/
rcu_read_lock();
if (hci_dev_test_flag(hdev, HCI_LE_SCAN) ||
hci_lookup_le_connect(hdev)) {
rcu_read_unlock();
bt_dev_err(hdev, "Command not allowed when scan/LE connect");
return -EPERM;
}
rcu_read_unlock();
/* Cannot disable addr resolution if advertising is enabled. */
err = hci_pause_advertising_sync(hdev);
if (err) {
@@ -2790,6 +2800,8 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
if (hci_dev_test_flag(hdev, HCI_PA_SYNC)) {
struct hci_conn *conn;
rcu_read_lock();
conn = hci_conn_hash_lookup_create_pa_sync(hdev);
if (conn) {
struct conn_params pa;
@@ -2799,6 +2811,8 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
bacpy(&pa.addr, &conn->dst);
pa.addr_type = conn->dst_type;
rcu_read_unlock();
/* Clear first since there could be addresses left
* behind.
*/
@@ -2808,6 +2822,8 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
err = hci_le_add_accept_list_sync(hdev, &pa,
&num_entries);
goto done;
} else {
rcu_read_unlock();
}
}
@@ -2818,10 +2834,13 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
* the controller.
*/
list_for_each_entry_safe(b, t, &hdev->le_accept_list, list) {
if (hci_conn_hash_lookup_le(hdev, &b->bdaddr, b->bdaddr_type))
continue;
rcu_read_lock();
if (hci_conn_hash_lookup_le(hdev, &b->bdaddr, b->bdaddr_type)) {
rcu_read_unlock();
continue;
}
/* Pointers not dereferenced, no locks needed */
pend_conn = hci_pend_le_action_lookup(&hdev->pend_le_conns,
&b->bdaddr,
b->bdaddr_type);
@@ -2829,6 +2848,8 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
&b->bdaddr,
b->bdaddr_type);
rcu_read_unlock();
/* If the device is not likely to connect or report,
* remove it from the acceptlist.
*/
@@ -2955,6 +2976,8 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type,
if (sent) {
struct hci_conn *conn;
rcu_read_lock();
conn = hci_conn_hash_lookup_ba(hdev, PA_LINK,
&sent->bdaddr);
if (conn) {
@@ -2979,8 +3002,12 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type,
phy++;
}
rcu_read_unlock();
if (num_phy)
goto done;
} else {
rcu_read_unlock();
}
}
}
@@ -3231,12 +3258,16 @@ int hci_update_passive_scan_sync(struct hci_dev *hdev)
/* If there is at least one pending LE connection, we should
* keep the background scan running.
*/
bool exists;
/* If controller is connecting, we should not start scanning
* since some controllers are not able to scan and connect at
* the same time.
*/
if (hci_lookup_le_connect(hdev))
rcu_read_lock();
exists = hci_lookup_le_connect(hdev);
rcu_read_unlock();
if (exists)
return 0;
bt_dev_dbg(hdev, "start background scanning");
@@ -3454,6 +3485,7 @@ int hci_write_fast_connectable_sync(struct hci_dev *hdev, bool enable)
}
static bool disconnected_accept_list_entries(struct hci_dev *hdev)
__must_hold(&hdev->lock)
{
struct bdaddr_list *b;
@@ -3494,12 +3526,16 @@ int hci_update_scan_sync(struct hci_dev *hdev)
if (hdev->scanning_paused)
return 0;
hci_dev_lock(hdev);
if (hci_dev_test_flag(hdev, HCI_CONNECTABLE) ||
disconnected_accept_list_entries(hdev))
scan = SCAN_PAGE;
else
scan = SCAN_DISABLED;
hci_dev_unlock(hdev);
if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE))
scan |= SCAN_INQUIRY;
@@ -6665,6 +6701,8 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
if (!hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES))
hci_pause_advertising_sync(hdev);
hci_dev_lock(hdev);
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
if (params) {
conn->le_conn_min_interval = params->conn_min_interval;
@@ -6678,6 +6716,8 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
conn->le_supv_timeout = hdev->le_supv_timeout;
}
hci_dev_unlock(hdev);
/* If controller is scanning, we stop it since some controllers are
* not able to scan and connect at the same time. Also set the
* HCI_LE_SCAN_INTERRUPTED flag so that the command complete
@@ -7235,13 +7275,13 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
}
static int hci_le_past_params_sync(struct hci_dev *hdev, struct hci_conn *conn,
struct hci_conn *acl, struct bt_iso_qos *qos)
u16 acl_handle, struct bt_iso_qos *qos)
{
struct hci_cp_le_past_params cp;
int err;
memset(&cp, 0, sizeof(cp));
cp.handle = cpu_to_le16(acl->handle);
cp.handle = cpu_to_le16(acl_handle);
/* An HCI_LE_Periodic_Advertising_Sync_Transfer_Received event is sent
* to the Host. HCI_LE_Periodic_Advertising_Report events will be
* enabled with duplicate filtering enabled.
@@ -7306,16 +7346,28 @@ static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data)
* 2. Check if that HCI_CONN_FLAG_PAST has been set which indicates that
* user really intended to use PAST.
*/
hci_dev_lock(hdev);
le = hci_conn_hash_lookup_le(hdev, &conn->dst, conn->dst_type);
if (le) {
struct hci_conn_params *params;
hci_conn_flags_t flags = 0;
u16 le_handle = le->handle;
params = hci_conn_params_lookup(hdev, &le->dst, le->dst_type);
if (params && params->flags & HCI_CONN_FLAG_PAST) {
err = hci_le_past_params_sync(hdev, conn, le, qos);
if (params)
flags = params->flags;
hci_dev_unlock(hdev);
if (flags & HCI_CONN_FLAG_PAST) {
err = hci_le_past_params_sync(hdev, conn, le_handle,
qos);
if (!err)
goto done;
}
} else {
hci_dev_unlock(hdev);
}
/* SID has not been set listen for HCI_EV_LE_EXT_ADV_REPORT to update

View File

@@ -3091,6 +3091,8 @@ static int unpair_device_sync(struct hci_dev *hdev, void *data)
struct mgmt_cp_unpair_device *cp = cmd->param;
struct hci_conn *conn;
hci_dev_lock(hdev);
if (cp->addr.type == BDADDR_BREDR)
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
&cp->addr.bdaddr);
@@ -3098,6 +3100,11 @@ static int unpair_device_sync(struct hci_dev *hdev, void *data)
conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr,
le_addr_type(cp->addr.type));
if (conn)
hci_conn_get(conn);
hci_dev_unlock(hdev);
if (!conn)
return 0;
@@ -3105,6 +3112,7 @@ static int unpair_device_sync(struct hci_dev *hdev, void *data)
* will clean up the connection no matter the error.
*/
hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
hci_conn_put(conn);
return 0;
}
@@ -3252,6 +3260,8 @@ static int disconnect_sync(struct hci_dev *hdev, void *data)
struct mgmt_cp_disconnect *cp = cmd->param;
struct hci_conn *conn;
hci_dev_lock(hdev);
if (cp->addr.type == BDADDR_BREDR)
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
&cp->addr.bdaddr);
@@ -3259,6 +3269,11 @@ static int disconnect_sync(struct hci_dev *hdev, void *data)
conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr,
le_addr_type(cp->addr.type));
if (conn)
hci_conn_get(conn);
hci_dev_unlock(hdev);
if (!conn)
return -ENOTCONN;
@@ -3266,6 +3281,7 @@ static int disconnect_sync(struct hci_dev *hdev, void *data)
* will clean up the connection no matter the error.
*/
hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
hci_conn_put(conn);
return 0;
}
@@ -7388,6 +7404,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));
@@ -7402,6 +7421,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,
@@ -7409,12 +7430,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);
@@ -7548,6 +7573,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));
@@ -7564,15 +7592,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);
}
@@ -7937,14 +7971,36 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
static int conn_update_sync(struct hci_dev *hdev, void *data)
{
struct hci_conn_params *params = data;
struct hci_conn *conn;
struct hci_conn *conn = data;
struct hci_conn_params *params;
struct hci_conn_params local = {};
conn = hci_conn_hash_lookup_le(hdev, &params->addr, params->addr_type);
if (!conn)
return -ECANCELED;
hci_dev_lock(hdev);
return hci_le_conn_update_sync(hdev, conn, params);
if (!hci_conn_valid(hdev, conn) || conn->role != HCI_ROLE_MASTER)
goto cancel;
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
if (!params)
goto cancel;
local.conn_min_interval = params->conn_min_interval;
local.conn_max_interval = params->conn_max_interval;
local.conn_latency = params->conn_latency;
local.supervision_timeout = params->supervision_timeout;
hci_dev_unlock(hdev);
return hci_le_conn_update_sync(hdev, conn, &local);
cancel:
hci_dev_unlock(hdev);
return -ECANCELED;
}
static void conn_update_sync_destroy(struct hci_dev *hdev, void *data, int err)
{
hci_conn_put(data);
}
static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -8054,9 +8110,13 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data,
(conn->le_conn_min_interval != min ||
conn->le_conn_max_interval != max ||
conn->le_conn_latency != latency ||
conn->le_supv_timeout != timeout))
hci_cmd_sync_queue(hdev, conn_update_sync,
hci_param, NULL);
conn->le_supv_timeout != timeout)) {
hci_conn_get(conn);
if (hci_cmd_sync_queue(hdev, conn_update_sync,
conn,
conn_update_sync_destroy) < 0)
hci_conn_put(conn);
}
}
}
@@ -9848,6 +9908,22 @@ bool mgmt_powering_down(struct hci_dev *hdev)
return false;
}
u8 hci_to_mgmt_reason(u8 err)
{
switch (err) {
case HCI_ERROR_CONNECTION_TIMEOUT:
return MGMT_DEV_DISCONN_TIMEOUT;
case HCI_ERROR_REMOTE_USER_TERM:
case HCI_ERROR_REMOTE_LOW_RESOURCES:
case HCI_ERROR_REMOTE_POWER_OFF:
return MGMT_DEV_DISCONN_REMOTE;
case HCI_ERROR_LOCAL_HOST_TERM:
return MGMT_DEV_DISCONN_LOCAL_HOST;
default:
return MGMT_DEV_DISCONN_UNKNOWN;
}
}
void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type, u8 reason,
bool mgmt_connected)
@@ -9909,7 +9985,8 @@ void mgmt_connect_failed(struct hci_dev *hdev, struct hci_conn *conn, u8 status)
if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) {
mgmt_device_disconnected(hdev, &conn->dst, conn->type,
conn->dst_type, status, true);
conn->dst_type,
hci_to_mgmt_reason(status), true);
return;
}