* [PATCH 0/4] Bluetooth: fix hci_conn lookup RCU usage + holding refcounts
@ 2026-07-05 14:47 Pauli Virtanen
2026-07-05 14:47 ` [PATCH 1/4] Bluetooth: hci_sync: extend conn_hash lookup RCU critical sections Pauli Virtanen
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Pauli Virtanen @ 2026-07-05 14:47 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen, marcel, luiz.dentz, linux-kernel
Resending parts of
https://lore.kernel.org/linux-bluetooth/cover.1762100290.git.pav@iki.fi/
plus additional hci_conn_params usage fix.
Pauli Virtanen (4):
Bluetooth: hci_sync: extend conn_hash lookup RCU critical sections
Bluetooth: mgmt: fix RCU use in unpair_device/disconnect_sync
Bluetooth: mgmt: hold reference for hci_conn in mgmt_pending_cmds
Bluetooth: hci_sync: hold hdev->lock for hci_conn_params lookups
net/bluetooth/hci_sync.c | 61 ++++++++++++++++++++++++++++++++++++----
net/bluetooth/mgmt.c | 42 ++++++++++++++++++++++++---
2 files changed, 93 insertions(+), 10 deletions(-)
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/4] Bluetooth: hci_sync: extend conn_hash lookup RCU critical sections
2026-07-05 14:47 [PATCH 0/4] Bluetooth: fix hci_conn lookup RCU usage + holding refcounts Pauli Virtanen
@ 2026-07-05 14:47 ` Pauli Virtanen
2026-07-05 14:47 ` [PATCH 2/4] Bluetooth: mgmt: fix RCU use in unpair_device/disconnect_sync Pauli Virtanen
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Pauli Virtanen @ 2026-07-05 14:47 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen, marcel, luiz.dentz, linux-kernel
Using RCU-protected pointers outside the critical sections without
refcount is incorrect and may result to UAF.
Extend critical section to cover both hci_conn_hash lookup and use of
the returned conn.
Add surrounding rcu_read_lock() also when return value is not used, in
preparation for RCU lockdep requirement to hci_lookup_le_connect().
This avoids concurrent deletion of the conn before we are done
dereferencing it.
Fixes: 58ddd115fe063 ("Bluetooth: hci_conn: Fix not setting conn_timeout for Broadcast Receiver")
Signed-off-by: Pauli Virtanen <pav@iki.fi>
---
net/bluetooth/hci_sync.c | 41 +++++++++++++++++++++++++++++++++++++---
1 file changed, 38 insertions(+), 3 deletions(-)
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index b096fca86bb5..a614bab77119 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -1028,14 +1028,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);
}
@@ -2621,12 +2626,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) {
@@ -2764,6 +2774,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;
@@ -2773,6 +2785,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.
*/
@@ -2782,6 +2796,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();
}
}
@@ -2792,10 +2808,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))
+ 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);
@@ -2803,6 +2822,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.
*/
@@ -2929,6 +2950,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) {
@@ -2953,8 +2976,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();
}
}
}
@@ -3205,12 +3232,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");
@@ -3468,12 +3499,16 @@ int hci_update_scan_sync(struct hci_dev *hdev)
if (hdev->scanning_paused)
return 0;
+ rcu_read_lock();
+
if (hci_dev_test_flag(hdev, HCI_CONNECTABLE) ||
disconnected_accept_list_entries(hdev))
scan = SCAN_PAGE;
else
scan = SCAN_DISABLED;
+ rcu_read_unlock();
+
if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE))
scan |= SCAN_INQUIRY;
--
2.55.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/4] Bluetooth: mgmt: fix RCU use in unpair_device/disconnect_sync
2026-07-05 14:47 [PATCH 0/4] Bluetooth: fix hci_conn lookup RCU usage + holding refcounts Pauli Virtanen
2026-07-05 14:47 ` [PATCH 1/4] Bluetooth: hci_sync: extend conn_hash lookup RCU critical sections Pauli Virtanen
@ 2026-07-05 14:47 ` Pauli Virtanen
2026-07-05 14:47 ` [PATCH 3/4] Bluetooth: mgmt: hold reference for hci_conn in mgmt_pending_cmds Pauli Virtanen
2026-07-05 14:47 ` [PATCH 4/4] Bluetooth: hci_sync: hold hdev->lock for hci_conn_params lookups Pauli Virtanen
3 siblings, 0 replies; 5+ messages in thread
From: Pauli Virtanen @ 2026-07-05 14:47 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen, marcel, luiz.dentz, linux-kernel
Dereferencing RCU-protected pointers outside critical sections is
invalid and may lead to UAF.
Take RCU for hci_conn lookups. Since hci_abort_conn() cannot be called
with RCU held, take refcount temporarily.
Fixes: 227a0cdf4a028 ("Bluetooth: MGMT: Fix not generating command complete for MGMT_OP_DISCONNECT")
Signed-off-by: Pauli Virtanen <pav@iki.fi>
---
net/bluetooth/mgmt.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 733a4b70e10c..cfcb97badd02 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -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;
+ rcu_read_lock();
+
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);
+
+ rcu_read_unlock();
+
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;
+ rcu_read_lock();
+
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);
+
+ rcu_read_unlock();
+
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;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 3/4] Bluetooth: mgmt: hold reference for hci_conn in mgmt_pending_cmds
2026-07-05 14:47 [PATCH 0/4] Bluetooth: fix hci_conn lookup RCU usage + holding refcounts Pauli Virtanen
2026-07-05 14:47 ` [PATCH 1/4] Bluetooth: hci_sync: extend conn_hash lookup RCU critical sections Pauli Virtanen
2026-07-05 14:47 ` [PATCH 2/4] Bluetooth: mgmt: fix RCU use in unpair_device/disconnect_sync Pauli Virtanen
@ 2026-07-05 14:47 ` Pauli Virtanen
2026-07-05 14:47 ` [PATCH 4/4] Bluetooth: hci_sync: hold hdev->lock for hci_conn_params lookups Pauli Virtanen
3 siblings, 0 replies; 5+ messages in thread
From: Pauli Virtanen @ 2026-07-05 14:47 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen, marcel, luiz.dentz, linux-kernel
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: 7b445e220db9 ("Bluetooth: MGMT: Fix holding hci_conn reference while command is queued")
Signed-off-by: Pauli Virtanen <pav@iki.fi>
---
net/bluetooth/mgmt.c | 26 ++++++++++++++++++++++----
1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index cfcb97badd02..c33751b6fbea 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -7404,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));
@@ -7418,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,
@@ -7425,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);
@@ -7564,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));
@@ -7580,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);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 4/4] Bluetooth: hci_sync: hold hdev->lock for hci_conn_params lookups
2026-07-05 14:47 [PATCH 0/4] Bluetooth: fix hci_conn lookup RCU usage + holding refcounts Pauli Virtanen
` (2 preceding siblings ...)
2026-07-05 14:47 ` [PATCH 3/4] Bluetooth: mgmt: hold reference for hci_conn in mgmt_pending_cmds Pauli Virtanen
@ 2026-07-05 14:47 ` Pauli Virtanen
3 siblings, 0 replies; 5+ messages in thread
From: Pauli Virtanen @ 2026-07-05 14:47 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen, marcel, luiz.dentz, linux-kernel
hci_conn_params_lookup requires hdev->lock be held, otherwise the list
iteration or param access is not safe.
Hold hdev->lock for params lookups in hci_sync.
Fixes: c530569adc19 ("Bluetooth: hci_core: Introduce HCI_CONN_FLAG_PAST")
Signed-off-by: Pauli Virtanen <pav@iki.fi>
---
net/bluetooth/hci_sync.c | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index a614bab77119..dfc4cdafe14b 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -6676,6 +6676,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;
@@ -6689,6 +6691,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
@@ -7246,13 +7250,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.
@@ -7317,16 +7321,26 @@ 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;
+ 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);
+ hci_dev_unlock(hdev);
+
+ err = hci_le_past_params_sync(hdev, conn, le_handle,
+ qos);
if (!err)
goto done;
+ } else {
+ hci_dev_unlock(hdev);
}
+ } else {
+ hci_dev_unlock(hdev);
}
/* SID has not been set listen for HCI_EV_LE_EXT_ADV_REPORT to update
--
2.55.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-07-05 14:48 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-05 14:47 [PATCH 0/4] Bluetooth: fix hci_conn lookup RCU usage + holding refcounts Pauli Virtanen
2026-07-05 14:47 ` [PATCH 1/4] Bluetooth: hci_sync: extend conn_hash lookup RCU critical sections Pauli Virtanen
2026-07-05 14:47 ` [PATCH 2/4] Bluetooth: mgmt: fix RCU use in unpair_device/disconnect_sync Pauli Virtanen
2026-07-05 14:47 ` [PATCH 3/4] Bluetooth: mgmt: hold reference for hci_conn in mgmt_pending_cmds Pauli Virtanen
2026-07-05 14:47 ` [PATCH 4/4] Bluetooth: hci_sync: hold hdev->lock for hci_conn_params lookups Pauli Virtanen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox