bluetooth pull request for net:

- purge error queues in socket destructors
  - hci_sync: Fix CIS host feature condition
  - L2CAP: Fix invalid response to L2CAP_ECRED_RECONF_REQ
  - L2CAP: Fix result of L2CAP_ECRED_CONN_RSP when MTU is too short
  - L2CAP: Fix response to L2CAP_ECRED_CONN_REQ
  - L2CAP: Fix not checking output MTU is acceptable on L2CAP_ECRED_CONN_REQ
  - L2CAP: Fix missing key size check for L2CAP_LE_CONN_REQ
  - hci_qca: Cleanup on all setup failures
 -----BEGIN PGP SIGNATURE-----
 
 iQJNBAABCgA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmmcw1EZHGx1aXoudm9u
 LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKUTyD/4jtQwDrveC19zamF5n7lFY
 Oils6eftANcLFzLwTrMqGO7IxESga4qdNOf2vc/UgVSUfNqsPIUJ5El+LzpXZXAa
 sYBP/KudEX53CfU3fEVyPTUaWkZ4CdMRZeiCmgXqW7GxYbGw92SFuaSIHAP6Ep4s
 Z7Ryd1H0xhX9QPMc4g4IgoMiBiKzNs4GtlLSbDJcivAtbC/34nkMOxK9g+1DbU0F
 qzW+oPfYCpPzXTf20I1QIAMt5smnSM3Tuvo9u2pZRuEGpKjENxeY4hdAejfjeKA6
 RLWXm6JvMP2lUBT68plMQQdYyQ8DxG75sVjgSoQYIu2YTVnsX76t/kD2hhiHXH/Y
 nQoy4dtA1/5V7Ka0cfMhcvino4Rb9Gh3dsFKJOuWRT+aTY+gNhpyr56SuJh24Y3C
 7tUeEDI4fBkJGaRAbreVbaI5vw4kbSfi7IDOM/ccWDSLaG8HGaLOtn0IU8q4AgMa
 IkYzB5zwtiyM/zaSTO1k0HkpjR0wwftnTd+Fj2mUWdTwSeek64R9enmKYmg5UJrv
 14yhfLHFsbAQo+o1B3ZslnCdYQJpgFmyAInV6Jpunc78IE9+g/YA55K22JbDDSzI
 t9Zy25OWLyYZyuD1PzDkMlYU5OARNYeyRXbJ3w037LrpqRoEuFsK0qTmgi+kR9C7
 VR9IpCqgf4SJbL7ge83H8g==
 =JBaa
 -----END PGP SIGNATURE-----

Merge tag 'for-net-2026-02-23' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth

Luiz Augusto von Dentz says:

====================
bluetooth pull request for net:

 - purge error queues in socket destructors
 - hci_sync: Fix CIS host feature condition
 - L2CAP: Fix invalid response to L2CAP_ECRED_RECONF_REQ
 - L2CAP: Fix result of L2CAP_ECRED_CONN_RSP when MTU is too short
 - L2CAP: Fix response to L2CAP_ECRED_CONN_REQ
 - L2CAP: Fix not checking output MTU is acceptable on L2CAP_ECRED_CONN_REQ
 - L2CAP: Fix missing key size check for L2CAP_LE_CONN_REQ
 - hci_qca: Cleanup on all setup failures

* tag 'for-net-2026-02-23' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth:
  Bluetooth: L2CAP: Fix missing key size check for L2CAP_LE_CONN_REQ
  Bluetooth: L2CAP: Fix not checking output MTU is acceptable on L2CAP_ECRED_CONN_REQ
  Bluetooth: Fix CIS host feature condition
  Bluetooth: L2CAP: Fix response to L2CAP_ECRED_CONN_REQ
  Bluetooth: hci_qca: Cleanup on all setup failures
  Bluetooth: purge error queues in socket destructors
  Bluetooth: L2CAP: Fix result of L2CAP_ECRED_CONN_RSP when MTU is too short
  Bluetooth: L2CAP: Fix invalid response to L2CAP_ECRED_RECONF_REQ
====================

Link: https://patch.msgid.link/20260223211634.3800315-1-luiz.dentz@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2026-02-24 15:03:07 +01:00
commit 1348659dc9
8 changed files with 109 additions and 47 deletions

View File

@ -2046,19 +2046,23 @@ retry:
}
out:
if (ret && retries < MAX_INIT_RETRIES) {
bt_dev_warn(hdev, "Retry BT power ON:%d", retries);
if (ret) {
qca_power_shutdown(hu);
if (hu->serdev) {
serdev_device_close(hu->serdev);
ret = serdev_device_open(hu->serdev);
if (ret) {
bt_dev_err(hdev, "failed to open port");
return ret;
if (retries < MAX_INIT_RETRIES) {
bt_dev_warn(hdev, "Retry BT power ON:%d", retries);
if (hu->serdev) {
serdev_device_close(hu->serdev);
ret = serdev_device_open(hu->serdev);
if (ret) {
bt_dev_err(hdev, "failed to open port");
return ret;
}
}
retries++;
goto retry;
}
retries++;
goto retry;
return ret;
}
/* Setup bdaddr */

View File

@ -284,9 +284,9 @@ struct l2cap_conn_rsp {
#define L2CAP_CR_LE_BAD_KEY_SIZE 0x0007
#define L2CAP_CR_LE_ENCRYPTION 0x0008
#define L2CAP_CR_LE_INVALID_SCID 0x0009
#define L2CAP_CR_LE_SCID_IN_USE 0X000A
#define L2CAP_CR_LE_UNACCEPT_PARAMS 0X000B
#define L2CAP_CR_LE_INVALID_PARAMS 0X000C
#define L2CAP_CR_LE_SCID_IN_USE 0x000A
#define L2CAP_CR_LE_UNACCEPT_PARAMS 0x000B
#define L2CAP_CR_LE_INVALID_PARAMS 0x000C
/* connect/create channel status */
#define L2CAP_CS_NO_INFO 0x0000
@ -493,6 +493,8 @@ struct l2cap_ecred_reconf_req {
#define L2CAP_RECONF_SUCCESS 0x0000
#define L2CAP_RECONF_INVALID_MTU 0x0001
#define L2CAP_RECONF_INVALID_MPS 0x0002
#define L2CAP_RECONF_INVALID_CID 0x0003
#define L2CAP_RECONF_INVALID_PARAMS 0x0004
struct l2cap_ecred_reconf_rsp {
__le16 result;

View File

@ -2166,6 +2166,7 @@ static void hci_sock_destruct(struct sock *sk)
mgmt_cleanup(sk);
skb_queue_purge(&sk->sk_receive_queue);
skb_queue_purge(&sk->sk_write_queue);
skb_queue_purge(&sk->sk_error_queue);
}
static const struct proto_ops hci_sock_ops = {

View File

@ -4592,7 +4592,7 @@ static int hci_le_set_host_features_sync(struct hci_dev *hdev)
{
int err;
if (iso_capable(hdev)) {
if (cis_capable(hdev)) {
/* Connected Isochronous Channels (Host Support) */
err = hci_le_set_host_feature_sync(hdev, 32,
(iso_enabled(hdev) ? 0x01 :

View File

@ -746,6 +746,7 @@ static void iso_sock_destruct(struct sock *sk)
skb_queue_purge(&sk->sk_receive_queue);
skb_queue_purge(&sk->sk_write_queue);
skb_queue_purge(&sk->sk_error_queue);
}
static void iso_sock_cleanup_listen(struct sock *parent)

View File

@ -4916,6 +4916,13 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
goto response_unlock;
}
/* Check if Key Size is sufficient for the security level */
if (!l2cap_check_enc_key_size(conn->hcon, pchan)) {
result = L2CAP_CR_LE_BAD_KEY_SIZE;
chan = NULL;
goto response_unlock;
}
/* Check for valid dynamic CID range */
if (scid < L2CAP_CID_DYN_START || scid > L2CAP_CID_LE_DYN_END) {
result = L2CAP_CR_LE_INVALID_SCID;
@ -5051,13 +5058,15 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
struct l2cap_chan *chan, *pchan;
u16 mtu, mps;
__le16 psm;
u8 result, len = 0;
u8 result, rsp_len = 0;
int i, num_scid;
bool defer = false;
if (!enable_ecred)
return -EINVAL;
memset(pdu, 0, sizeof(*pdu));
if (cmd_len < sizeof(*req) || (cmd_len - sizeof(*req)) % sizeof(u16)) {
result = L2CAP_CR_LE_INVALID_PARAMS;
goto response;
@ -5066,6 +5075,9 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
cmd_len -= sizeof(*req);
num_scid = cmd_len / sizeof(u16);
/* Always respond with the same number of scids as in the request */
rsp_len = cmd_len;
if (num_scid > L2CAP_ECRED_MAX_CID) {
result = L2CAP_CR_LE_INVALID_PARAMS;
goto response;
@ -5075,7 +5087,7 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
mps = __le16_to_cpu(req->mps);
if (mtu < L2CAP_ECRED_MIN_MTU || mps < L2CAP_ECRED_MIN_MPS) {
result = L2CAP_CR_LE_UNACCEPT_PARAMS;
result = L2CAP_CR_LE_INVALID_PARAMS;
goto response;
}
@ -5095,8 +5107,6 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
BT_DBG("psm 0x%2.2x mtu %u mps %u", __le16_to_cpu(psm), mtu, mps);
memset(pdu, 0, sizeof(*pdu));
/* Check if we have socket listening on psm */
pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src,
&conn->hcon->dst, LE_LINK);
@ -5109,7 +5119,16 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
if (!smp_sufficient_security(conn->hcon, pchan->sec_level,
SMP_ALLOW_STK)) {
result = L2CAP_CR_LE_AUTHENTICATION;
result = pchan->sec_level == BT_SECURITY_MEDIUM ?
L2CAP_CR_LE_ENCRYPTION : L2CAP_CR_LE_AUTHENTICATION;
goto unlock;
}
/* Check if the listening channel has set an output MTU then the
* requested MTU shall be less than or equal to that value.
*/
if (pchan->omtu && mtu < pchan->omtu) {
result = L2CAP_CR_LE_UNACCEPT_PARAMS;
goto unlock;
}
@ -5121,7 +5140,6 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
BT_DBG("scid[%d] 0x%4.4x", i, scid);
pdu->dcid[i] = 0x0000;
len += sizeof(*pdu->dcid);
/* Check for valid dynamic CID range */
if (scid < L2CAP_CID_DYN_START || scid > L2CAP_CID_LE_DYN_END) {
@ -5188,7 +5206,7 @@ response:
return 0;
l2cap_send_cmd(conn, cmd->ident, L2CAP_ECRED_CONN_RSP,
sizeof(*pdu) + len, pdu);
sizeof(*pdu) + rsp_len, pdu);
return 0;
}
@ -5310,14 +5328,14 @@ static inline int l2cap_ecred_reconf_req(struct l2cap_conn *conn,
struct l2cap_ecred_reconf_req *req = (void *) data;
struct l2cap_ecred_reconf_rsp rsp;
u16 mtu, mps, result;
struct l2cap_chan *chan;
struct l2cap_chan *chan[L2CAP_ECRED_MAX_CID] = {};
int i, num_scid;
if (!enable_ecred)
return -EINVAL;
if (cmd_len < sizeof(*req) || cmd_len - sizeof(*req) % sizeof(u16)) {
result = L2CAP_CR_LE_INVALID_PARAMS;
if (cmd_len < sizeof(*req) || (cmd_len - sizeof(*req)) % sizeof(u16)) {
result = L2CAP_RECONF_INVALID_CID;
goto respond;
}
@ -5327,42 +5345,69 @@ static inline int l2cap_ecred_reconf_req(struct l2cap_conn *conn,
BT_DBG("mtu %u mps %u", mtu, mps);
if (mtu < L2CAP_ECRED_MIN_MTU) {
result = L2CAP_RECONF_INVALID_MTU;
result = L2CAP_RECONF_INVALID_PARAMS;
goto respond;
}
if (mps < L2CAP_ECRED_MIN_MPS) {
result = L2CAP_RECONF_INVALID_MPS;
result = L2CAP_RECONF_INVALID_PARAMS;
goto respond;
}
cmd_len -= sizeof(*req);
num_scid = cmd_len / sizeof(u16);
if (num_scid > L2CAP_ECRED_MAX_CID) {
result = L2CAP_RECONF_INVALID_PARAMS;
goto respond;
}
result = L2CAP_RECONF_SUCCESS;
/* Check if each SCID, MTU and MPS are valid */
for (i = 0; i < num_scid; i++) {
u16 scid;
scid = __le16_to_cpu(req->scid[i]);
if (!scid)
return -EPROTO;
chan = __l2cap_get_chan_by_dcid(conn, scid);
if (!chan)
continue;
/* If the MTU value is decreased for any of the included
* channels, then the receiver shall disconnect all
* included channels.
*/
if (chan->omtu > mtu) {
BT_ERR("chan %p decreased MTU %u -> %u", chan,
chan->omtu, mtu);
result = L2CAP_RECONF_INVALID_MTU;
if (!scid) {
result = L2CAP_RECONF_INVALID_CID;
goto respond;
}
chan->omtu = mtu;
chan->remote_mps = mps;
chan[i] = __l2cap_get_chan_by_dcid(conn, scid);
if (!chan[i]) {
result = L2CAP_RECONF_INVALID_CID;
goto respond;
}
/* The MTU field shall be greater than or equal to the greatest
* current MTU size of these channels.
*/
if (chan[i]->omtu > mtu) {
BT_ERR("chan %p decreased MTU %u -> %u", chan[i],
chan[i]->omtu, mtu);
result = L2CAP_RECONF_INVALID_MTU;
goto respond;
}
/* If more than one channel is being configured, the MPS field
* shall be greater than or equal to the current MPS size of
* each of these channels. If only one channel is being
* configured, the MPS field may be less than the current MPS
* of that channel.
*/
if (chan[i]->remote_mps >= mps && i) {
BT_ERR("chan %p decreased MPS %u -> %u", chan[i],
chan[i]->remote_mps, mps);
result = L2CAP_RECONF_INVALID_MPS;
goto respond;
}
}
/* Commit the new MTU and MPS values after checking they are valid */
for (i = 0; i < num_scid; i++) {
chan[i]->omtu = mtu;
chan[i]->remote_mps = mps;
}
respond:

View File

@ -1029,10 +1029,17 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
break;
}
/* Setting is not supported as it's the remote side that
* decides this.
*/
err = -EPERM;
/* Only allow setting output MTU when not connected */
if (sk->sk_state == BT_CONNECTED) {
err = -EISCONN;
break;
}
err = copy_safe_from_sockptr(&mtu, sizeof(mtu), optval, optlen);
if (err)
break;
chan->omtu = mtu;
break;
case BT_RCVMTU:
@ -1817,6 +1824,7 @@ static void l2cap_sock_destruct(struct sock *sk)
skb_queue_purge(&sk->sk_receive_queue);
skb_queue_purge(&sk->sk_write_queue);
skb_queue_purge(&sk->sk_error_queue);
}
static void l2cap_skb_msg_name(struct sk_buff *skb, void *msg_name,

View File

@ -470,6 +470,7 @@ static void sco_sock_destruct(struct sock *sk)
skb_queue_purge(&sk->sk_receive_queue);
skb_queue_purge(&sk->sk_write_queue);
skb_queue_purge(&sk->sk_error_queue);
}
static void sco_sock_cleanup_listen(struct sock *parent)