bluetooth pull request for net:

- hci_ll: Fix firmware leak on error path
  - hci_sync: annotate data-races around hdev->req_status
  - L2CAP: Fix null-ptr-deref on l2cap_sock_ready_cb
  - L2CAP: Validate PDU length before reading SDU length in l2cap_ecred_data_rcv()
  - L2CAP: Fix regressions caused by reusing ident
  - L2CAP: Fix stack-out-of-bounds read in l2cap_ecred_conn_req
  - MGMT: Fix dangling pointer on mgmt_add_adv_patterns_monitor_complete
  - SCO: Fix use-after-free in sco_recv_frame() due to missing sock_hold
 -----BEGIN PGP SIGNATURE-----
 
 iQJNBAABCgA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmm8SHgZHGx1aXoudm9u
 LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKblnD/9BEGzP5EGkrV8b7xoWzAhl
 MBXnlHm4euetPx89B3xkQ7lN9wOA4jAPkwzL2OlLw87CSi3+VkgklH/6H3LyK5hf
 SNfcuEyTE/Y2zgPeBeUQ8V9WgZFrIvgiqcyAInTD0GLrN7h5sG7ABJupgHnwTeZa
 mV7dkHbznIRY4n9hpmZQx6WQi2EY/KpRKh3a4RvMsS5fTTH29EacYxAgsRMZmrMA
 vxcFbLLNOlE4Oj4Pxttrddx4C1ewFuY61FQ7dH9n/crmyN/p7yiweGfCdXaLGNjn
 CYMQJg1IUv0L4yxYYamInJ2eWBA4A2Ml3geeULPSO0bciCSzEvLMYXVKOkRgIki0
 l82uol3KHrf6L5cYZmMhB2AIjKiXcEDmpM+eRf/O9QjXSAq3xNdBkGDFSTZgScox
 BlugMSI9eehB4frfmuFFZlhVNz5z7KDbbQYskwi5uucYPJKswKi2k+2mhvbu3fCV
 I35xDz7JPvU++yfJ87haxRSSRM8yC6FfqUr7jZKAPrZFuT/9eWNMZXTDRFX0ps6a
 gvRyI7muCA1RF1fLY+GUANpaLt9Ws/EAjMGX5hqPL6r2TbPHvmX5JxehqViE1+mp
 aqSAZxfR/n18M4SdomBDSLcoAn2JReDX/jthOJ8Ahal3Hp43N12VA7WPk6fX2UUB
 Ad3wLkQN7dv6KAkzmCq3LQ==
 =FvaP
 -----END PGP SIGNATURE-----

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

Luiz Augusto von Dentz says:

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

 - hci_ll: Fix firmware leak on error path
 - hci_sync: annotate data-races around hdev->req_status
 - L2CAP: Fix null-ptr-deref on l2cap_sock_ready_cb
 - L2CAP: Validate PDU length before reading SDU length in l2cap_ecred_data_rcv()
 - L2CAP: Fix regressions caused by reusing ident
 - L2CAP: Fix stack-out-of-bounds read in l2cap_ecred_conn_req
 - MGMT: Fix dangling pointer on mgmt_add_adv_patterns_monitor_complete
 - SCO: Fix use-after-free in sco_recv_frame() due to missing sock_hold

* tag 'for-net-2026-03-19' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth:
  Bluetooth: L2CAP: Fix regressions caused by reusing ident
  Bluetooth: L2CAP: Fix null-ptr-deref on l2cap_sock_ready_cb
  Bluetooth: hci_ll: Fix firmware leak on error path
  Bluetooth: hci_sync: annotate data-races around hdev->req_status
  Bluetooth: MGMT: Fix dangling pointer on mgmt_add_adv_patterns_monitor_complete
  Bluetooth: SCO: Fix use-after-free in sco_recv_frame() due to missing sock_hold
  Bluetooth: L2CAP: Validate PDU length before reading SDU length in l2cap_ecred_data_rcv()
  Bluetooth: L2CAP: Fix stack-out-of-bounds read in l2cap_ecred_conn_req
====================

Link: https://patch.msgid.link/20260319190455.135302-1-luiz.dentz@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-03-19 16:49:38 -07:00
commit 57ce3b2e9c
9 changed files with 60 additions and 22 deletions

View File

@ -541,6 +541,8 @@ static int download_firmware(struct ll_device *lldev)
if (err || !fw->data || !fw->size) {
bt_dev_err(lldev->hu.hdev, "request_firmware failed(errno %d) for %s",
err, bts_scr_name);
if (!err)
release_firmware(fw);
return -EINVAL;
}
ptr = (void *)fw->data;

View File

@ -658,6 +658,7 @@ struct l2cap_conn {
struct sk_buff *rx_skb;
__u32 rx_len;
struct ida tx_ida;
__u8 tx_ident;
struct sk_buff_head pending_rx;
struct work_struct pending_rx_work;

View File

@ -3095,7 +3095,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:

View File

@ -4126,7 +4126,7 @@ static int hci_send_cmd_sync(struct hci_dev *hdev, struct sk_buff *skb)
kfree_skb(skb);
}
if (hdev->req_status == HCI_REQ_PEND &&
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);

View File

@ -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);
@ -167,20 +167,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;
@ -194,7 +194,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;
@ -665,9 +665,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);
}
@ -683,12 +683,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);
}

View File

@ -926,16 +926,39 @@ int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator)
static int l2cap_get_ident(struct l2cap_conn *conn)
{
u8 max;
int ident;
/* LE link does not support tools like l2ping so use the full range */
if (conn->hcon->type == LE_LINK)
return ida_alloc_range(&conn->tx_ida, 1, 255, GFP_ATOMIC);
max = 255;
/* Get next available identificator.
* 1 - 128 are used by kernel.
* 129 - 199 are reserved.
* 200 - 254 are used by utilities like l2ping, etc.
*/
return ida_alloc_range(&conn->tx_ida, 1, 128, GFP_ATOMIC);
else
max = 128;
/* Allocate ident using min as last used + 1 (cyclic) */
ident = ida_alloc_range(&conn->tx_ida, READ_ONCE(conn->tx_ident) + 1,
max, GFP_ATOMIC);
/* Force min 1 to start over */
if (ident <= 0) {
ident = ida_alloc_range(&conn->tx_ida, 1, max, GFP_ATOMIC);
if (ident <= 0) {
/* If all idents are in use, log an error, this is
* extremely unlikely to happen and would indicate a bug
* in the code that idents are not being freed properly.
*/
BT_ERR("Unable to allocate ident: %d", ident);
return 0;
}
}
WRITE_ONCE(conn->tx_ident, ident);
return ident;
}
static void l2cap_send_acl(struct l2cap_conn *conn, struct sk_buff *skb,
@ -5081,14 +5104,14 @@ 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;
}
/* Always respond with the same number of scids as in the request */
rsp_len = cmd_len;
mtu = __le16_to_cpu(req->mtu);
mps = __le16_to_cpu(req->mps);
@ -6690,6 +6713,11 @@ static int l2cap_ecred_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
if (!chan->sdu) {
u16 sdu_len;
if (!pskb_may_pull(skb, L2CAP_SDULEN_SIZE)) {
err = -EINVAL;
goto failed;
}
sdu_len = get_unaligned_le16(skb->data);
skb_pull(skb, L2CAP_SDULEN_SIZE);

View File

@ -1698,6 +1698,9 @@ static void l2cap_sock_ready_cb(struct l2cap_chan *chan)
struct sock *sk = chan->data;
struct sock *parent;
if (!sk)
return;
lock_sock(sk);
parent = bt_sk(sk)->parent;

View File

@ -5355,7 +5355,7 @@ static void mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev,
* hci_adv_monitors_clear is about to be called which will take care of
* freeing the adv_monitor instances.
*/
if (status == -ECANCELED && !mgmt_pending_valid(hdev, cmd))
if (status == -ECANCELED || !mgmt_pending_valid(hdev, cmd))
return;
monitor = cmd->user_data;

View File

@ -401,7 +401,7 @@ static void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb)
struct sock *sk;
sco_conn_lock(conn);
sk = conn->sk;
sk = sco_sock_hold(conn);
sco_conn_unlock(conn);
if (!sk)
@ -410,11 +410,15 @@ static void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb)
BT_DBG("sk %p len %u", sk, skb->len);
if (sk->sk_state != BT_CONNECTED)
goto drop;
goto drop_put;
if (!sock_queue_rcv_skb(sk, skb))
if (!sock_queue_rcv_skb(sk, skb)) {
sock_put(sk);
return;
}
drop_put:
sock_put(sk);
drop:
kfree_skb(skb);
}