* [PATCH 0/9] Bluetooth: avoid concurrent deletion of hci_conn
@ 2025-10-03 17:08 Pauli Virtanen
2025-10-03 17:08 ` [PATCH 1/9] Bluetooth: hci_event: extend hdev lock in hci_le_remote_conn_param_req_evt Pauli Virtanen
` (9 more replies)
0 siblings, 10 replies; 11+ messages in thread
From: Pauli Virtanen @ 2025-10-03 17:08 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
This contains the simpler fixes from
https://lore.kernel.org/linux-bluetooth/cover.1758481869.git.pav@iki.fi/
hdev has two workqueues that run concurrently, and both may delete
hci_conn. hci_conn* pointers then require either (i) hdev/rcu lock
covering lookup and usage, or (ii) hci_conn_get reference held.
If neither is done, it's likely there are corner cases that hit UAF,
especially if controller misbehaves.
Correct code in several places to follow the patterns (1)
take lock
conn = hci_conn_hash_lookup(...)
if (conn)
do_something(conn)
release lock
or (2)
take lock
conn = hci_conn_hash_lookup(...)
if (conn)
conn = hci_conn_get(conn)
release lock
do_something_carefully(conn)
hci_conn_put(conn)
Generally do_something_carefully should do (3)
take lock
if (hci_conn_valid(hdev, conn))
do_something(conn)
release lock
hci_conn_valid() shouldn't be called unless refcount on conn is known to
be held, as the pointer may otherwise already be freed, and even though
hci_conn_valid() doesn't dereference the comparison of freed pointer it
does is strictly speaking undefined behavior (kalloc is not guaranteed
to not reuse addresses).
Some of the code touched here is missing locks for (3), those need to be
addressed in separate series.
Pauli Virtanen (9):
Bluetooth: hci_event: extend hdev lock in
hci_le_remote_conn_param_req_evt
Bluetooth: hci_conn: take hdev lock in set_cig_params_sync
Bluetooth: mgmt: take lock and hold reference when handling hci_conn
Bluetooth: L2CAP: fix hci_conn_valid() usage
Bluetooth: hci_sync: extend conn_hash lookup RCU critical sections
Bluetooth: hci_sync: make hci_cmd_sync_run* indicate if item was added
Bluetooth: hci_sync: hci_cmd_sync_queue_once() return -EEXIST if
exists
Bluetooth: hci_conn: hold reference in abort_conn_sync
Bluetooth: hci_sync: hold references in hci_sync callbacks
net/bluetooth/hci_conn.c | 22 +++++-
net/bluetooth/hci_event.c | 33 +++++----
net/bluetooth/hci_sync.c | 144 ++++++++++++++++++++++++++++++-------
net/bluetooth/l2cap_core.c | 8 ++-
net/bluetooth/mgmt.c | 42 +++++++++--
5 files changed, 202 insertions(+), 47 deletions(-)
--
2.51.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/9] Bluetooth: hci_event: extend hdev lock in hci_le_remote_conn_param_req_evt
2025-10-03 17:08 [PATCH 0/9] Bluetooth: avoid concurrent deletion of hci_conn Pauli Virtanen
@ 2025-10-03 17:08 ` Pauli Virtanen
2025-10-03 17:08 ` [PATCH 2/9] Bluetooth: hci_conn: take hdev lock in set_cig_params_sync Pauli Virtanen
` (8 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Pauli Virtanen @ 2025-10-03 17:08 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
Cover conn lookup and field access with hdev lock in
hci_le_remote_conn_param_req_evt.
This avoids any concurrent deletion of the conn before we are done
dereferencing it.
Fixes: 95118dd4edfec ("Bluetooth: hci_event: Use of a function table to handle LE subevents")
Signed-off-by: Pauli Virtanen <pav@iki.fi>
---
net/bluetooth/hci_event.c | 33 ++++++++++++++++++++-------------
1 file changed, 20 insertions(+), 13 deletions(-)
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 1dabf5a7ae18..91da067e7997 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -6620,25 +6620,31 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, void *data,
latency = le16_to_cpu(ev->latency);
timeout = le16_to_cpu(ev->timeout);
+ hci_dev_lock(hdev);
+
hcon = hci_conn_hash_lookup_handle(hdev, handle);
- if (!hcon || hcon->state != BT_CONNECTED)
- return send_conn_param_neg_reply(hdev, handle,
- HCI_ERROR_UNKNOWN_CONN_ID);
+ if (!hcon || hcon->state != BT_CONNECTED) {
+ send_conn_param_neg_reply(hdev, handle,
+ HCI_ERROR_UNKNOWN_CONN_ID);
+ goto unlock;
+ }
- if (max > hcon->le_conn_max_interval)
- return send_conn_param_neg_reply(hdev, handle,
- HCI_ERROR_INVALID_LL_PARAMS);
+ if (max > hcon->le_conn_max_interval) {
+ send_conn_param_neg_reply(hdev, handle,
+ HCI_ERROR_INVALID_LL_PARAMS);
+ goto unlock;
+ }
- if (hci_check_conn_params(min, max, latency, timeout))
- return send_conn_param_neg_reply(hdev, handle,
- HCI_ERROR_INVALID_LL_PARAMS);
+ if (hci_check_conn_params(min, max, latency, timeout)) {
+ send_conn_param_neg_reply(hdev, handle,
+ HCI_ERROR_INVALID_LL_PARAMS);
+ goto unlock;
+ }
if (hcon->role == HCI_ROLE_MASTER) {
struct hci_conn_params *params;
u8 store_hint;
- hci_dev_lock(hdev);
-
params = hci_conn_params_lookup(hdev, &hcon->dst,
hcon->dst_type);
if (params) {
@@ -6651,8 +6657,6 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, void *data,
store_hint = 0x00;
}
- hci_dev_unlock(hdev);
-
mgmt_new_conn_param(hdev, &hcon->dst, hcon->dst_type,
store_hint, min, max, latency, timeout);
}
@@ -6666,6 +6670,9 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, void *data,
cp.max_ce_len = 0;
hci_send_cmd(hdev, HCI_OP_LE_CONN_PARAM_REQ_REPLY, sizeof(cp), &cp);
+
+unlock:
+ hci_dev_unlock(hdev);
}
static void hci_le_direct_adv_report_evt(struct hci_dev *hdev, void *data,
--
2.51.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 2/9] Bluetooth: hci_conn: take hdev lock in set_cig_params_sync
2025-10-03 17:08 [PATCH 0/9] Bluetooth: avoid concurrent deletion of hci_conn Pauli Virtanen
2025-10-03 17:08 ` [PATCH 1/9] Bluetooth: hci_event: extend hdev lock in hci_le_remote_conn_param_req_evt Pauli Virtanen
@ 2025-10-03 17:08 ` Pauli Virtanen
2025-10-03 17:08 ` [PATCH 3/9] Bluetooth: mgmt: take lock and hold reference when handling hci_conn Pauli Virtanen
` (7 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Pauli Virtanen @ 2025-10-03 17:08 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
Take hdev lock to prevent hci_conn from being deleted or modified
concurrently.
Fixes: a091289218202 ("Bluetooth: hci_conn: Fix hci_le_set_cig_params")
Signed-off-by: Pauli Virtanen <pav@iki.fi>
---
net/bluetooth/hci_conn.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 111f0e37b672..af17884fdf75 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -1813,9 +1813,13 @@ static int set_cig_params_sync(struct hci_dev *hdev, void *data)
u8 aux_num_cis = 0;
u8 cis_id;
+ hci_dev_lock(hdev);
+
conn = hci_conn_hash_lookup_cig(hdev, cig_id);
- if (!conn)
+ if (!conn) {
+ hci_dev_unlock(hdev);
return 0;
+ }
qos = &conn->iso_qos;
pdu->cig_id = cig_id;
@@ -1854,6 +1858,8 @@ static int set_cig_params_sync(struct hci_dev *hdev, void *data)
}
pdu->num_cis = aux_num_cis;
+ hci_dev_unlock(hdev);
+
if (!pdu->num_cis)
return 0;
--
2.51.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 3/9] Bluetooth: mgmt: take lock and hold reference when handling hci_conn
2025-10-03 17:08 [PATCH 0/9] Bluetooth: avoid concurrent deletion of hci_conn Pauli Virtanen
2025-10-03 17:08 ` [PATCH 1/9] Bluetooth: hci_event: extend hdev lock in hci_le_remote_conn_param_req_evt Pauli Virtanen
2025-10-03 17:08 ` [PATCH 2/9] Bluetooth: hci_conn: take hdev lock in set_cig_params_sync Pauli Virtanen
@ 2025-10-03 17:08 ` Pauli Virtanen
2025-10-03 17:08 ` [PATCH 4/9] Bluetooth: L2CAP: fix hci_conn_valid() usage Pauli Virtanen
` (6 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Pauli Virtanen @ 2025-10-03 17:08 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
Take hdev/rcu lock to prevent concurrent deletion of the hci_conn we are
handling.
When using hci_conn* pointers across critical sections, always take
refcount to keep the pointer valid.
For hci_abort_conn() only hold refcount, as the function takes
hdev->lock itself.
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 | 42 ++++++++++++++++++++++++++++++++++++++----
1 file changed, 38 insertions(+), 4 deletions(-)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index a3d16eece0d2..94e683bd09b9 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -3065,6 +3065,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);
@@ -3072,6 +3074,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;
@@ -3079,6 +3086,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;
}
@@ -3226,6 +3234,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);
@@ -3233,6 +3243,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;
@@ -3240,6 +3255,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;
}
@@ -7286,6 +7302,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));
@@ -7300,6 +7319,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,
@@ -7307,12 +7328,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);
@@ -7446,6 +7471,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));
@@ -7462,15 +7490,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.51.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 4/9] Bluetooth: L2CAP: fix hci_conn_valid() usage
2025-10-03 17:08 [PATCH 0/9] Bluetooth: avoid concurrent deletion of hci_conn Pauli Virtanen
` (2 preceding siblings ...)
2025-10-03 17:08 ` [PATCH 3/9] Bluetooth: mgmt: take lock and hold reference when handling hci_conn Pauli Virtanen
@ 2025-10-03 17:08 ` Pauli Virtanen
2025-10-03 17:08 ` [PATCH 5/9] Bluetooth: hci_sync: extend conn_hash lookup RCU critical sections Pauli Virtanen
` (5 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Pauli Virtanen @ 2025-10-03 17:08 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
Hold RCU lock while queuing to ensure conn is not deleted concurrently.
Fixes: b4f82f9ed43ae ("Bluetooth: L2CAP: Fix slab-use-after-free Read in l2cap_send_cmd")
Signed-off-by: Pauli Virtanen <pav@iki.fi>
---
net/bluetooth/l2cap_core.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 805c752ac0a9..7900a6dd2b1b 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -948,11 +948,17 @@ static u8 l2cap_get_ident(struct l2cap_conn *conn)
static void l2cap_send_acl(struct l2cap_conn *conn, struct sk_buff *skb,
u8 flags)
{
- /* Check if the hcon still valid before attempting to send */
+ /* Check if the hcon still valid before attempting to send.
+ * Lockless: conn->handle/hdev accessed, but shouldn't change here.
+ */
+ rcu_read_lock();
+
if (hci_conn_valid(conn->hcon->hdev, conn->hcon))
hci_send_acl(conn->hchan, skb, flags);
else
kfree_skb(skb);
+
+ rcu_read_unlock();
}
static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
--
2.51.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 5/9] Bluetooth: hci_sync: extend conn_hash lookup RCU critical sections
2025-10-03 17:08 [PATCH 0/9] Bluetooth: avoid concurrent deletion of hci_conn Pauli Virtanen
` (3 preceding siblings ...)
2025-10-03 17:08 ` [PATCH 4/9] Bluetooth: L2CAP: fix hci_conn_valid() usage Pauli Virtanen
@ 2025-10-03 17:08 ` Pauli Virtanen
2025-10-03 17:08 ` [PATCH 6/9] Bluetooth: hci_sync: make hci_cmd_sync_run* indicate if item was added Pauli Virtanen
` (4 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Pauli Virtanen @ 2025-10-03 17:08 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
Extend critical section to cover both hci_conn_hash lookup and use of
the returned conn. Add separate function for when we are just checking
if a conn exists.
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")
Fixes: cf75ad8b41d2a ("Bluetooth: hci_sync: Convert MGMT_SET_POWERED")
Fixes: c2994b008492d ("Bluetooth: hci_sync: Fix not setting Random Address when required")
Signed-off-by: Pauli Virtanen <pav@iki.fi>
---
net/bluetooth/hci_sync.c | 49 +++++++++++++++++++++++++++++++++++-----
1 file changed, 43 insertions(+), 6 deletions(-)
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 28ad08cd7d70..bf68da42e664 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -1035,6 +1035,21 @@ static bool adv_use_rpa(struct hci_dev *hdev, uint32_t flags)
return true;
}
+static bool hci_check_le_connect(struct hci_dev *hdev)
+{
+ bool found;
+
+ rcu_read_lock();
+ found = hci_lookup_le_connect(hdev);
+ rcu_read_unlock();
+
+ /* The return value may be wrong if the conn is modified concurrently,
+ * e.g. by HCI event. This function should be used only when this is OK
+ * (time of check doesn't matter or operation will be tried again).
+ */
+ return found;
+}
+
static int hci_set_random_addr_sync(struct hci_dev *hdev, bdaddr_t *rpa)
{
/* If a random_addr has been set we're advertising or initiating an LE
@@ -1049,7 +1064,7 @@ static int hci_set_random_addr_sync(struct hci_dev *hdev, bdaddr_t *rpa)
*/
if (bacmp(&hdev->random_addr, BDADDR_ANY) &&
(hci_dev_test_flag(hdev, HCI_LE_ADV) ||
- hci_lookup_le_connect(hdev))) {
+ hci_check_le_connect(hdev))) {
bt_dev_dbg(hdev, "Deferring random address update");
hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
return 0;
@@ -2636,7 +2651,7 @@ static int hci_pause_addr_resolution(struct hci_dev *hdev)
* when initiating an LE connection.
*/
if (hci_dev_test_flag(hdev, HCI_LE_SCAN) ||
- hci_lookup_le_connect(hdev)) {
+ hci_check_le_connect(hdev)) {
bt_dev_err(hdev, "Command not allowed when scan/LE connect");
return -EPERM;
}
@@ -2778,6 +2793,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;
@@ -2787,6 +2804,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.
*/
@@ -2796,6 +2815,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();
}
}
@@ -2806,10 +2827,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);
@@ -2817,6 +2841,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.
*/
@@ -2943,6 +2969,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) {
@@ -2967,8 +2995,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();
}
}
}
@@ -3224,7 +3256,7 @@ int hci_update_passive_scan_sync(struct hci_dev *hdev)
* since some controllers are not able to scan and connect at
* the same time.
*/
- if (hci_lookup_le_connect(hdev))
+ if (hci_check_le_connect(hdev))
return 0;
bt_dev_dbg(hdev, "start background scanning");
@@ -3479,12 +3511,17 @@ int hci_update_scan_sync(struct hci_dev *hdev)
if (hdev->scanning_paused)
return 0;
+ /* If connection states change we update again, so lockless is OK */
+ 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.51.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 6/9] Bluetooth: hci_sync: make hci_cmd_sync_run* indicate if item was added
2025-10-03 17:08 [PATCH 0/9] Bluetooth: avoid concurrent deletion of hci_conn Pauli Virtanen
` (4 preceding siblings ...)
2025-10-03 17:08 ` [PATCH 5/9] Bluetooth: hci_sync: extend conn_hash lookup RCU critical sections Pauli Virtanen
@ 2025-10-03 17:08 ` Pauli Virtanen
2025-10-03 17:08 ` [PATCH 7/9] Bluetooth: hci_sync: hci_cmd_sync_queue_once() return -EEXIST if exists Pauli Virtanen
` (3 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Pauli Virtanen @ 2025-10-03 17:08 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
hci_cmd_sync_run_once() needs to indicate whether a queue item was
added, so caller can know if callbacks are called. Change the function
to return -EEXIST if queue item already exists.
hci_cmd_sync_run() may run the work immediately. If so, make it behave
as if item was queued successfully. Change it to call also destroy() and
return 0.
Modify all callsites vs. the changes.
Signed-off-by: Pauli Virtanen <pav@iki.fi>
---
net/bluetooth/hci_conn.c | 4 +++-
net/bluetooth/hci_sync.c | 13 ++++++++++---
2 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index af17884fdf75..10dba0cac78b 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -2936,6 +2936,7 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data)
int hci_abort_conn(struct hci_conn *conn, u8 reason)
{
struct hci_dev *hdev = conn->hdev;
+ int err;
/* If abort_reason has already been set it means the connection is
* already being aborted so don't attempt to overwrite it.
@@ -2972,7 +2973,8 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
* as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
* already queue its callback on cmd_sync_work.
*/
- return hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
+ err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
+ return (err == -EEXIST) ? 0 : err;
}
void hci_setup_tx_timestamp(struct sk_buff *skb, size_t key_offset,
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index bf68da42e664..1764f61fc520 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -801,8 +801,15 @@ int hci_cmd_sync_run(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
return -ENETDOWN;
/* If on cmd_sync_work then run immediately otherwise queue */
- if (current_work() == &hdev->cmd_sync_work)
- return func(hdev, data);
+ if (current_work() == &hdev->cmd_sync_work) {
+ int err;
+
+ err = func(hdev, data);
+ if (destroy)
+ destroy(hdev, data, err);
+
+ return 0;
+ }
return hci_cmd_sync_submit(hdev, func, data, destroy);
}
@@ -818,7 +825,7 @@ int hci_cmd_sync_run_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy)
{
if (hci_cmd_sync_lookup_entry(hdev, func, data, destroy))
- return 0;
+ return -EEXIST;
return hci_cmd_sync_run(hdev, func, data, destroy);
}
--
2.51.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 7/9] Bluetooth: hci_sync: hci_cmd_sync_queue_once() return -EEXIST if exists
2025-10-03 17:08 [PATCH 0/9] Bluetooth: avoid concurrent deletion of hci_conn Pauli Virtanen
` (5 preceding siblings ...)
2025-10-03 17:08 ` [PATCH 6/9] Bluetooth: hci_sync: make hci_cmd_sync_run* indicate if item was added Pauli Virtanen
@ 2025-10-03 17:08 ` Pauli Virtanen
2025-10-03 17:08 ` [PATCH 8/9] Bluetooth: hci_conn: hold reference in abort_conn_sync Pauli Virtanen
` (2 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Pauli Virtanen @ 2025-10-03 17:08 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
hci_cmd_sync_queue_once() needs to indicate whether a queue item was
added, so caller can know if callbacks are called.
Change the function to return -EEXIST if queue item already exists.
Modify all callsites to handle that.
Signed-off-by: Pauli Virtanen <pav@iki.fi>
---
net/bluetooth/hci_sync.c | 37 ++++++++++++++++++++++++++-----------
1 file changed, 26 insertions(+), 11 deletions(-)
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 1764f61fc520..c5a618b30131 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -780,7 +780,7 @@ int hci_cmd_sync_queue_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy)
{
if (hci_cmd_sync_lookup_entry(hdev, func, data, destroy))
- return 0;
+ return -EEXIST;
return hci_cmd_sync_queue(hdev, func, data, destroy);
}
@@ -3294,6 +3294,8 @@ static int update_passive_scan_sync(struct hci_dev *hdev, void *data)
int hci_update_passive_scan(struct hci_dev *hdev)
{
+ int err;
+
/* Only queue if it would have any effect */
if (!test_bit(HCI_UP, &hdev->flags) ||
test_bit(HCI_INIT, &hdev->flags) ||
@@ -3303,8 +3305,9 @@ int hci_update_passive_scan(struct hci_dev *hdev)
hci_dev_test_flag(hdev, HCI_UNREGISTER))
return 0;
- return hci_cmd_sync_queue_once(hdev, update_passive_scan_sync, NULL,
- NULL);
+ err = hci_cmd_sync_queue_once(hdev, update_passive_scan_sync, NULL,
+ NULL);
+ return (err == -EEXIST) ? 0 : err;
}
int hci_write_sc_support_sync(struct hci_dev *hdev, u8 val)
@@ -6954,8 +6957,11 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
- return hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn,
- NULL);
+ int err;
+
+ err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn,
+ NULL);
+ return (err == -EEXIST) ? 0 : err;
}
static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
@@ -6991,8 +6997,11 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
- return hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn,
- create_le_conn_complete);
+ int err;
+
+ err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn,
+ create_le_conn_complete);
+ return (err == -EEXIST) ? 0 : err;
}
int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
@@ -7150,8 +7159,11 @@ static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data)
int hci_connect_pa_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
- return hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, conn,
- create_pa_complete);
+ int err;
+
+ err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, conn,
+ create_pa_complete);
+ return (err == -EEXIST) ? 0 : err;
}
static void create_big_complete(struct hci_dev *hdev, void *data, int err)
@@ -7213,6 +7225,9 @@ static int hci_le_big_create_sync(struct hci_dev *hdev, void *data)
int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
- return hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn,
- create_big_complete);
+ int err;
+
+ err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn,
+ create_big_complete);
+ return (err == -EEXIST) ? 0 : err;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 8/9] Bluetooth: hci_conn: hold reference in abort_conn_sync
2025-10-03 17:08 [PATCH 0/9] Bluetooth: avoid concurrent deletion of hci_conn Pauli Virtanen
` (6 preceding siblings ...)
2025-10-03 17:08 ` [PATCH 7/9] Bluetooth: hci_sync: hci_cmd_sync_queue_once() return -EEXIST if exists Pauli Virtanen
@ 2025-10-03 17:08 ` Pauli Virtanen
2025-10-03 17:08 ` [PATCH 9/9] Bluetooth: hci_sync: hold references in hci_sync callbacks Pauli Virtanen
2025-10-06 0:39 ` [PATCH 0/9] Bluetooth: avoid concurrent deletion of hci_conn Pauli Virtanen
9 siblings, 0 replies; 11+ messages in thread
From: Pauli Virtanen @ 2025-10-03 17:08 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
hci_conn_valid() should not be used on potentially freed hci_conn
pointers, as relying on kalloc not reusing addresses is bad practice.
Hold a hci_conn reference for the queue job so the pointer is not freed
too early.
This also avoids potential UAF during abort_conn_sync().
Fixes: 881559af5f5c5 ("Bluetooth: hci_sync: Attempt to dequeue connection attempt")
Signed-off-by: Pauli Virtanen <pav@iki.fi>
---
net/bluetooth/hci_conn.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 10dba0cac78b..2e12c918da0a 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -2933,6 +2933,13 @@ static int abort_conn_sync(struct hci_dev *hdev, void *data)
return hci_abort_conn_sync(hdev, conn, conn->abort_reason);
}
+static void abort_conn_destroy(struct hci_dev *hdev, void *data, int err)
+{
+ struct hci_conn *conn = data;
+
+ hci_conn_put(conn);
+}
+
int hci_abort_conn(struct hci_conn *conn, u8 reason)
{
struct hci_dev *hdev = conn->hdev;
@@ -2973,7 +2980,10 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
* as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
* already queue its callback on cmd_sync_work.
*/
- err = hci_cmd_sync_run_once(hdev, abort_conn_sync, conn, NULL);
+ err = hci_cmd_sync_run_once(hdev, abort_conn_sync, hci_conn_get(conn),
+ abort_conn_destroy);
+ if (err)
+ hci_conn_put(conn);
return (err == -EEXIST) ? 0 : err;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 9/9] Bluetooth: hci_sync: hold references in hci_sync callbacks
2025-10-03 17:08 [PATCH 0/9] Bluetooth: avoid concurrent deletion of hci_conn Pauli Virtanen
` (7 preceding siblings ...)
2025-10-03 17:08 ` [PATCH 8/9] Bluetooth: hci_conn: hold reference in abort_conn_sync Pauli Virtanen
@ 2025-10-03 17:08 ` Pauli Virtanen
2025-10-06 0:39 ` [PATCH 0/9] Bluetooth: avoid concurrent deletion of hci_conn Pauli Virtanen
9 siblings, 0 replies; 11+ messages in thread
From: Pauli Virtanen @ 2025-10-03 17:08 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Pauli Virtanen
hci_conn_valid() should not be used on potentially freed hci_conn
pointers, as relying on kalloc not reusing addresses is bad practice.
Hold a hci_conn reference for queued jobs so the pointers are not freed
too early.
This also avoids potential UAF if the conn would be freed while the jobs
are running.
Fixes: 881559af5f5c5 ("Bluetooth: hci_sync: Attempt to dequeue connection attempt")
Signed-off-by: Pauli Virtanen <pav@iki.fi>
---
net/bluetooth/hci_sync.c | 55 +++++++++++++++++++++++++++++++---------
1 file changed, 43 insertions(+), 12 deletions(-)
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index c5a618b30131..8832f1c6875c 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -6955,12 +6955,23 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
conn->conn_timeout, NULL);
}
+static void hci_acl_create_conn_sync_complete(struct hci_dev *hdev, void *data,
+ int err)
+{
+ struct hci_conn *conn = data;
+
+ hci_conn_put(conn);
+}
+
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
int err;
- err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn,
- NULL);
+ err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync,
+ hci_conn_get(conn),
+ hci_acl_create_conn_sync_complete);
+ if (err)
+ hci_conn_put(conn);
return (err == -EEXIST) ? 0 : err;
}
@@ -6971,36 +6982,41 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
bt_dev_dbg(hdev, "err %d", err);
if (err == -ECANCELED)
- return;
+ goto done;
hci_dev_lock(hdev);
if (!hci_conn_valid(hdev, conn))
- goto done;
+ goto unlock;
if (!err) {
hci_connect_le_scan_cleanup(conn, 0x00);
- goto done;
+ goto unlock;
}
/* Check if connection is still pending */
if (conn != hci_lookup_le_connect(hdev))
- goto done;
+ goto unlock;
/* Flush to make sure we send create conn cancel command if needed */
flush_delayed_work(&conn->le_conn_timeout);
hci_conn_failed(conn, bt_status(err));
-done:
+unlock:
hci_dev_unlock(hdev);
+done:
+ hci_conn_put(conn);
}
int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
int err;
- err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn,
+ err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync,
+ hci_conn_get(conn),
create_le_conn_complete);
+ if (err)
+ hci_conn_put(conn);
return (err == -EEXIST) ? 0 : err;
}
@@ -7048,7 +7064,7 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
bt_dev_dbg(hdev, "err %d", err);
if (err == -ECANCELED)
- return;
+ goto done;
hci_dev_lock(hdev);
@@ -7072,6 +7088,8 @@ static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
unlock:
hci_dev_unlock(hdev);
+done:
+ hci_conn_put(conn);
}
static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data)
@@ -7161,8 +7179,11 @@ int hci_connect_pa_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
int err;
- err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, conn,
+ err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync,
+ hci_conn_get(conn),
create_pa_complete);
+ if (err)
+ hci_conn_put(conn);
return (err == -EEXIST) ? 0 : err;
}
@@ -7173,10 +7194,17 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err)
bt_dev_dbg(hdev, "err %d", err);
if (err == -ECANCELED)
- return;
+ goto done;
+
+ hci_dev_lock(hdev);
if (hci_conn_valid(hdev, conn))
clear_bit(HCI_CONN_CREATE_BIG_SYNC, &conn->flags);
+
+ hci_dev_unlock(hdev);
+
+done:
+ hci_conn_put(conn);
}
static int hci_le_big_create_sync(struct hci_dev *hdev, void *data)
@@ -7227,7 +7255,10 @@ int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
int err;
- err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn,
+ err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync,
+ hci_conn_get(conn),
create_big_complete);
+ if (err)
+ hci_conn_put(conn);
return (err == -EEXIST) ? 0 : err;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 0/9] Bluetooth: avoid concurrent deletion of hci_conn
2025-10-03 17:08 [PATCH 0/9] Bluetooth: avoid concurrent deletion of hci_conn Pauli Virtanen
` (8 preceding siblings ...)
2025-10-03 17:08 ` [PATCH 9/9] Bluetooth: hci_sync: hold references in hci_sync callbacks Pauli Virtanen
@ 2025-10-06 0:39 ` Pauli Virtanen
9 siblings, 0 replies; 11+ messages in thread
From: Pauli Virtanen @ 2025-10-06 0:39 UTC (permalink / raw)
To: linux-bluetooth
pe, 2025-10-03 kello 20:08 +0300, Pauli Virtanen kirjoitti:
> This contains the simpler fixes from
> https://lore.kernel.org/linux-bluetooth/cover.1758481869.git.pav@iki.fi/
>
> hdev has two workqueues that run concurrently, and both may delete
> hci_conn. hci_conn* pointers then require either (i) hdev/rcu lock
> covering lookup and usage, or (ii) hci_conn_get reference held.
>
> If neither is done, it's likely there are corner cases that hit UAF,
> especially if controller misbehaves.
It may be possible to relax the requirements to also allow
(iii) work executing in hdev->workqueue can assume hci_conn* is not
deleted concurrently.
This is possible if something like the following can be made to work.
However, it may be better to just fix the locking without such trick to
keep it simpler, as there are not that many changes needed and we
anyway need to lock & get in hci_sync.
struct hci_conn_del_sync_work {
struct work_struct work;
struct hci_conn *conn;
};
void hci_conn_del_sync_work(struct work_struct *work)
{
struct hci_conn_del_sync_work *del = container_of(...);
struct hci_dev *hdev = del->conn->hdev;
hci_dev_lock(hdev);
if (hci_conn_valid(hdev, del->conn)
hci_conn_del(del->conn);
hci_dev_unlock(hdev);
hci_conn_put(del->conn);
}
void hci_conn_del_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
struct hci_conn_del_sync_work del;
INIT_WORK(&del.work, hci_conn_del_sync_work);
del.conn = hci_conn_get(conn);
queue_work(hdev->workqueue, &del.work);
flush_work(&del.work);
}
>
> Correct code in several places to follow the patterns (1)
>
> take lock
> conn = hci_conn_hash_lookup(...)
> if (conn)
> do_something(conn)
> release lock
>
> or (2)
>
> take lock
> conn = hci_conn_hash_lookup(...)
> if (conn)
> conn = hci_conn_get(conn)
> release lock
> do_something_carefully(conn)
> hci_conn_put(conn)
>
> Generally do_something_carefully should do (3)
>
> take lock
> if (hci_conn_valid(hdev, conn))
> do_something(conn)
> release lock
>
> hci_conn_valid() shouldn't be called unless refcount on conn is known to
> be held, as the pointer may otherwise already be freed, and even though
> hci_conn_valid() doesn't dereference the comparison of freed pointer it
> does is strictly speaking undefined behavior (kalloc is not guaranteed
> to not reuse addresses).
>
> Some of the code touched here is missing locks for (3), those need to be
> addressed in separate series.
>
> Pauli Virtanen (9):
> Bluetooth: hci_event: extend hdev lock in
> hci_le_remote_conn_param_req_evt
> Bluetooth: hci_conn: take hdev lock in set_cig_params_sync
> Bluetooth: mgmt: take lock and hold reference when handling hci_conn
> Bluetooth: L2CAP: fix hci_conn_valid() usage
> Bluetooth: hci_sync: extend conn_hash lookup RCU critical sections
> Bluetooth: hci_sync: make hci_cmd_sync_run* indicate if item was added
> Bluetooth: hci_sync: hci_cmd_sync_queue_once() return -EEXIST if
> exists
> Bluetooth: hci_conn: hold reference in abort_conn_sync
> Bluetooth: hci_sync: hold references in hci_sync callbacks
>
> net/bluetooth/hci_conn.c | 22 +++++-
> net/bluetooth/hci_event.c | 33 +++++----
> net/bluetooth/hci_sync.c | 144 ++++++++++++++++++++++++++++++-------
> net/bluetooth/l2cap_core.c | 8 ++-
> net/bluetooth/mgmt.c | 42 +++++++++--
> 5 files changed, 202 insertions(+), 47 deletions(-)
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2025-10-06 0:41 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-03 17:08 [PATCH 0/9] Bluetooth: avoid concurrent deletion of hci_conn Pauli Virtanen
2025-10-03 17:08 ` [PATCH 1/9] Bluetooth: hci_event: extend hdev lock in hci_le_remote_conn_param_req_evt Pauli Virtanen
2025-10-03 17:08 ` [PATCH 2/9] Bluetooth: hci_conn: take hdev lock in set_cig_params_sync Pauli Virtanen
2025-10-03 17:08 ` [PATCH 3/9] Bluetooth: mgmt: take lock and hold reference when handling hci_conn Pauli Virtanen
2025-10-03 17:08 ` [PATCH 4/9] Bluetooth: L2CAP: fix hci_conn_valid() usage Pauli Virtanen
2025-10-03 17:08 ` [PATCH 5/9] Bluetooth: hci_sync: extend conn_hash lookup RCU critical sections Pauli Virtanen
2025-10-03 17:08 ` [PATCH 6/9] Bluetooth: hci_sync: make hci_cmd_sync_run* indicate if item was added Pauli Virtanen
2025-10-03 17:08 ` [PATCH 7/9] Bluetooth: hci_sync: hci_cmd_sync_queue_once() return -EEXIST if exists Pauli Virtanen
2025-10-03 17:08 ` [PATCH 8/9] Bluetooth: hci_conn: hold reference in abort_conn_sync Pauli Virtanen
2025-10-03 17:08 ` [PATCH 9/9] Bluetooth: hci_sync: hold references in hci_sync callbacks Pauli Virtanen
2025-10-06 0:39 ` [PATCH 0/9] Bluetooth: avoid concurrent deletion of hci_conn Pauli Virtanen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox