Skip to content

Commit 6170191

Browse files
fourdimgregkh
authored andcommitted
Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()
commit 12917f5 upstream. 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(), which dispatches to a dedicated per-type cancel function. The create command is in exactly one of two states: still queued, or in flight. The cancel function 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 flag mechanism via HCI_CONN_CREATE_CIS but cannot be dequeued per-connection. hci_acl_create_conn_sync() and hci_le_create_conn_sync() clear HCI_CONN_CREATE after the create command completes, but the command status handler can free conn via hci_conn_del() (for example when the controller rejects the connection) while the worker is still blocked on the connection complete event. Hold a reference on conn across the create command so the flag can be cleared without a use-after-free. Fixes: a13f316 ("Bluetooth: hci_conn: Consolidate code for aborting connections") Cc: stable@vger.kernel.org Suggested-by: XIAO WU <xiaowu.417@qq.com> Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Siwei Zhang <oss@fourdim.xyz> Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 26168db commit 6170191

3 files changed

Lines changed: 123 additions & 32 deletions

File tree

include/net/bluetooth/hci_core.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,7 @@ enum {
984984
HCI_CONN_AUTH_FAILURE,
985985
HCI_CONN_PER_ADV,
986986
HCI_CONN_BIG_CREATED,
987+
HCI_CONN_CREATE,
987988
HCI_CONN_CREATE_CIS,
988989
HCI_CONN_CREATE_BIG_SYNC,
989990
HCI_CONN_BIG_SYNC,

net/bluetooth/hci_conn.c

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2979,26 +2979,11 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
29792979

29802980
conn->abort_reason = reason;
29812981

2982-
/* If the connection is pending check the command opcode since that
2983-
* might be blocking on hci_cmd_sync_work while waiting its respective
2984-
* event so we need to hci_cmd_sync_cancel to cancel it.
2985-
*
2986-
* hci_connect_le serializes the connection attempts so only one
2987-
* connection can be in BT_CONNECT at time.
2982+
/* Cancel the connect attempt. A return of 0 means the create command
2983+
* was still queued and got dequeued, so there is nothing to disconnect.
29882984
*/
2989-
if (conn->state == BT_CONNECT && READ_ONCE(hdev->req_status) == HCI_REQ_PEND) {
2990-
switch (hci_skb_event(hdev->sent_cmd)) {
2991-
case HCI_EV_CONN_COMPLETE:
2992-
case HCI_EV_LE_CONN_COMPLETE:
2993-
case HCI_EV_LE_ENHANCED_CONN_COMPLETE:
2994-
case HCI_EVT_LE_CIS_ESTABLISHED:
2995-
hci_cmd_sync_cancel(hdev, ECANCELED);
2996-
break;
2997-
}
2998-
/* Cancel connect attempt if still queued/pending */
2999-
} else if (!hci_cancel_connect_sync(hdev, conn)) {
2985+
if (!hci_cancel_connect_sync(hdev, conn))
30002986
return 0;
3001-
}
30022987

30032988
/* Run immediately if on cmd_sync_work since this may be called
30042989
* as a result to MGMT_OP_DISCONNECT/MGMT_OP_UNPAIR which does

net/bluetooth/hci_sync.c

Lines changed: 119 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6556,6 +6556,11 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
65566556

65576557
bt_dev_dbg(hdev, "conn %p", conn);
65586558

6559+
/* Hold a reference so conn stays valid for the HCI_CONN_CREATE
6560+
* clear_bit() at done.
6561+
*/
6562+
hci_conn_get(conn);
6563+
65596564
clear_bit(HCI_CONN_SCANNING, &conn->flags);
65606565
conn->state = BT_CONNECT;
65616566

@@ -6568,6 +6573,7 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
65686573
hdev->le_scan_type == LE_SCAN_ACTIVE &&
65696574
!hci_dev_test_flag(hdev, HCI_LE_SIMULTANEOUS_ROLES)) {
65706575
hci_conn_del(conn);
6576+
hci_conn_put(conn);
65716577
return -EBUSY;
65726578
}
65736579

@@ -6613,6 +6619,12 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
66136619
&own_addr_type);
66146620
if (err)
66156621
goto done;
6622+
6623+
/* Mark create connection in flight so hci_cancel_connect_sync() can
6624+
* cancel it while blocking on the connection complete event.
6625+
*/
6626+
set_bit(HCI_CONN_CREATE, &conn->flags);
6627+
66166628
/* Send command LE Extended Create Connection if supported */
66176629
if (use_ext_conn(hdev)) {
66186630
err = hci_le_ext_create_conn_sync(hdev, conn, own_addr_type);
@@ -6648,11 +6660,14 @@ static int hci_le_create_conn_sync(struct hci_dev *hdev, void *data)
66486660
conn->conn_timeout, NULL);
66496661

66506662
done:
6663+
clear_bit(HCI_CONN_CREATE, &conn->flags);
6664+
66516665
if (err == -ETIMEDOUT)
66526666
hci_le_connect_cancel_sync(hdev, conn, 0x00);
66536667

66546668
/* Re-enable advertising after the connection attempt is finished. */
66556669
hci_resume_advertising_sync(hdev);
6670+
hci_conn_put(conn);
66566671
return err;
66576672
}
66586673

@@ -6927,10 +6942,25 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
69276942
else
69286943
cp.role_switch = 0x00;
69296944

6930-
return __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
6931-
sizeof(cp), &cp,
6932-
HCI_EV_CONN_COMPLETE,
6933-
conn->conn_timeout, NULL);
6945+
/* Hold a reference so conn stays valid for the HCI_CONN_CREATE
6946+
* clear_bit() below.
6947+
*/
6948+
hci_conn_get(conn);
6949+
6950+
/* Mark create connection in flight so hci_cancel_connect_sync() can
6951+
* cancel it while blocking on the connection complete event.
6952+
*/
6953+
set_bit(HCI_CONN_CREATE, &conn->flags);
6954+
6955+
err = __hci_cmd_sync_status_sk(hdev, HCI_OP_CREATE_CONN,
6956+
sizeof(cp), &cp,
6957+
HCI_EV_CONN_COMPLETE,
6958+
conn->conn_timeout, NULL);
6959+
6960+
clear_bit(HCI_CONN_CREATE, &conn->flags);
6961+
hci_conn_put(conn);
6962+
6963+
return err;
69346964
}
69356965

69366966
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
@@ -6976,22 +7006,97 @@ int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
69767006
create_le_conn_complete);
69777007
}
69787008

6979-
int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
7009+
static int hci_acl_cancel_create_conn_sync(struct hci_dev *hdev,
7010+
struct hci_conn *conn)
69807011
{
6981-
if (conn->state != BT_OPEN)
6982-
return -EINVAL;
7012+
struct hci_cmd_sync_work_entry *entry;
7013+
int err = -EBUSY;
7014+
7015+
/* cmd_sync_work_lock makes the HCI_CONN_CREATE test and the cancel
7016+
* atomic against the worker, which takes this lock to dequeue every
7017+
* entry: while it is held no other command can become pending, so
7018+
* hci_cmd_sync_cancel() cannot cancel an unrelated command.
7019+
*/
7020+
mutex_lock(&hdev->cmd_sync_work_lock);
7021+
7022+
/* In flight: this connection owns the pending request, cancel it. */
7023+
if (test_bit(HCI_CONN_CREATE, &conn->flags)) {
7024+
hci_cmd_sync_cancel(hdev, ECANCELED);
7025+
goto unlock;
7026+
}
7027+
7028+
/* Still queued: a successful dequeue means it never started, so there
7029+
* is nothing to disconnect.
7030+
*/
7031+
entry = _hci_cmd_sync_lookup_entry(hdev, hci_acl_create_conn_sync, conn,
7032+
NULL);
7033+
if (entry) {
7034+
_hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
7035+
err = 0;
7036+
}
7037+
7038+
unlock:
7039+
mutex_unlock(&hdev->cmd_sync_work_lock);
7040+
return err;
7041+
}
7042+
7043+
static int hci_le_cancel_create_conn_sync(struct hci_dev *hdev,
7044+
struct hci_conn *conn)
7045+
{
7046+
struct hci_cmd_sync_work_entry *entry;
7047+
int err = -EBUSY;
69837048

7049+
/* cmd_sync_work_lock keeps the HCI_CONN_CREATE test and the cancel
7050+
* atomic against the cmd_sync worker.
7051+
*/
7052+
mutex_lock(&hdev->cmd_sync_work_lock);
7053+
7054+
if (test_bit(HCI_CONN_CREATE, &conn->flags)) {
7055+
hci_cmd_sync_cancel(hdev, ECANCELED);
7056+
goto unlock;
7057+
}
7058+
7059+
entry = _hci_cmd_sync_lookup_entry(hdev, hci_le_create_conn_sync, conn,
7060+
create_le_conn_complete);
7061+
if (entry) {
7062+
_hci_cmd_sync_cancel_entry(hdev, entry, -ECANCELED);
7063+
err = 0;
7064+
}
7065+
7066+
unlock:
7067+
mutex_unlock(&hdev->cmd_sync_work_lock);
7068+
return err;
7069+
}
7070+
7071+
static int hci_cis_cancel_create_conn_sync(struct hci_dev *hdev,
7072+
struct hci_conn *conn)
7073+
{
7074+
/* LE Create CIS is shared by the whole CIG and cannot be dequeued
7075+
* per-connection, so only an in-flight command can be cancelled.
7076+
* cmd_sync_work_lock keeps the test and the cancel atomic against the
7077+
* cmd_sync worker.
7078+
*/
7079+
mutex_lock(&hdev->cmd_sync_work_lock);
7080+
7081+
if (test_bit(HCI_CONN_CREATE_CIS, &conn->flags))
7082+
hci_cmd_sync_cancel(hdev, ECANCELED);
7083+
7084+
mutex_unlock(&hdev->cmd_sync_work_lock);
7085+
return -EBUSY;
7086+
}
7087+
7088+
int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
7089+
{
69847090
switch (conn->type) {
69857091
case ACL_LINK:
6986-
return !hci_cmd_sync_dequeue_once(hdev,
6987-
hci_acl_create_conn_sync,
6988-
conn, NULL);
7092+
return hci_acl_cancel_create_conn_sync(hdev, conn);
69897093
case LE_LINK:
6990-
return !hci_cmd_sync_dequeue_once(hdev, hci_le_create_conn_sync,
6991-
conn, create_le_conn_complete);
7094+
return hci_le_cancel_create_conn_sync(hdev, conn);
7095+
case CIS_LINK:
7096+
return hci_cis_cancel_create_conn_sync(hdev, conn);
7097+
default:
7098+
return -ENOENT;
69927099
}
6993-
6994-
return -ENOENT;
69957100
}
69967101

69977102
int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn,

0 commit comments

Comments
 (0)