FAILED: patch "[PATCH] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()" failed to apply to 6.6-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.6-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.6.y
git checkout FETCH_HEAD
git cherry-pick -x 12917f591cea1af36087dba5b9ec888652f0b42a
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026071350-agent-mustard-7408@gregkh' --subject-prefix 'PATCH 6.6.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 12917f591cea1af36087dba5b9ec888652f0b42a Mon Sep 17 00:00:00 2001
From: Siwei Zhang <[email protected]>
Date: Mon, 15 Jun 2026 11:33:05 -0400
Subject: [PATCH] Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()
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.
include/net/bluetooth/hci_core.h +1 -0
@@ -985,6 +985,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,
net/bluetooth/hci_conn.c +3 -18
@@ -3178,26 +3178,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
net/bluetooth/hci_sync.c +121 -16
182 lines not on this page · raw message
Sasha Levin
maintainerPATCH 1/4 Bluetooth: hci_core: Enable buffer flow control for SCO/eSCO
From: Luiz Augusto von Dentz <[email protected]>
[ Upstream commit 13218453521d75916dfed55efb8e809bfc03cb4b ]
This enables buffer flow control for SCO/eSCO
(see: Bluetooth Core 6.0 spec: 6.22. Synchronous Flow Control Enable), recently this has caused the following problem and is actually a nice addition for the likes of Socket TX complete:
< HCI Command: Read Buffer Size (0x04|0x0005) plen 0
Quoted 1 line
HCI Event: Command Complete (
0x0e) plen 11
Read Buffer Size (0x04|0x0005) ncmd 1
Status: Success (0x00)
ACL MTU: 1021 ACL max packet: 5
SCO MTU: 240 SCO max packet: 8
...
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
Quoted 1 line
HCI Event: Hardware Error (
0x10) plen 1
Code: 0x0a
To fix the code will now attempt to enable buffer flow control when HCI_QUIRK_SYNC_FLOWCTL_SUPPORTED is set by the driver:
< HCI Command: Write Sync Fl.. (0x03|0x002f) plen 1
Flow control: Enabled (0x01)
Quoted 1 line
HCI Event: Command Complete (
0x0e) plen 4
Write Sync Flow Control Enable (0x03|0x002f) ncmd 1
Status: Success (0x00)On success then HCI_SCO_FLOWCTL would be set which indicates sco_cnt shall be used for flow contro.
Stable-dep-of: 12917f591cea ("Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()")
--- include/net/bluetooth/hci.h | 13 +++++++ include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 62 +++++++++++++++----------------- net/bluetooth/hci_event.c | 2 ++ net/bluetooth/hci_sync.c | 24 +++++++++++++ 5 files changed, 68 insertions(+), 34 deletions(-)
include/net/bluetooth/hci.h +13 -0
@@ -206,6 +206,13 @@ enum {*/ HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED , +/* When this quirk is set consider Sync Flow Control as supported by +* the driver. +* +* This quirk must be set before hci_register_dev is called. +*/ + HCI_QUIRK_SYNC_FLOWCTL_SUPPORTED, +/* When this quirk is set, the controller has validated that * LE states reported through the HCI_LE_READ_SUPPORTED_STATES are * valid. This mechanism is necessary as many controllers have
@@ -416,6 +423,7 @@ enum {HCI_WIDEBAND_SPEECH_ENABLED ,HCI_EVENT_FILTER_CONFIGURED ,HCI_PA_SYNC , +HCI_SCO_FLOWCTL ,HCI_DUT_MODE , HCI_VENDOR_DIAG,
@@ -1513,6 +1521,11 @@ struct hci_rp_read_tx_power {__s8 tx_power; } __packed; +#define HCI_OP_WRITE_SYNC_FLOWCTL 0x0c2f +struct hci_cp_write_sync_flowctl { +__u8 enable; +} __packed; +#define HCI_OP_READ_PAGE_SCAN_TYPE 0x0c46struct hci_rp_read_page_scan_type {__u8 status;
include/net/bluetooth/hci_core.h +1 -0
@@ -1824,6 +1824,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn);#define lmp_hold_capable (dev) ((dev)->features[0][0] & LMP_HOLD)#define lmp_sniff_capable (dev) ((dev)->features[0][0] & LMP_SNIFF)#define lmp_park_capable (dev) ((dev)->features[0][1] & LMP_PARK) +#define lmp_sco_capable (dev) ((dev)->features[0][1] & LMP_SCO)#define lmp_inq_rssi_capable (dev) ((dev)->features[0][3] & LMP_RSSI_INQ)#define lmp_esco_capable (dev) ((dev)->features[0][3] & LMP_ESCO)#define lmp_bredr_capable (dev) (!((dev)->features[0][4] & LMP_NO_BREDR))
net/bluetooth/hci_core.c +28 -34
102 lines not on this page · raw message
net/bluetooth/hci_event.c +2 -0
@@ -4445,9 +4445,11 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, void *data,break ;case SCO_LINK : +case ESCO_LINK : hdev->sco_cnt += count;if (hdev->sco_cnt > hdev->sco_pkts) hdev->sco_cnt = hdev->sco_pkts; +break ;case ISO_LINK :
net/bluetooth/hci_sync.c +24 -0
@@ -3877,6 +3877,28 @@ static int hci_write_ca_timeout_sync(struct hci_dev *hdev) sizeof(param), ¶m, HCI_CMD_TIMEOUT); } +/* Enable SCO flow control if supported */ +static int hci_write_sync_flowctl_sync (struct hci_dev *hdev) +{ +struct hci_cp_write_sync_flowctl cp; +int err; + +/* Check if the controller supports SCO and HCI_OP_WRITE_SYNC_FLOWCTL */ +if (!lmp_sco_capable (hdev) || !(hdev->commands[10 ] &BIT (4 )) || + !test_bit (HCI_QUIRK_SYNC_FLOWCTL_SUPPORTED , &hdev->quirks)) +return 0 ; + +memset (&cp,0 ,sizeof (cp)); + cp.enable =0x01 ; + + err =__hci_cmd_sync_status (hdev,HCI_OP_WRITE_SYNC_FLOWCTL , +sizeof (cp), &cp,HCI_CMD_TIMEOUT ); +if (!err) +hci_dev_set_flag (hdev,HCI_SCO_FLOWCTL ); + +return err; +} +/* BR Controller init stage 2 command sequence */ static const struct hci_init_stage br_init2[] = {/* HCI_OP_READ_BUFFER_SIZE */
@@ -3895,6 +3917,8 @@ static const struct hci_init_stage br_init2[] = {HCI_INIT (hci_clear_event_filter_sync),/* HCI_OP_WRITE_CA_TIMEOUT */ HCI_INIT (hci_write_ca_timeout_sync), +/* HCI_OP_WRITE_SYNC_FLOWCTL */ + HCI_INIT(hci_write_sync_flowctl_sync), {} };
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/4 Bluetooth: separate CIS_LINK and BIS_LINK link types
From: Pauli Virtanen <[email protected]>
[ Upstream commit 23205562ffc8de20f57afdd984858cab29e77968 ]
Use separate link type id for unicast and broadcast ISO connections. These connection types are handled with separate HCI commands, socket API is different, and hci_conn has union fields that are different in the two cases, so they shall not be mixed up.
Currently in most places it is attempted to distinguish ucast by bacmp(&c->dst, BDADDR_ANY) but it is wrong as dst is set for bcast sink hci_conn in iso_conn_ready(). Additionally checking sync_handle might be OK, but depends on details of bcast conn configuration flow.
To avoid complicating it, use separate link types.
Stable-dep-of: 12917f591cea ("Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()")
--- include/net/bluetooth/hci.h | 3 ++- include/net/bluetooth/hci_core.h | 38 ++++++++++++++++++-------------- net/bluetooth/hci_conn.c | 37 ++++++++++++++++++------------- net/bluetooth/hci_core.c | 21 +++++++++++------- net/bluetooth/hci_event.c | 25 +++++++++++---------- net/bluetooth/hci_sync.c | 14 +++++++----- net/bluetooth/iso.c | 4 ++-- net/bluetooth/mgmt.c | 3 ++- 8 files changed, 84 insertions(+), 61 deletions(-)
include/net/bluetooth/hci.h +2 -1
@@ -527,7 +527,8 @@ enum {#define ESCO_LINK 0x02/* Low Energy links do not have defined link type. Use invented one */ #define LE_LINK 0x80 -#define ISO_LINK 0x82 +#define CIS_LINK 0x82 +#define BIS_LINK 0x83#define INVALID_LINK 0xff/* LMP features */
include/net/bluetooth/hci_core.h +21 -17
@@ -995,7 +995,8 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)case ESCO_LINK : h->sco_num++;break ; -case ISO_LINK : +case CIS_LINK : +case BIS_LINK : h->iso_num++;break ; }
@@ -1021,7 +1022,8 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)case ESCO_LINK : h->sco_num--;break ; -case ISO_LINK : +case CIS_LINK : +case BIS_LINK : h->iso_num--;break ; }
@@ -1038,7 +1040,8 @@ static inline unsigned int hci_conn_num(struct hci_dev *hdev, __u8 type)case SCO_LINK :case ESCO_LINK :return h->sco_num; -case ISO_LINK : +case CIS_LINK : +case BIS_LINK :return h->iso_num;default :return 0 ;
@@ -1099,7 +1102,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_bis(struct hci_dev *hdev,rcu_read_lock ();list_for_each_entry_rcu (c, &h->list, list) { -if (bacmp (&c->dst, ba) || c->type !=ISO_LINK ) +if (bacmp (&c->dst, ba) || c->type !=BIS_LINK )continue ;if (c->iso_qos.bcast.bis == bis) {
@@ -1123,8 +1126,8 @@ hci_conn_hash_lookup_per_adv_bis(struct hci_dev *hdev,rcu_read_lock ();list_for_each_entry_rcu (c, &h->list, list) { -if (bacmp (&c->dst, ba) || c->type !=ISO_LINK || - !test_bit (HCI_CONN_PER_ADV , &c->flags)) +if (bacmp (&c->dst, ba) || c->type !=BIS_LINK || + !test_bit (HCI_CONN_PER_ADV , &c->flags))continue ; if (c->iso_qos.bcast.big == big &&
@@ -1234,7 +1237,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_cis(struct hci_dev *hdev,rcu_read_lock ();list_for_each_entry_rcu (c, &h->list, list) { -if (c->type !=ISO_LINK || !bacmp (&c->dst,BDADDR_ANY )) +if (c->type !=CIS_LINK )continue ;/* Match CIG ID if set */
@@ -1266,7 +1269,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_cig(struct hci_dev *hdev,rcu_read_lock ();list_for_each_entry_rcu (c, &h->list, list) { -if (c->type !=ISO_LINK || !bacmp (&c->dst,BDADDR_ANY )) +if (c->type !=CIS_LINK )continue ;if (handle == c->iso_qos.ucast.cig) {
@@ -1289,7 +1292,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_big(struct hci_dev *hdev,rcu_read_lock ();list_for_each_entry_rcu (c, &h->list, list) { -if (bacmp (&c->dst,BDADDR_ANY ) || c->type !=ISO_LINK ) +if (bacmp (&c->dst,BDADDR_ANY ) || c->type !=BIS_LINK )continue ;if (handle == c->iso_qos.bcast.big) {
@@ -1312,7 +1315,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_big_any_dst(struct hci_dev *rcu_read_lock ();list_for_each_entry_rcu (c, &h->list, list) { -if (c->type !=ISO_LINK ) +if (c->type !=BIS_LINK )continue ;if (handle !=BT_ISO_QOS_BIG_UNSET && handle == c->iso_qos.bcast.big) {
@@ -1335,8 +1338,8 @@ hci_conn_hash_lookup_big_state(struct hci_dev *hdev, __u8 handle, __u16 state)rcu_read_lock ();list_for_each_entry_rcu (c, &h->list, list) { -if (bacmp (&c->dst,BDADDR_ANY ) || c->type !=ISO_LINK || - c->state != state) +if (c->type !=BIS_LINK ||bacmp (&c->dst,BDADDR_ANY ) || + c->state != state)continue ;if (handle == c->iso_qos.bcast.big) {
@@ -1359,8 +1362,8 @@ hci_conn_hash_lookup_pa_sync_big_handle(struct hci_dev *hdev, __u8 big)rcu_read_lock ();list_for_each_entry_rcu (c, &h->list, list) { -if (c->type !=ISO_LINK || - !test_bit (HCI_CONN_PA_SYNC , &c->flags)) +if (c->type !=BIS_LINK || + !test_bit (HCI_CONN_PA_SYNC , &c->flags))continue ;if (c->iso_qos.bcast.big == big) {
@@ -1382,8 +1385,8 @@ hci_conn_hash_lookup_pa_sync_handle(struct hci_dev *hdev, __u16 sync_handle)rcu_read_lock ();list_for_each_entry_rcu (c, &h->list, list) { -if (c->type !=ISO_LINK || - !test_bit (HCI_CONN_PA_SYNC , &c->flags)) +if (c->type !=BIS_LINK || + !test_bit (HCI_CONN_PA_SYNC , &c->flags))continue ;if (c->sync_handle == sync_handle) {
@@ -1959,7 +1962,8 @@ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,case ESCO_LINK :return sco_connect_ind (hdev, bdaddr, flags); -case ISO_LINK : +case CIS_LINK : +case BIS_LINK :return iso_connect_ind (hdev, bdaddr, flags);default :
net/bluetooth/hci_conn.c +22 -15
@@ -870,9 +870,11 @@ static void cis_cleanup(struct hci_conn *conn)/* Check if ISO connection is a CIS and remove CIG if there are * no other connections using it. */ -hci_conn_hash_list_state (hdev, find_cis,ISO_LINK ,BT_BOUND , &d); -hci_conn_hash_list_state (hdev, find_cis,ISO_LINK ,BT_CONNECT , &d); -hci_conn_hash_list_state (hdev, find_cis,ISO_LINK ,BT_CONNECTED , &d); +hci_conn_hash_list_state (hdev, find_cis,CIS_LINK ,BT_BOUND , &d); +hci_conn_hash_list_state (hdev, find_cis,CIS_LINK ,BT_CONNECT , + &d); +hci_conn_hash_list_state (hdev, find_cis,CIS_LINK ,BT_CONNECTED , + &d);if (d.count)return ;
@@ -895,7 +897,8 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_tif (!hdev->acl_mtu)return ERR_PTR (-ECONNREFUSED );break ; -case ISO_LINK : +case CIS_LINK : +case BIS_LINK :if (hdev->iso_mtu)/* Dedicated ISO Buffer exists */ break ;
@@ -959,7 +962,8 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_thci_copy_identity_address (hdev, &conn->src, &conn->src_type); conn->mtu = hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu;break ; -case ISO_LINK : +case CIS_LINK : +case BIS_LINK :/* conn->src should reflect the local identity address */ hci_copy_identity_address (hdev, &conn->src, &conn->src_type);
@@ -1055,7 +1059,8 @@ static void hci_conn_cleanup_child(struct hci_conn *conn, u8 reason)if (HCI_CONN_HANDLE_UNSET (conn->handle))hci_conn_failed (conn, reason);break ; -case ISO_LINK : +case CIS_LINK : +case BIS_LINK :if ((conn->state !=BT_CONNECTED && !test_bit (HCI_CONN_CREATE_CIS , &conn->flags)) ||test_bit (HCI_CONN_BIG_CREATED , &conn->flags))
@@ -1130,7 +1135,8 @@ void hci_conn_del(struct hci_conn *conn) hdev->acl_cnt += conn->sent; }else {/* Unacked ISO frames */ -if (conn->type ==ISO_LINK ) { +if (conn->type ==CIS_LINK || + conn->type ==BIS_LINK ) {if (hdev->iso_pkts) hdev->iso_cnt += conn->sent;else if (hdev->le_pkts)
@@ -1501,7 +1507,7 @@ static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst, memcmp(conn->le_per_adv_data, base, base_len)))return ERR_PTR (-EADDRINUSE ); - conn =hci_conn_add_unset (hdev,ISO_LINK , dst,HCI_ROLE_MASTER ); + conn =hci_conn_add_unset (hdev,BIS_LINK , dst,HCI_ROLE_MASTER );if (IS_ERR (conn))return conn;
@@ -1707,7 +1713,7 @@ static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos) data.count =0 ;/* Create a BIS for each bound connection */ -hci_conn_hash_list_state (hdev, bis_list,ISO_LINK , +hci_conn_hash_list_state (hdev, bis_list,BIS_LINK ,BT_BOUND , &data); cp.handle = qos->bcast.big;
@@ -1803,12 +1809,12 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)for (data.cig =0x00 ; data.cig <0xf0 ; data.cig++) { data.count =0 ; -hci_conn_hash_list_state (hdev, find_cis,ISO_LINK , +hci_conn_hash_list_state (hdev, find_cis,CIS_LINK ,BT_CONNECT , &data);if (data.count)continue ; -hci_conn_hash_list_state (hdev, find_cis,ISO_LINK , +hci_conn_hash_list_state (hdev, find_cis,CIS_LINK ,BT_CONNECTED , &data);if (!data.count)break ;
@@ -1860,7 +1866,8 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst, cis =hci_conn_hash_lookup_cis (hdev, dst, dst_type, qos->ucast.cig, qos->ucast.cis);if (!cis) { - cis =hci_conn_add_unset (hdev,ISO_LINK , dst,HCI_ROLE_MASTER ); + cis =hci_conn_add_unset (hdev,CIS_LINK , dst, +HCI_ROLE_MASTER );if (IS_ERR (cis))return cis; cis->cleanup = cis_cleanup;
@@ -1950,7 +1957,7 @@ bool hci_iso_setup_path(struct hci_conn *conn)int hci_conn_check_create_cis (struct hci_conn *conn) { -if (conn->type !=ISO_LINK || !bacmp (&conn->dst,BDADDR_ANY )) +if (conn->type !=CIS_LINK )return -EINVAL ; if (!conn->parent || conn->parent->state != BT_CONNECTED ||
@@ -2248,7 +2255,7 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,* the start periodic advertising and create BIG commands have * been queued */ -hci_conn_hash_list_state (hdev, bis_mark_per_adv,ISO_LINK , +hci_conn_hash_list_state (hdev, bis_mark_per_adv,BIS_LINK ,BT_BOUND , &data);/* Queue start periodic advertising and create BIG */
@@ -2910,7 +2917,7 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)* hci_connect_le serializes the connection attempts so only one * connection can be in BT_CONNECT at time. */ -if (conn->state ==BT_CONNECT && hdev->req_status ==HCI_REQ_PEND ) { +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 :
net/bluetooth/hci_core.c +13 -8
@@ -2907,12 +2907,13 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)break ;case HCI_ACLDATA_PKT :/* Detect if ISO packet has been sent as ACL */ -if (hci_conn_num (hdev,ISO_LINK )) { +if (hci_conn_num (hdev,CIS_LINK ) || +hci_conn_num (hdev,BIS_LINK )) {__u16 handle =__le16_to_cpu (hci_acl_hdr (skb)->handle);__u8 type; type =hci_conn_lookup_type (hdev,hci_handle (handle)); -if (type ==ISO_LINK ) +if (type ==CIS_LINK || type ==BIS_LINK )hci_skb_pkt_type (skb) =HCI_ISODATA_PKT ; }break ;
@@ -3347,7 +3348,8 @@ static inline void hci_quote_sent(struct hci_conn *conn, int num, int *quote)case LE_LINK : cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;break ; -case ISO_LINK : +case CIS_LINK : +case BIS_LINK : cnt = hdev->iso_mtu ? hdev->iso_cnt : hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;break ;
@@ -3361,7 +3363,7 @@ static inline void hci_quote_sent(struct hci_conn *conn, int num, int *quote) }static struct hci_conn *hci_low_sent (struct hci_dev *hdev,__u8 type, -int *quote) +__u8 type2,int *quote) {struct hci_conn_hash *h = &hdev->conn_hash;struct hci_conn *conn =NULL , *c;
@@ -3373,7 +3375,8 @@ static struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type,rcu_read_lock ();list_for_each_entry_rcu (c, &h->list, list) { -if (c->type != type ||skb_queue_empty (&c->data_q)) +if ((c->type != type && c->type != type2) || +skb_queue_empty (&c->data_q))continue ;if (c->state !=BT_CONNECTED && c->state !=BT_CONFIG )
@@ -3576,7 +3579,7 @@ static void hci_sched_sco(struct hci_dev *hdev, __u8 type)else cnt = &hdev->sco_cnt; -while (*cnt && (conn =hci_low_sent (hdev, type, "e))) { +while (*cnt && (conn =hci_low_sent (hdev, type, type, "e))) {while (quote-- && (skb =skb_dequeue (&conn->data_q))) {BT_DBG ("skb %p len %d" , skb, skb->len);hci_send_frame (hdev, skb);
@@ -3704,12 +3707,14 @@ static void hci_sched_iso(struct hci_dev *hdev)BT_DBG ("%s" , hdev->name); -if (!hci_conn_num (hdev,ISO_LINK )) +if (!hci_conn_num (hdev,CIS_LINK ) && + !hci_conn_num (hdev,BIS_LINK ))return ; cnt = hdev->iso_pkts ? &hdev->iso_cnt : hdev->le_pkts ? &hdev->le_cnt : &hdev->acl_cnt; -while (*cnt && (conn =hci_low_sent (hdev,ISO_LINK , "e))) { +while (*cnt && (conn =hci_low_sent (hdev,CIS_LINK ,BIS_LINK , + "e))) {while (quote-- && (skb =skb_dequeue (&conn->data_q))) {BT_DBG ("skb %p len %d" , skb, skb->len);hci_send_frame (hdev, skb);
net/bluetooth/hci_event.c +13 -12
@@ -3776,7 +3776,7 @@ static void hci_unbound_cis_failed(struct hci_dev *hdev, u8 cig, u8 status)lockdep_assert_held (&hdev->lock);list_for_each_entry_safe (conn, tmp, &hdev->conn_hash.list, list) { -if (conn->type !=ISO_LINK || !bacmp (&conn->dst,BDADDR_ANY ) || +if (conn->type !=CIS_LINK || conn->state ==BT_OPEN || conn->iso_qos.ucast.cig != cig)continue ;
@@ -4452,7 +4452,8 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, void *data,break ; -case ISO_LINK : +case CIS_LINK : +case BIS_LINK :if (hdev->iso_pkts) { hdev->iso_cnt += count;if (hdev->iso_cnt > hdev->iso_pkts)
@@ -6384,7 +6385,7 @@ static void hci_le_pa_sync_estabilished_evt(struct hci_dev *hdev, void *data,hci_dev_clear_flag (hdev,HCI_PA_SYNC ); - mask |=hci_proto_connect_ind (hdev, &ev->bdaddr,ISO_LINK , &flags); + mask |=hci_proto_connect_ind (hdev, &ev->bdaddr,BIS_LINK , &flags);if (!(mask &HCI_LM_ACCEPT )) {hci_le_pa_term_sync (hdev, ev->handle); goto unlock;
@@ -6395,7 +6396,7 @@ static void hci_le_pa_sync_estabilished_evt(struct hci_dev *hdev, void *data,if (ev->status) {/* Add connection to indicate the failed PA sync event */ - pa_sync =hci_conn_add_unset (hdev,ISO_LINK ,BDADDR_ANY , + pa_sync =hci_conn_add_unset (hdev,BIS_LINK ,BDADDR_ANY ,HCI_ROLE_SLAVE );if (!pa_sync)
@@ -6422,7 +6423,7 @@ static void hci_le_per_adv_report_evt(struct hci_dev *hdev, void *data,hci_dev_lock (hdev); - mask |=hci_proto_connect_ind (hdev,BDADDR_ANY ,ISO_LINK , &flags); + mask |=hci_proto_connect_ind (hdev,BDADDR_ANY ,BIS_LINK , &flags);if (!(mask &HCI_LM_ACCEPT ))hci_le_pa_term_sync (hdev, ev->sync_handle);
@@ -6693,7 +6694,7 @@ static void hci_le_cis_estabilished_evt(struct hci_dev *hdev, void *data, goto unlock; } -if (conn->type !=ISO_LINK ) { +if (conn->type !=CIS_LINK ) {bt_dev_err (hdev,"Invalid connection link type handle 0x%4.4x" , handle);
@@ -6811,7 +6812,7 @@ static void hci_le_cis_req_evt(struct hci_dev *hdev, void *data,if (!acl) goto unlock; - mask =hci_proto_connect_ind (hdev, &acl->dst,ISO_LINK , &flags); + mask =hci_proto_connect_ind (hdev, &acl->dst,CIS_LINK , &flags);if (!(mask &HCI_LM_ACCEPT )) {hci_le_reject_cis (hdev, ev->cis_handle); goto unlock;
@@ -6819,8 +6820,8 @@ static void hci_le_cis_req_evt(struct hci_dev *hdev, void *data, cis =hci_conn_hash_lookup_handle (hdev, cis_handle);if (!cis) { - cis =hci_conn_add (hdev,ISO_LINK , &acl->dst,HCI_ROLE_SLAVE , - cis_handle); + cis =hci_conn_add (hdev,CIS_LINK , &acl->dst, +HCI_ROLE_SLAVE , cis_handle);if (IS_ERR (cis)) {hci_le_reject_cis (hdev, ev->cis_handle); goto unlock;
@@ -6954,7 +6955,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,bt_dev_dbg (hdev,"ignore too large handle %u" , handle);continue ; } - bis =hci_conn_add (hdev,ISO_LINK ,BDADDR_ANY , + bis =hci_conn_add (hdev,BIS_LINK ,BDADDR_ANY ,HCI_ROLE_SLAVE , handle);if (IS_ERR (bis))continue ;
@@ -7011,7 +7012,7 @@ static void hci_le_big_info_adv_report_evt(struct hci_dev *hdev, void *data,hci_dev_lock (hdev); - mask |=hci_proto_connect_ind (hdev,BDADDR_ANY ,ISO_LINK , &flags); + mask |=hci_proto_connect_ind (hdev,BDADDR_ANY ,BIS_LINK , &flags);if (!(mask &HCI_LM_ACCEPT )) {hci_le_pa_term_sync (hdev, ev->sync_handle); goto unlock;
@@ -7028,7 +7029,7 @@ static void hci_le_big_info_adv_report_evt(struct hci_dev *hdev, void *data, goto unlock;/* Add connection to indicate the PA sync event */ - pa_sync =hci_conn_add_unset (hdev,ISO_LINK ,BDADDR_ANY , + pa_sync =hci_conn_add_unset (hdev,BIS_LINK ,BDADDR_ANY ,HCI_ROLE_SLAVE );if (IS_ERR (pa_sync))
net/bluetooth/hci_sync.c +9 -5
@@ -2971,7 +2971,7 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type,if (sent) {struct hci_conn *conn; - conn =hci_conn_hash_lookup_ba (hdev,ISO_LINK , + conn =hci_conn_hash_lookup_ba (hdev,BIS_LINK , &sent->bdaddr);if (conn) {struct bt_iso_qos *qos = &conn->iso_qos;
@@ -5582,7 +5582,7 @@ static int hci_connect_cancel_sync(struct hci_dev *hdev, struct hci_conn *conn,if (conn->type ==LE_LINK )return hci_le_connect_cancel_sync (hdev, conn, reason); -if (conn->type ==ISO_LINK ) { +if (conn->type ==CIS_LINK ) {/* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E * page 1857: *
@@ -5595,9 +5595,10 @@ static int hci_connect_cancel_sync(struct hci_dev *hdev, struct hci_conn *conn,return hci_disconnect_sync (hdev, conn, reason);/* CIS with no Create CIS sent have nothing to cancel */ -if (bacmp (&conn->dst,BDADDR_ANY )) -return HCI_ERROR_LOCAL_HOST_TERM ; +return HCI_ERROR_LOCAL_HOST_TERM ; + } +if (conn->type ==BIS_LINK ) {/* There is no way to cancel a BIS without terminating the BIG * which is done later on connection cleanup. */
@@ -5659,9 +5660,12 @@ static int hci_reject_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, {struct hci_cp_reject_conn_req cp; -if (conn->type ==ISO_LINK ) +if (conn->type ==CIS_LINK )return hci_le_reject_cis_sync (hdev, conn, reason); +if (conn->type ==BIS_LINK ) +return -EINVAL ; +if (conn->type ==SCO_LINK || conn->type ==ESCO_LINK )return hci_reject_sco_sync (hdev, conn, reason);
net/bluetooth/iso.c +2 -2
@@ -1955,7 +1955,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)static void iso_connect_cfm (struct hci_conn *hcon,__u8 status) { -if (hcon->type !=ISO_LINK ) { +if (hcon->type !=CIS_LINK && hcon->type !=BIS_LINK ) {if (hcon->type !=LE_LINK )return ;
@@ -1996,7 +1996,7 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)static void iso_disconn_cfm (struct hci_conn *hcon,__u8 reason) { -if (hcon->type !=ISO_LINK ) +if (hcon->type !=CIS_LINK && hcon->type !=BIS_LINK )return ;BT_DBG ("hcon %p reason %d" , hcon, reason);
net/bluetooth/mgmt.c +2 -1
@@ -3233,7 +3233,8 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,static u8 link_to_bdaddr (u8 link_type,u8 addr_type) {switch (link_type) { -case ISO_LINK : +case CIS_LINK : +case BIS_LINK :case LE_LINK :switch (addr_type) {case ADDR_LE_DEV_PUBLIC :
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/4 Bluetooth: hci_sync: annotate data-races around hdev->req_status
In reply to Sasha LevinFrom: Cen Zhang <[email protected]>
[ Upstream commit b6807cfc195ef99e1ac37b2e1e60df40295daa8c ]
__hci_cmd_sync_sk() sets hdev->req_status under hdev->req_lock:
hdev->req_status = HCI_REQ_PEND;
However, several other functions read or write hdev->req_status without holding any lock:
- hci_send_cmd_sync() reads req_status in hci_cmd_work (workqueue)
- hci_cmd_sync_complete() reads/writes from HCI event completion
- hci_cmd_sync_cancel() / hci_cmd_sync_cancel_sync() read/write
- hci_abort_conn() reads in connection abort path
Since __hci_cmd_sync_sk() runs on hdev->req_workqueue while hci_send_cmd_sync() runs on hdev->workqueue, these are different workqueues that can execute concurrently on different CPUs. The plain C accesses constitute a data race.
Add READ_ONCE()/WRITE_ONCE() annotations on all concurrent accesses to hdev->req_status to prevent potential compiler optimizations that could affect correctness (e.g., load fusing in the wait_event condition or store reordering).
Stable-dep-of: 12917f591cea ("Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()")
--- net/bluetooth/hci_core.c | 2 +- net/bluetooth/hci_sync.c | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-)
net/bluetooth/hci_core.c +1 -1
@@ -4060,7 +4060,7 @@ static void hci_send_cmd_sync(struct hci_dev *hdev, struct sk_buff *skb)return ; } -if (hci_req_status_pend (hdev) && +if (READ_ONCE (hdev->req_status) ==HCI_REQ_PEND && !hci_dev_test_and_set_flag (hdev,HCI_CMD_PENDING )) {kfree_skb (hdev->req_skb); hdev->req_skb =skb_clone (hdev->sent_cmd,GFP_KERNEL );
net/bluetooth/hci_sync.c +10 -10
@@ -25,11 +25,11 @@ static void hci_cmd_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode, {bt_dev_dbg (hdev,"result 0x%2.2x" , result); -if (hdev->req_status !=HCI_REQ_PEND ) +if (READ_ONCE (hdev->req_status) !=HCI_REQ_PEND )return ; hdev->req_result = result; - hdev->req_status =HCI_REQ_DONE ; +WRITE_ONCE (hdev->req_status,HCI_REQ_DONE );/* Free the request command so it is not used as response */ kfree_skb (hdev->req_skb);
@@ -168,20 +168,20 @@ struct sk_buff *__hci_cmd_sync_sk(struct hci_dev *hdev, u16 opcode, u32 plen,hci_cmd_sync_add (&req, opcode, plen, param, event, sk); - hdev->req_status =HCI_REQ_PEND ; +WRITE_ONCE (hdev->req_status,HCI_REQ_PEND ); err =hci_req_sync_run (&req);if (err <0 )return ERR_PTR (err); err =wait_event_interruptible_timeout (hdev->req_wait_q, - hdev->req_status !=HCI_REQ_PEND , +READ_ONCE (hdev->req_status) !=HCI_REQ_PEND , timeout);if (err == -ERESTARTSYS )return ERR_PTR (-EINTR ); -switch (hdev->req_status) { +switch (READ_ONCE (hdev->req_status)) {case HCI_REQ_DONE : err = -bt_to_errno (hdev->req_result);break ;
@@ -195,7 +195,7 @@ struct sk_buff *__hci_cmd_sync_sk(struct hci_dev *hdev, u16 opcode, u32 plen,break ; } - hdev->req_status =0 ; +WRITE_ONCE (hdev->req_status,0 ); hdev->req_result =0 ; skb = hdev->req_rsp; hdev->req_rsp =NULL ;
@@ -689,9 +689,9 @@ void hci_cmd_sync_cancel(struct hci_dev *hdev, int err) {bt_dev_dbg (hdev,"err 0x%2.2x" , err); -if (hdev->req_status ==HCI_REQ_PEND ) { +if (READ_ONCE (hdev->req_status) ==HCI_REQ_PEND ) { hdev->req_result = err; - hdev->req_status =HCI_REQ_CANCELED ; +WRITE_ONCE (hdev->req_status,HCI_REQ_CANCELED );queue_work (hdev->workqueue, &hdev->cmd_sync_cancel_work); }
@@ -707,12 +707,12 @@ void hci_cmd_sync_cancel_sync(struct hci_dev *hdev, int err) {bt_dev_dbg (hdev,"err 0x%2.2x" , err); -if (hdev->req_status ==HCI_REQ_PEND ) { +if (READ_ONCE (hdev->req_status) ==HCI_REQ_PEND ) {/* req_result is __u32 so error must be positive to be properly * propagated. */ hdev->req_result = err <0 ? -err : err; - hdev->req_status =HCI_REQ_CANCELED ; +WRITE_ONCE (hdev->req_status,HCI_REQ_CANCELED );wake_up_interruptible (&hdev->req_wait_q); }
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/4 Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()
In reply to Sasha LevinFrom: Siwei Zhang <[email protected]>
[ Upstream commit 12917f591cea1af36087dba5b9ec888652f0b42a ]
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.
--- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_conn.c | 21 +---- net/bluetooth/hci_sync.c | 133 +++++++++++++++++++++++++++---- 3 files changed, 123 insertions(+), 32 deletions(-)
include/net/bluetooth/hci_core.h +1 -0
@@ -957,6 +957,7 @@ enum {HCI_CONN_AUTH_FAILURE ,HCI_CONN_PER_ADV ,HCI_CONN_BIG_CREATED , +HCI_CONN_CREATE ,HCI_CONN_CREATE_CIS ,HCI_CONN_BIG_SYNC , HCI_CONN_BIG_SYNC_FAILED,
net/bluetooth/hci_conn.c +3 -18
@@ -2910,26 +2910,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
net/bluetooth/hci_sync.c +119 -14
178 lines not on this page · raw message
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 1/4 Bluetooth: hci_core: Enable buffer flow control for SCO/eSCO
In reply to [email protected]From: Luiz Augusto von Dentz <[email protected]>
[ Upstream commit 13218453521d75916dfed55efb8e809bfc03cb4b ]
This enables buffer flow control for SCO/eSCO
(see: Bluetooth Core 6.0 spec: 6.22. Synchronous Flow Control Enable), recently this has caused the following problem and is actually a nice addition for the likes of Socket TX complete:
< HCI Command: Read Buffer Size (0x04|0x0005) plen 0
Quoted 1 line
HCI Event: Command Complete (
0x0e) plen 11
Read Buffer Size (0x04|0x0005) ncmd 1
Status: Success (0x00)
ACL MTU: 1021 ACL max packet: 5
SCO MTU: 240 SCO max packet: 8
...
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
< SCO Data TX: Handle 257 flags 0x00 dlen 120
Quoted 1 line
HCI Event: Hardware Error (
0x10) plen 1
Code: 0x0a
To fix the code will now attempt to enable buffer flow control when HCI_QUIRK_SYNC_FLOWCTL_SUPPORTED is set by the driver:
< HCI Command: Write Sync Fl.. (0x03|0x002f) plen 1
Flow control: Enabled (0x01)
Quoted 1 line
HCI Event: Command Complete (
0x0e) plen 4
Write Sync Flow Control Enable (0x03|0x002f) ncmd 1
Status: Success (0x00)On success then HCI_SCO_FLOWCTL would be set which indicates sco_cnt shall be used for flow contro.
Stable-dep-of: 12917f591cea ("Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()")
--- include/net/bluetooth/hci.h | 13 +++++++ include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 62 +++++++++++++++----------------- net/bluetooth/hci_event.c | 2 ++ net/bluetooth/hci_sync.c | 24 +++++++++++++ 5 files changed, 68 insertions(+), 34 deletions(-)
include/net/bluetooth/hci.h +13 -0
@@ -206,6 +206,13 @@ enum {*/ HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED , +/* When this quirk is set consider Sync Flow Control as supported by +* the driver. +* +* This quirk must be set before hci_register_dev is called. +*/ + HCI_QUIRK_SYNC_FLOWCTL_SUPPORTED, +/* When this quirk is set, the controller has validated that * LE states reported through the HCI_LE_READ_SUPPORTED_STATES are * valid. This mechanism is necessary as many controllers have
@@ -416,6 +423,7 @@ enum {HCI_WIDEBAND_SPEECH_ENABLED ,HCI_EVENT_FILTER_CONFIGURED ,HCI_PA_SYNC , +HCI_SCO_FLOWCTL ,HCI_DUT_MODE , HCI_VENDOR_DIAG,
@@ -1513,6 +1521,11 @@ struct hci_rp_read_tx_power {__s8 tx_power; } __packed; +#define HCI_OP_WRITE_SYNC_FLOWCTL 0x0c2f +struct hci_cp_write_sync_flowctl { +__u8 enable; +} __packed; +#define HCI_OP_READ_PAGE_SCAN_TYPE 0x0c46struct hci_rp_read_page_scan_type {__u8 status;
include/net/bluetooth/hci_core.h +1 -0
@@ -1824,6 +1824,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn);#define lmp_hold_capable (dev) ((dev)->features[0][0] & LMP_HOLD)#define lmp_sniff_capable (dev) ((dev)->features[0][0] & LMP_SNIFF)#define lmp_park_capable (dev) ((dev)->features[0][1] & LMP_PARK) +#define lmp_sco_capable (dev) ((dev)->features[0][1] & LMP_SCO)#define lmp_inq_rssi_capable (dev) ((dev)->features[0][3] & LMP_RSSI_INQ)#define lmp_esco_capable (dev) ((dev)->features[0][3] & LMP_ESCO)#define lmp_bredr_capable (dev) (!((dev)->features[0][4] & LMP_NO_BREDR))
net/bluetooth/hci_core.c +28 -34
102 lines not on this page · raw message
net/bluetooth/hci_event.c +2 -0
@@ -4445,9 +4445,11 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, void *data,break ;case SCO_LINK : +case ESCO_LINK : hdev->sco_cnt += count;if (hdev->sco_cnt > hdev->sco_pkts) hdev->sco_cnt = hdev->sco_pkts; +break ;case ISO_LINK :
net/bluetooth/hci_sync.c +24 -0
@@ -3877,6 +3877,28 @@ static int hci_write_ca_timeout_sync(struct hci_dev *hdev) sizeof(param), ¶m, HCI_CMD_TIMEOUT); } +/* Enable SCO flow control if supported */ +static int hci_write_sync_flowctl_sync (struct hci_dev *hdev) +{ +struct hci_cp_write_sync_flowctl cp; +int err; + +/* Check if the controller supports SCO and HCI_OP_WRITE_SYNC_FLOWCTL */ +if (!lmp_sco_capable (hdev) || !(hdev->commands[10 ] &BIT (4 )) || + !test_bit (HCI_QUIRK_SYNC_FLOWCTL_SUPPORTED , &hdev->quirks)) +return 0 ; + +memset (&cp,0 ,sizeof (cp)); + cp.enable =0x01 ; + + err =__hci_cmd_sync_status (hdev,HCI_OP_WRITE_SYNC_FLOWCTL , +sizeof (cp), &cp,HCI_CMD_TIMEOUT ); +if (!err) +hci_dev_set_flag (hdev,HCI_SCO_FLOWCTL ); + +return err; +} +/* BR Controller init stage 2 command sequence */ static const struct hci_init_stage br_init2[] = {/* HCI_OP_READ_BUFFER_SIZE */
@@ -3895,6 +3917,8 @@ static const struct hci_init_stage br_init2[] = {HCI_INIT (hci_clear_event_filter_sync),/* HCI_OP_WRITE_CA_TIMEOUT */ HCI_INIT (hci_write_ca_timeout_sync), +/* HCI_OP_WRITE_SYNC_FLOWCTL */ + HCI_INIT(hci_write_sync_flowctl_sync), {} };
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/4 Bluetooth: separate CIS_LINK and BIS_LINK link types
From: Pauli Virtanen <[email protected]>
[ Upstream commit 23205562ffc8de20f57afdd984858cab29e77968 ]
Use separate link type id for unicast and broadcast ISO connections. These connection types are handled with separate HCI commands, socket API is different, and hci_conn has union fields that are different in the two cases, so they shall not be mixed up.
Currently in most places it is attempted to distinguish ucast by bacmp(&c->dst, BDADDR_ANY) but it is wrong as dst is set for bcast sink hci_conn in iso_conn_ready(). Additionally checking sync_handle might be OK, but depends on details of bcast conn configuration flow.
To avoid complicating it, use separate link types.
Stable-dep-of: 12917f591cea ("Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()")
--- include/net/bluetooth/hci.h | 3 ++- include/net/bluetooth/hci_core.h | 38 ++++++++++++++++++-------------- net/bluetooth/hci_conn.c | 37 ++++++++++++++++++------------- net/bluetooth/hci_core.c | 21 +++++++++++------- net/bluetooth/hci_event.c | 25 +++++++++++---------- net/bluetooth/hci_sync.c | 14 +++++++----- net/bluetooth/iso.c | 4 ++-- net/bluetooth/mgmt.c | 3 ++- 8 files changed, 84 insertions(+), 61 deletions(-)
include/net/bluetooth/hci.h +2 -1
@@ -527,7 +527,8 @@ enum {#define ESCO_LINK 0x02/* Low Energy links do not have defined link type. Use invented one */ #define LE_LINK 0x80 -#define ISO_LINK 0x82 +#define CIS_LINK 0x82 +#define BIS_LINK 0x83#define INVALID_LINK 0xff/* LMP features */
include/net/bluetooth/hci_core.h +21 -17
@@ -995,7 +995,8 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)case ESCO_LINK : h->sco_num++;break ; -case ISO_LINK : +case CIS_LINK : +case BIS_LINK : h->iso_num++;break ; }
@@ -1021,7 +1022,8 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)case ESCO_LINK : h->sco_num--;break ; -case ISO_LINK : +case CIS_LINK : +case BIS_LINK : h->iso_num--;break ; }
@@ -1038,7 +1040,8 @@ static inline unsigned int hci_conn_num(struct hci_dev *hdev, __u8 type)case SCO_LINK :case ESCO_LINK :return h->sco_num; -case ISO_LINK : +case CIS_LINK : +case BIS_LINK :return h->iso_num;default :return 0 ;
@@ -1099,7 +1102,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_bis(struct hci_dev *hdev,rcu_read_lock ();list_for_each_entry_rcu (c, &h->list, list) { -if (bacmp (&c->dst, ba) || c->type !=ISO_LINK ) +if (bacmp (&c->dst, ba) || c->type !=BIS_LINK )continue ;if (c->iso_qos.bcast.bis == bis) {
@@ -1123,8 +1126,8 @@ hci_conn_hash_lookup_per_adv_bis(struct hci_dev *hdev,rcu_read_lock ();list_for_each_entry_rcu (c, &h->list, list) { -if (bacmp (&c->dst, ba) || c->type !=ISO_LINK || - !test_bit (HCI_CONN_PER_ADV , &c->flags)) +if (bacmp (&c->dst, ba) || c->type !=BIS_LINK || + !test_bit (HCI_CONN_PER_ADV , &c->flags))continue ; if (c->iso_qos.bcast.big == big &&
@@ -1234,7 +1237,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_cis(struct hci_dev *hdev,rcu_read_lock ();list_for_each_entry_rcu (c, &h->list, list) { -if (c->type !=ISO_LINK || !bacmp (&c->dst,BDADDR_ANY )) +if (c->type !=CIS_LINK )continue ;/* Match CIG ID if set */
@@ -1266,7 +1269,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_cig(struct hci_dev *hdev,rcu_read_lock ();list_for_each_entry_rcu (c, &h->list, list) { -if (c->type !=ISO_LINK || !bacmp (&c->dst,BDADDR_ANY )) +if (c->type !=CIS_LINK )continue ;if (handle == c->iso_qos.ucast.cig) {
@@ -1289,7 +1292,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_big(struct hci_dev *hdev,rcu_read_lock ();list_for_each_entry_rcu (c, &h->list, list) { -if (bacmp (&c->dst,BDADDR_ANY ) || c->type !=ISO_LINK ) +if (bacmp (&c->dst,BDADDR_ANY ) || c->type !=BIS_LINK )continue ;if (handle == c->iso_qos.bcast.big) {
@@ -1312,7 +1315,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_big_any_dst(struct hci_dev *rcu_read_lock ();list_for_each_entry_rcu (c, &h->list, list) { -if (c->type !=ISO_LINK ) +if (c->type !=BIS_LINK )continue ;if (handle !=BT_ISO_QOS_BIG_UNSET && handle == c->iso_qos.bcast.big) {
@@ -1335,8 +1338,8 @@ hci_conn_hash_lookup_big_state(struct hci_dev *hdev, __u8 handle, __u16 state)rcu_read_lock ();list_for_each_entry_rcu (c, &h->list, list) { -if (bacmp (&c->dst,BDADDR_ANY ) || c->type !=ISO_LINK || - c->state != state) +if (c->type !=BIS_LINK ||bacmp (&c->dst,BDADDR_ANY ) || + c->state != state)continue ;if (handle == c->iso_qos.bcast.big) {
@@ -1359,8 +1362,8 @@ hci_conn_hash_lookup_pa_sync_big_handle(struct hci_dev *hdev, __u8 big)rcu_read_lock ();list_for_each_entry_rcu (c, &h->list, list) { -if (c->type !=ISO_LINK || - !test_bit (HCI_CONN_PA_SYNC , &c->flags)) +if (c->type !=BIS_LINK || + !test_bit (HCI_CONN_PA_SYNC , &c->flags))continue ;if (c->iso_qos.bcast.big == big) {
@@ -1382,8 +1385,8 @@ hci_conn_hash_lookup_pa_sync_handle(struct hci_dev *hdev, __u16 sync_handle)rcu_read_lock ();list_for_each_entry_rcu (c, &h->list, list) { -if (c->type !=ISO_LINK || - !test_bit (HCI_CONN_PA_SYNC , &c->flags)) +if (c->type !=BIS_LINK || + !test_bit (HCI_CONN_PA_SYNC , &c->flags))continue ;if (c->sync_handle == sync_handle) {
@@ -1959,7 +1962,8 @@ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,case ESCO_LINK :return sco_connect_ind (hdev, bdaddr, flags); -case ISO_LINK : +case CIS_LINK : +case BIS_LINK :return iso_connect_ind (hdev, bdaddr, flags);default :
net/bluetooth/hci_conn.c +22 -15
@@ -870,9 +870,11 @@ static void cis_cleanup(struct hci_conn *conn)/* Check if ISO connection is a CIS and remove CIG if there are * no other connections using it. */ -hci_conn_hash_list_state (hdev, find_cis,ISO_LINK ,BT_BOUND , &d); -hci_conn_hash_list_state (hdev, find_cis,ISO_LINK ,BT_CONNECT , &d); -hci_conn_hash_list_state (hdev, find_cis,ISO_LINK ,BT_CONNECTED , &d); +hci_conn_hash_list_state (hdev, find_cis,CIS_LINK ,BT_BOUND , &d); +hci_conn_hash_list_state (hdev, find_cis,CIS_LINK ,BT_CONNECT , + &d); +hci_conn_hash_list_state (hdev, find_cis,CIS_LINK ,BT_CONNECTED , + &d);if (d.count)return ;
@@ -895,7 +897,8 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_tif (!hdev->acl_mtu)return ERR_PTR (-ECONNREFUSED );break ; -case ISO_LINK : +case CIS_LINK : +case BIS_LINK :if (hdev->iso_mtu)/* Dedicated ISO Buffer exists */ break ;
@@ -959,7 +962,8 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type, bdaddr_thci_copy_identity_address (hdev, &conn->src, &conn->src_type); conn->mtu = hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu;break ; -case ISO_LINK : +case CIS_LINK : +case BIS_LINK :/* conn->src should reflect the local identity address */ hci_copy_identity_address (hdev, &conn->src, &conn->src_type);
@@ -1055,7 +1059,8 @@ static void hci_conn_cleanup_child(struct hci_conn *conn, u8 reason)if (HCI_CONN_HANDLE_UNSET (conn->handle))hci_conn_failed (conn, reason);break ; -case ISO_LINK : +case CIS_LINK : +case BIS_LINK :if ((conn->state !=BT_CONNECTED && !test_bit (HCI_CONN_CREATE_CIS , &conn->flags)) ||test_bit (HCI_CONN_BIG_CREATED , &conn->flags))
@@ -1130,7 +1135,8 @@ void hci_conn_del(struct hci_conn *conn) hdev->acl_cnt += conn->sent; }else {/* Unacked ISO frames */ -if (conn->type ==ISO_LINK ) { +if (conn->type ==CIS_LINK || + conn->type ==BIS_LINK ) {if (hdev->iso_pkts) hdev->iso_cnt += conn->sent;else if (hdev->le_pkts)
@@ -1501,7 +1507,7 @@ static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst, memcmp(conn->le_per_adv_data, base, base_len)))return ERR_PTR (-EADDRINUSE ); - conn =hci_conn_add_unset (hdev,ISO_LINK , dst,HCI_ROLE_MASTER ); + conn =hci_conn_add_unset (hdev,BIS_LINK , dst,HCI_ROLE_MASTER );if (IS_ERR (conn))return conn;
@@ -1707,7 +1713,7 @@ static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos) data.count =0 ;/* Create a BIS for each bound connection */ -hci_conn_hash_list_state (hdev, bis_list,ISO_LINK , +hci_conn_hash_list_state (hdev, bis_list,BIS_LINK ,BT_BOUND , &data); cp.handle = qos->bcast.big;
@@ -1803,12 +1809,12 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)for (data.cig =0x00 ; data.cig <0xf0 ; data.cig++) { data.count =0 ; -hci_conn_hash_list_state (hdev, find_cis,ISO_LINK , +hci_conn_hash_list_state (hdev, find_cis,CIS_LINK ,BT_CONNECT , &data);if (data.count)continue ; -hci_conn_hash_list_state (hdev, find_cis,ISO_LINK , +hci_conn_hash_list_state (hdev, find_cis,CIS_LINK ,BT_CONNECTED , &data);if (!data.count)break ;
@@ -1860,7 +1866,8 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst, cis =hci_conn_hash_lookup_cis (hdev, dst, dst_type, qos->ucast.cig, qos->ucast.cis);if (!cis) { - cis =hci_conn_add_unset (hdev,ISO_LINK , dst,HCI_ROLE_MASTER ); + cis =hci_conn_add_unset (hdev,CIS_LINK , dst, +HCI_ROLE_MASTER );if (IS_ERR (cis))return cis; cis->cleanup = cis_cleanup;
@@ -1950,7 +1957,7 @@ bool hci_iso_setup_path(struct hci_conn *conn)int hci_conn_check_create_cis (struct hci_conn *conn) { -if (conn->type !=ISO_LINK || !bacmp (&conn->dst,BDADDR_ANY )) +if (conn->type !=CIS_LINK )return -EINVAL ; if (!conn->parent || conn->parent->state != BT_CONNECTED ||
@@ -2248,7 +2255,7 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,* the start periodic advertising and create BIG commands have * been queued */ -hci_conn_hash_list_state (hdev, bis_mark_per_adv,ISO_LINK , +hci_conn_hash_list_state (hdev, bis_mark_per_adv,BIS_LINK ,BT_BOUND , &data);/* Queue start periodic advertising and create BIG */
@@ -2910,7 +2917,7 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)* hci_connect_le serializes the connection attempts so only one * connection can be in BT_CONNECT at time. */ -if (conn->state ==BT_CONNECT && hdev->req_status ==HCI_REQ_PEND ) { +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 :
net/bluetooth/hci_core.c +13 -8
@@ -2907,12 +2907,13 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)break ;case HCI_ACLDATA_PKT :/* Detect if ISO packet has been sent as ACL */ -if (hci_conn_num (hdev,ISO_LINK )) { +if (hci_conn_num (hdev,CIS_LINK ) || +hci_conn_num (hdev,BIS_LINK )) {__u16 handle =__le16_to_cpu (hci_acl_hdr (skb)->handle);__u8 type; type =hci_conn_lookup_type (hdev,hci_handle (handle)); -if (type ==ISO_LINK ) +if (type ==CIS_LINK || type ==BIS_LINK )hci_skb_pkt_type (skb) =HCI_ISODATA_PKT ; }break ;
@@ -3347,7 +3348,8 @@ static inline void hci_quote_sent(struct hci_conn *conn, int num, int *quote)case LE_LINK : cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;break ; -case ISO_LINK : +case CIS_LINK : +case BIS_LINK : cnt = hdev->iso_mtu ? hdev->iso_cnt : hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;break ;
@@ -3361,7 +3363,7 @@ static inline void hci_quote_sent(struct hci_conn *conn, int num, int *quote) }static struct hci_conn *hci_low_sent (struct hci_dev *hdev,__u8 type, -int *quote) +__u8 type2,int *quote) {struct hci_conn_hash *h = &hdev->conn_hash;struct hci_conn *conn =NULL , *c;
@@ -3373,7 +3375,8 @@ static struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type,rcu_read_lock ();list_for_each_entry_rcu (c, &h->list, list) { -if (c->type != type ||skb_queue_empty (&c->data_q)) +if ((c->type != type && c->type != type2) || +skb_queue_empty (&c->data_q))continue ;if (c->state !=BT_CONNECTED && c->state !=BT_CONFIG )
@@ -3576,7 +3579,7 @@ static void hci_sched_sco(struct hci_dev *hdev, __u8 type)else cnt = &hdev->sco_cnt; -while (*cnt && (conn =hci_low_sent (hdev, type, "e))) { +while (*cnt && (conn =hci_low_sent (hdev, type, type, "e))) {while (quote-- && (skb =skb_dequeue (&conn->data_q))) {BT_DBG ("skb %p len %d" , skb, skb->len);hci_send_frame (hdev, skb);
@@ -3704,12 +3707,14 @@ static void hci_sched_iso(struct hci_dev *hdev)BT_DBG ("%s" , hdev->name); -if (!hci_conn_num (hdev,ISO_LINK )) +if (!hci_conn_num (hdev,CIS_LINK ) && + !hci_conn_num (hdev,BIS_LINK ))return ; cnt = hdev->iso_pkts ? &hdev->iso_cnt : hdev->le_pkts ? &hdev->le_cnt : &hdev->acl_cnt; -while (*cnt && (conn =hci_low_sent (hdev,ISO_LINK , "e))) { +while (*cnt && (conn =hci_low_sent (hdev,CIS_LINK ,BIS_LINK , + "e))) {while (quote-- && (skb =skb_dequeue (&conn->data_q))) {BT_DBG ("skb %p len %d" , skb, skb->len);hci_send_frame (hdev, skb);
net/bluetooth/hci_event.c +13 -12
@@ -3776,7 +3776,7 @@ static void hci_unbound_cis_failed(struct hci_dev *hdev, u8 cig, u8 status)lockdep_assert_held (&hdev->lock);list_for_each_entry_safe (conn, tmp, &hdev->conn_hash.list, list) { -if (conn->type !=ISO_LINK || !bacmp (&conn->dst,BDADDR_ANY ) || +if (conn->type !=CIS_LINK || conn->state ==BT_OPEN || conn->iso_qos.ucast.cig != cig)continue ;
@@ -4452,7 +4452,8 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, void *data,break ; -case ISO_LINK : +case CIS_LINK : +case BIS_LINK :if (hdev->iso_pkts) { hdev->iso_cnt += count;if (hdev->iso_cnt > hdev->iso_pkts)
@@ -6384,7 +6385,7 @@ static void hci_le_pa_sync_estabilished_evt(struct hci_dev *hdev, void *data,hci_dev_clear_flag (hdev,HCI_PA_SYNC ); - mask |=hci_proto_connect_ind (hdev, &ev->bdaddr,ISO_LINK , &flags); + mask |=hci_proto_connect_ind (hdev, &ev->bdaddr,BIS_LINK , &flags);if (!(mask &HCI_LM_ACCEPT )) {hci_le_pa_term_sync (hdev, ev->handle); goto unlock;
@@ -6395,7 +6396,7 @@ static void hci_le_pa_sync_estabilished_evt(struct hci_dev *hdev, void *data,if (ev->status) {/* Add connection to indicate the failed PA sync event */ - pa_sync =hci_conn_add_unset (hdev,ISO_LINK ,BDADDR_ANY , + pa_sync =hci_conn_add_unset (hdev,BIS_LINK ,BDADDR_ANY ,HCI_ROLE_SLAVE );if (!pa_sync)
@@ -6422,7 +6423,7 @@ static void hci_le_per_adv_report_evt(struct hci_dev *hdev, void *data,hci_dev_lock (hdev); - mask |=hci_proto_connect_ind (hdev,BDADDR_ANY ,ISO_LINK , &flags); + mask |=hci_proto_connect_ind (hdev,BDADDR_ANY ,BIS_LINK , &flags);if (!(mask &HCI_LM_ACCEPT ))hci_le_pa_term_sync (hdev, ev->sync_handle);
@@ -6693,7 +6694,7 @@ static void hci_le_cis_estabilished_evt(struct hci_dev *hdev, void *data, goto unlock; } -if (conn->type !=ISO_LINK ) { +if (conn->type !=CIS_LINK ) {bt_dev_err (hdev,"Invalid connection link type handle 0x%4.4x" , handle);
@@ -6811,7 +6812,7 @@ static void hci_le_cis_req_evt(struct hci_dev *hdev, void *data,if (!acl) goto unlock; - mask =hci_proto_connect_ind (hdev, &acl->dst,ISO_LINK , &flags); + mask =hci_proto_connect_ind (hdev, &acl->dst,CIS_LINK , &flags);if (!(mask &HCI_LM_ACCEPT )) {hci_le_reject_cis (hdev, ev->cis_handle); goto unlock;
@@ -6819,8 +6820,8 @@ static void hci_le_cis_req_evt(struct hci_dev *hdev, void *data, cis =hci_conn_hash_lookup_handle (hdev, cis_handle);if (!cis) { - cis =hci_conn_add (hdev,ISO_LINK , &acl->dst,HCI_ROLE_SLAVE , - cis_handle); + cis =hci_conn_add (hdev,CIS_LINK , &acl->dst, +HCI_ROLE_SLAVE , cis_handle);if (IS_ERR (cis)) {hci_le_reject_cis (hdev, ev->cis_handle); goto unlock;
@@ -6954,7 +6955,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,bt_dev_dbg (hdev,"ignore too large handle %u" , handle);continue ; } - bis =hci_conn_add (hdev,ISO_LINK ,BDADDR_ANY , + bis =hci_conn_add (hdev,BIS_LINK ,BDADDR_ANY ,HCI_ROLE_SLAVE , handle);if (IS_ERR (bis))continue ;
@@ -7011,7 +7012,7 @@ static void hci_le_big_info_adv_report_evt(struct hci_dev *hdev, void *data,hci_dev_lock (hdev); - mask |=hci_proto_connect_ind (hdev,BDADDR_ANY ,ISO_LINK , &flags); + mask |=hci_proto_connect_ind (hdev,BDADDR_ANY ,BIS_LINK , &flags);if (!(mask &HCI_LM_ACCEPT )) {hci_le_pa_term_sync (hdev, ev->sync_handle); goto unlock;
@@ -7028,7 +7029,7 @@ static void hci_le_big_info_adv_report_evt(struct hci_dev *hdev, void *data, goto unlock;/* Add connection to indicate the PA sync event */ - pa_sync =hci_conn_add_unset (hdev,ISO_LINK ,BDADDR_ANY , + pa_sync =hci_conn_add_unset (hdev,BIS_LINK ,BDADDR_ANY ,HCI_ROLE_SLAVE );if (IS_ERR (pa_sync))
net/bluetooth/hci_sync.c +9 -5
@@ -2971,7 +2971,7 @@ static int hci_le_set_ext_scan_param_sync(struct hci_dev *hdev, u8 type,if (sent) {struct hci_conn *conn; - conn =hci_conn_hash_lookup_ba (hdev,ISO_LINK , + conn =hci_conn_hash_lookup_ba (hdev,BIS_LINK , &sent->bdaddr);if (conn) {struct bt_iso_qos *qos = &conn->iso_qos;
@@ -5582,7 +5582,7 @@ static int hci_connect_cancel_sync(struct hci_dev *hdev, struct hci_conn *conn,if (conn->type ==LE_LINK )return hci_le_connect_cancel_sync (hdev, conn, reason); -if (conn->type ==ISO_LINK ) { +if (conn->type ==CIS_LINK ) {/* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E * page 1857: *
@@ -5595,9 +5595,10 @@ static int hci_connect_cancel_sync(struct hci_dev *hdev, struct hci_conn *conn,return hci_disconnect_sync (hdev, conn, reason);/* CIS with no Create CIS sent have nothing to cancel */ -if (bacmp (&conn->dst,BDADDR_ANY )) -return HCI_ERROR_LOCAL_HOST_TERM ; +return HCI_ERROR_LOCAL_HOST_TERM ; + } +if (conn->type ==BIS_LINK ) {/* There is no way to cancel a BIS without terminating the BIG * which is done later on connection cleanup. */
@@ -5659,9 +5660,12 @@ static int hci_reject_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, {struct hci_cp_reject_conn_req cp; -if (conn->type ==ISO_LINK ) +if (conn->type ==CIS_LINK )return hci_le_reject_cis_sync (hdev, conn, reason); +if (conn->type ==BIS_LINK ) +return -EINVAL ; +if (conn->type ==SCO_LINK || conn->type ==ESCO_LINK )return hci_reject_sco_sync (hdev, conn, reason);
net/bluetooth/iso.c +2 -2
@@ -1955,7 +1955,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)static void iso_connect_cfm (struct hci_conn *hcon,__u8 status) { -if (hcon->type !=ISO_LINK ) { +if (hcon->type !=CIS_LINK && hcon->type !=BIS_LINK ) {if (hcon->type !=LE_LINK )return ;
@@ -1996,7 +1996,7 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)static void iso_disconn_cfm (struct hci_conn *hcon,__u8 reason) { -if (hcon->type !=ISO_LINK ) +if (hcon->type !=CIS_LINK && hcon->type !=BIS_LINK )return ;BT_DBG ("hcon %p reason %d" , hcon, reason);
net/bluetooth/mgmt.c +2 -1
@@ -3233,7 +3233,8 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,static u8 link_to_bdaddr (u8 link_type,u8 addr_type) {switch (link_type) { -case ISO_LINK : +case CIS_LINK : +case BIS_LINK :case LE_LINK :switch (addr_type) {case ADDR_LE_DEV_PUBLIC :
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/4 Bluetooth: hci_sync: annotate data-races around hdev->req_status
In reply to Sasha LevinFrom: Cen Zhang <[email protected]>
[ Upstream commit b6807cfc195ef99e1ac37b2e1e60df40295daa8c ]
__hci_cmd_sync_sk() sets hdev->req_status under hdev->req_lock:
hdev->req_status = HCI_REQ_PEND;
However, several other functions read or write hdev->req_status without holding any lock:
- hci_send_cmd_sync() reads req_status in hci_cmd_work (workqueue)
- hci_cmd_sync_complete() reads/writes from HCI event completion
- hci_cmd_sync_cancel() / hci_cmd_sync_cancel_sync() read/write
- hci_abort_conn() reads in connection abort path
Since __hci_cmd_sync_sk() runs on hdev->req_workqueue while hci_send_cmd_sync() runs on hdev->workqueue, these are different workqueues that can execute concurrently on different CPUs. The plain C accesses constitute a data race.
Add READ_ONCE()/WRITE_ONCE() annotations on all concurrent accesses to hdev->req_status to prevent potential compiler optimizations that could affect correctness (e.g., load fusing in the wait_event condition or store reordering).
Stable-dep-of: 12917f591cea ("Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()")
--- net/bluetooth/hci_core.c | 2 +- net/bluetooth/hci_sync.c | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-)
net/bluetooth/hci_core.c +1 -1
@@ -4060,7 +4060,7 @@ static void hci_send_cmd_sync(struct hci_dev *hdev, struct sk_buff *skb)return ; } -if (hci_req_status_pend (hdev) && +if (READ_ONCE (hdev->req_status) ==HCI_REQ_PEND && !hci_dev_test_and_set_flag (hdev,HCI_CMD_PENDING )) {kfree_skb (hdev->req_skb); hdev->req_skb =skb_clone (hdev->sent_cmd,GFP_KERNEL );
net/bluetooth/hci_sync.c +10 -10
@@ -25,11 +25,11 @@ static void hci_cmd_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode, {bt_dev_dbg (hdev,"result 0x%2.2x" , result); -if (hdev->req_status !=HCI_REQ_PEND ) +if (READ_ONCE (hdev->req_status) !=HCI_REQ_PEND )return ; hdev->req_result = result; - hdev->req_status =HCI_REQ_DONE ; +WRITE_ONCE (hdev->req_status,HCI_REQ_DONE );/* Free the request command so it is not used as response */ kfree_skb (hdev->req_skb);
@@ -168,20 +168,20 @@ struct sk_buff *__hci_cmd_sync_sk(struct hci_dev *hdev, u16 opcode, u32 plen,hci_cmd_sync_add (&req, opcode, plen, param, event, sk); - hdev->req_status =HCI_REQ_PEND ; +WRITE_ONCE (hdev->req_status,HCI_REQ_PEND ); err =hci_req_sync_run (&req);if (err <0 )return ERR_PTR (err); err =wait_event_interruptible_timeout (hdev->req_wait_q, - hdev->req_status !=HCI_REQ_PEND , +READ_ONCE (hdev->req_status) !=HCI_REQ_PEND , timeout);if (err == -ERESTARTSYS )return ERR_PTR (-EINTR ); -switch (hdev->req_status) { +switch (READ_ONCE (hdev->req_status)) {case HCI_REQ_DONE : err = -bt_to_errno (hdev->req_result);break ;
@@ -195,7 +195,7 @@ struct sk_buff *__hci_cmd_sync_sk(struct hci_dev *hdev, u16 opcode, u32 plen,break ; } - hdev->req_status =0 ; +WRITE_ONCE (hdev->req_status,0 ); hdev->req_result =0 ; skb = hdev->req_rsp; hdev->req_rsp =NULL ;
@@ -689,9 +689,9 @@ void hci_cmd_sync_cancel(struct hci_dev *hdev, int err) {bt_dev_dbg (hdev,"err 0x%2.2x" , err); -if (hdev->req_status ==HCI_REQ_PEND ) { +if (READ_ONCE (hdev->req_status) ==HCI_REQ_PEND ) { hdev->req_result = err; - hdev->req_status =HCI_REQ_CANCELED ; +WRITE_ONCE (hdev->req_status,HCI_REQ_CANCELED );queue_work (hdev->workqueue, &hdev->cmd_sync_cancel_work); }
@@ -707,12 +707,12 @@ void hci_cmd_sync_cancel_sync(struct hci_dev *hdev, int err) {bt_dev_dbg (hdev,"err 0x%2.2x" , err); -if (hdev->req_status ==HCI_REQ_PEND ) { +if (READ_ONCE (hdev->req_status) ==HCI_REQ_PEND ) {/* req_result is __u32 so error must be positive to be properly * propagated. */ hdev->req_result = err <0 ? -err : err; - hdev->req_status =HCI_REQ_CANCELED ; +WRITE_ONCE (hdev->req_status,HCI_REQ_CANCELED );wake_up_interruptible (&hdev->req_wait_q); }
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/4 Bluetooth: hci_conn: Fix null ptr deref in hci_abort_conn()
In reply to Sasha LevinFrom: Siwei Zhang <[email protected]>
[ Upstream commit 12917f591cea1af36087dba5b9ec888652f0b42a ]
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.
--- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_conn.c | 21 +---- net/bluetooth/hci_sync.c | 133 +++++++++++++++++++++++++++---- 3 files changed, 123 insertions(+), 32 deletions(-)
include/net/bluetooth/hci_core.h +1 -0
@@ -957,6 +957,7 @@ enum {HCI_CONN_AUTH_FAILURE ,HCI_CONN_PER_ADV ,HCI_CONN_BIG_CREATED , +HCI_CONN_CREATE ,HCI_CONN_CREATE_CIS ,HCI_CONN_BIG_SYNC , HCI_CONN_BIG_SYNC_FAILED,
net/bluetooth/hci_conn.c +3 -18
@@ -2910,26 +2910,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
net/bluetooth/hci_sync.c +119 -14
178 lines not on this page · raw message
Signature · 1 line
2.53.0