* [PATCH v4 1/2] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()
@ 2026-06-12 16:16 Siwei Zhang
2026-06-12 16:16 ` [PATCH v4 2/2] Bluetooth: hci_sync: Remove unused hci_cmd_sync_dequeue_once() Siwei Zhang
2026-06-12 18:32 ` [v4,1/2] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn() bluez.test.bot
0 siblings, 2 replies; 3+ messages in thread
From: Siwei Zhang @ 2026-06-12 16:16 UTC (permalink / raw)
To: Luiz Augusto von Dentz, Pauli Virtanen; +Cc: linux-bluetooth, Siwei Zhang
hci_abort_conn() read hci_skb_event(hdev->sent_cmd) when a connection
was pending, but hdev->sent_cmd can be NULL while req_status is still
HCI_REQ_PEND, leading to a NULL pointer dereference and a general
protection fault from the hci_rx_work() receive path.
Instead of inspecting hdev->sent_cmd, track the in-flight create
connection command with a new per-connection HCI_CONN_CREATE flag and
route all cancellation through hci_cancel_connect_sync(). The create
command is in exactly one of two states: still queued, or in flight.
hci_cancel_connect_sync() holds cmd_sync_work_lock across the whole
decision: the worker takes this lock to dequeue every entry, so while it
is held a queued command cannot start running and an in-flight command
cannot complete and let the next command become pending. This keeps the
flag test and hci_cmd_sync_cancel() atomic with respect to the worker,
so a queued command is simply dequeued, and an in-flight command owned
by this connection is cancelled without the risk of cancelling an
unrelated command that became pending in the meantime. CIS uses the same
path via the existing HCI_CONN_CREATE_CIS flag.
Fixes: a13f316e90fd ("Bluetooth: hci_conn: Consolidate code for aborting connections")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Siwei Zhang <oss@fourdim.xyz>
---
include/net/bluetooth/hci_core.h | 1 +
net/bluetooth/hci_conn.c | 21 ++------
net/bluetooth/hci_sync.c | 83 +++++++++++++++++++++++++++-----
3 files changed, 75 insertions(+), 30 deletions(-)
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index aa600fbf9a53..aa554c34f9ec 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -988,6 +988,7 @@ enum {
HCI_CONN_AUTH_FAILURE,
HCI_CONN_PER_ADV,
HCI_CONN_BIG_CREATED,
+ HCI_CONN_CREATE,
HCI_CONN_CREATE_CIS,
HCI_CONN_CREATE_BIG_SYNC,
HCI_CONN_BIG_SYNC,
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 54eabaa46960..eba4a548bef5 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -3181,26 +3181,11 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
conn->abort_reason = reason;
- /* If the connection is pending check the command opcode since that
- * might be blocking on hci_cmd_sync_work while waiting its respective
- * event so we need to hci_cmd_sync_cancel to cancel it.
- *
- * hci_connect_le serializes the connection attempts so only one
- * connection can be in BT_CONNECT at time.
+ /* Cancel the connect attempt. A return of 0 means the create command
+ * was still queued and got dequeued, so there is nothing to disconnect.
*/
- if (conn->state == BT_CONNECT && READ_ONCE(hdev->req_status) == HCI_REQ_PEND) {
- switch (hci_skb_event(hdev->sent_cmd)) {
- case HCI_EV_CONN_COMPLETE:
- case HCI_EV_LE_CONN_COMPLETE:
- case HCI_EV_LE_ENHANCED_CONN_COMPLETE:
- case HCI_EVT_LE_CIS_ESTABLISHED:
- hci_cmd_sync_cancel(hdev, ECANCELED);
- break;
- }
- /* Cancel connect attempt if still queued/pending */
- } else if (!hci_cancel_connect_sync(hdev, conn)) {
+ if (!hci_cancel_connect_sync(hdev, conn))
return 0;
- }
/* Run immediately if on cmd_sync_work since this may be called
* as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index df23245d6ccd..5a6ffdb84c88 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -6668,6 +6668,12 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
&own_addr_type);
if (err)
goto done;
+
+ /* Mark create connection in flight so hci_cancel_connect_sync() can
+ * cancel it while blocking on the connection complete event.
+ */
+ set_bit(HCI_CONN_CREATE, &conn->flags);
+
/* Send command LE Extended Create Connection if supported */
if (use_ext_conn(hdev)) {
err = hci_le_ext_create_conn_sync(hdev, conn, own_addr_type);
@@ -6703,6 +6709,8 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
conn->conn_timeout, NULL);
done:
+ clear_bit(HCI_CONN_CREATE, &conn->flags);
+
if (err == -ETIMEDOUT)
hci_le_connect_cancel_sync(hdev, conn, 0x00);
@@ -6982,10 +6990,19 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
else
cp.role_switch = 0x00;
- return __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
- sizeof(cp), &cp,
- HCI_EV_CONN_COMPLETE,
- conn->conn_timeout, NULL);
+ /* Mark create connection in flight so hci_cancel_connect_sync() can
+ * cancel it while blocking on the connection complete event.
+ */
+ set_bit(HCI_CONN_CREATE, &conn->flags);
+
+ err = __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
+ sizeof(cp), &cp,
+ HCI_EV_CONN_COMPLETE,
+ conn->conn_timeout, NULL);
+
+ clear_bit(HCI_CONN_CREATE, &conn->flags);
+
+ return err;
}
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
@@ -7039,20 +7056,62 @@ int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
{
- if (conn->state != BT_OPEN)
- return -EINVAL;
+ struct hci_cmd_sync_work_entry *entry;
+ hci_cmd_sync_work_func_t func = NULL;
+ hci_cmd_sync_work_destroy_t destroy = NULL;
+ int create_flag = -1;
+ int err = -EBUSY;
switch (conn->type) {
case ACL_LINK:
- return !hci_cmd_sync_dequeue_once(hdev,
- hci_acl_create_conn_sync,
- conn, NULL);
+ func = hci_acl_create_conn_sync;
+ create_flag = HCI_CONN_CREATE;
+ break;
case LE_LINK:
- return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync,
- conn, create_le_conn_complete);
+ func = hci_le_create_conn_sync;
+ destroy = create_le_conn_complete;
+ create_flag = HCI_CONN_CREATE;
+ break;
+ case CIS_LINK:
+ /* LE Create CIS is shared by the whole CIG and cannot be
+ * dequeued per-connection; only cancel it in-flight below.
+ */
+ create_flag = HCI_CONN_CREATE_CIS;
+ break;
+ default:
+ return -ENOENT;
}
- return -ENOENT;
+ /* The create command is either still queued or in flight. Hold
+ * cmd_sync_work_lock across the test and the cancel: the worker takes
+ * this lock to dequeue every entry, so while it is held no other command
+ * can become pending, which keeps hci_cmd_sync_cancel() from racing with
+ * completion and cancelling an unrelated command.
+ */
+ mutex_lock(&hdev->cmd_sync_work_lock);
+
+ /* The flag is set while the worker blocks on the connection complete
+ * event, so if it is set this connection owns the pending request.
+ */
+ if (create_flag >= 0 && test_bit(create_flag, &conn->flags)) {
+ hci_cmd_sync_cancel(hdev, ECANCELED);
+ goto unlock;
+ }
+
+ /* Otherwise it may still be queued; dequeue it. A successful dequeue
+ * means it never started, so there is nothing to disconnect.
+ */
+ if (func) {
+ entry = _hci_cmd_sync_lookup_entry(hdev, func, conn, destroy);
+ if (entry) {
+ _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
+ err = 0;
+ }
+ }
+
+unlock:
+ mutex_unlock(&hdev->cmd_sync_work_lock);
+ return err;
}
int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn,
--
2.54.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH v4 2/2] Bluetooth: hci_sync: Remove unused hci_cmd_sync_dequeue_once()
2026-06-12 16:16 [PATCH v4 1/2] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn() Siwei Zhang
@ 2026-06-12 16:16 ` Siwei Zhang
2026-06-12 18:32 ` [v4,1/2] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn() bluez.test.bot
1 sibling, 0 replies; 3+ messages in thread
From: Siwei Zhang @ 2026-06-12 16:16 UTC (permalink / raw)
To: Luiz Augusto von Dentz, Pauli Virtanen; +Cc: linux-bluetooth, Siwei Zhang
hci_cmd_sync_dequeue_once() had a single in-tree caller,
hci_cancel_connect_sync(), which now holds cmd_sync_work_lock across the
in-flight create flag test and the dequeue and so open-codes the lookup
and cancel under that lock. That leaves the exported
hci_cmd_sync_dequeue_once() with no in-tree user, so remove it along with
its declaration.
Signed-off-by: Siwei Zhang <oss@fourdim.xyz>
---
include/net/bluetooth/hci_sync.h | 3 ---
net/bluetooth/hci_sync.c | 26 --------------------------
2 files changed, 29 deletions(-)
diff --git a/include/net/bluetooth/hci_sync.h b/include/net/bluetooth/hci_sync.h
index 73e494b2591d..818e62d9fe9e 100644
--- a/include/net/bluetooth/hci_sync.h
+++ b/include/net/bluetooth/hci_sync.h
@@ -84,9 +84,6 @@ void hci_cmd_sync_cancel_entry(struct hci_dev *hdev,
struct hci_cmd_sync_work_entry *entry);
bool hci_cmd_sync_dequeue(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
void *data, hci_cmd_sync_work_destroy_t destroy);
-bool hci_cmd_sync_dequeue_once(struct hci_dev *hdev,
- hci_cmd_sync_work_func_t func, void *data,
- hci_cmd_sync_work_destroy_t destroy);
int hci_update_eir_sync(struct hci_dev *hdev);
int hci_update_class_sync(struct hci_dev *hdev);
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 5a6ffdb84c88..0e44489c5309 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -860,32 +860,6 @@ void hci_cmd_sync_cancel_entry(struct hci_dev *hdev,
}
EXPORT_SYMBOL(hci_cmd_sync_cancel_entry);
-/* Dequeue one HCI command entry:
- *
- * - Lookup and cancel first entry that matches.
- */
-bool hci_cmd_sync_dequeue_once(struct hci_dev *hdev,
- hci_cmd_sync_work_func_t func,
- void *data, hci_cmd_sync_work_destroy_t destroy)
-{
- struct hci_cmd_sync_work_entry *entry;
-
- mutex_lock(&hdev->cmd_sync_work_lock);
-
- entry = _hci_cmd_sync_lookup_entry(hdev, func, data, destroy);
- if (!entry) {
- mutex_unlock(&hdev->cmd_sync_work_lock);
- return false;
- }
-
- _hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
-
- mutex_unlock(&hdev->cmd_sync_work_lock);
-
- return true;
-}
-EXPORT_SYMBOL(hci_cmd_sync_dequeue_once);
-
/* Dequeue HCI command entry:
*
* - Lookup and cancel any entry that matches by function callback or data or
--
2.54.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* RE: [v4,1/2] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()
2026-06-12 16:16 [PATCH v4 1/2] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn() Siwei Zhang
2026-06-12 16:16 ` [PATCH v4 2/2] Bluetooth: hci_sync: Remove unused hci_cmd_sync_dequeue_once() Siwei Zhang
@ 2026-06-12 18:32 ` bluez.test.bot
1 sibling, 0 replies; 3+ messages in thread
From: bluez.test.bot @ 2026-06-12 18:32 UTC (permalink / raw)
To: linux-bluetooth, oss
[-- Attachment #1: Type: text/plain, Size: 2794 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1110757
---Test result---
Test Summary:
CheckPatch PASS 2.09 seconds
VerifyFixes PASS 0.13 seconds
VerifySignedoff PASS 0.13 seconds
GitLint PASS 0.64 seconds
SubjectPrefix PASS 0.24 seconds
BuildKernel PASS 31.25 seconds
CheckAllWarning PASS 29.22 seconds
CheckSparse PASS 27.09 seconds
BuildKernel32 PASS 24.86 seconds
TestRunnerSetup PASS 539.28 seconds
TestRunner_l2cap-tester PASS 59.68 seconds
TestRunner_iso-tester PASS 85.37 seconds
TestRunner_bnep-tester PASS 18.81 seconds
TestRunner_mgmt-tester FAIL 211.24 seconds
TestRunner_rfcomm-tester PASS 25.31 seconds
TestRunner_sco-tester PASS 32.96 seconds
TestRunner_ioctl-tester PASS 26.06 seconds
TestRunner_mesh-tester FAIL 25.94 seconds
TestRunner_smp-tester PASS 23.50 seconds
TestRunner_userchan-tester PASS 20.60 seconds
TestRunner_6lowpan-tester FAIL 46.95 seconds
IncrementalBuild PASS 42.35 seconds
Details
##############################
Test: TestRunner_mgmt-tester - FAIL
Desc: Run mgmt-tester with test-runner
Output:
Total: 494, Passed: 489 (99.0%), Failed: 1, Not Run: 4
Failed Test Cases
Read Exp Feature - Success Failed 0.247 seconds
##############################
Test: TestRunner_mesh-tester - FAIL
Desc: Run mesh-tester with test-runner
Output:
Total: 10, Passed: 8 (80.0%), Failed: 2, Not Run: 0
Failed Test Cases
Mesh - Send cancel - 1 Timed out 2.773 seconds
Mesh - Send cancel - 2 Timed out 1.990 seconds
##############################
Test: TestRunner_6lowpan-tester - FAIL
Desc: Run 6lowpan-tester with test-runner
Output:
Total: 8, Passed: 3 (37.5%), Failed: 5, Not Run: 0
Failed Test Cases
Client Connect - Disconnect Timed out 5.128 seconds
Client Recv Dgram - Success Timed out 4.988 seconds
Client Recv Raw - Success Timed out 4.994 seconds
Client Recv IPHC Dgram - Success Timed out 4.996 seconds
Client Recv IPHC Raw - Success Timed out 4.994 seconds
https://github.com/bluez/bluetooth-next/pull/311
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-06-12 18:32 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-12 16:16 [PATCH v4 1/2] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn() Siwei Zhang
2026-06-12 16:16 ` [PATCH v4 2/2] Bluetooth: hci_sync: Remove unused hci_cmd_sync_dequeue_once() Siwei Zhang
2026-06-12 18:32 ` [v4,1/2] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn() bluez.test.bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox