summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2026-03-19 16:49:38 -0700
committerJakub Kicinski <kuba@kernel.org>2026-03-19 16:49:38 -0700
commit57ce3b2e9cdabcbc4d5a10f0e97a1399058c7417 (patch)
tree91375155b0354483e73e300429659ea73ebaed83
parentcbcb3cfcdc436d6f91a3d95ecfa9c831abe14aed (diff)
parent761fb8ec8778f0caf2bba5a41e3cff1ea86974f3 (diff)
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>
-rw-r--r--drivers/bluetooth/hci_ll.c2
-rw-r--r--include/net/bluetooth/l2cap.h1
-rw-r--r--net/bluetooth/hci_conn.c2
-rw-r--r--net/bluetooth/hci_core.c2
-rw-r--r--net/bluetooth/hci_sync.c20
-rw-r--r--net/bluetooth/l2cap_core.c40
-rw-r--r--net/bluetooth/l2cap_sock.c3
-rw-r--r--net/bluetooth/mgmt.c2
-rw-r--r--net/bluetooth/sco.c10
9 files changed, 60 insertions, 22 deletions
diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c
index 91acf24f1ef5..91c96ad12342 100644
--- a/drivers/bluetooth/hci_ll.c
+++ b/drivers/bluetooth/hci_ll.c
@@ -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;
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 010f1a8fd15f..5172afee5494 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -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;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 6eb59e9f2aa8..e6393f17576b 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -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:
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 31308c1de4ec..01f8ceeb1c0c 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -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);
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 3166914b0d6c..45d16639874a 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -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);
}
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 5deb6c4f1e41..3de3e3c8e966 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -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);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 597686790371..71e8c1b45bce 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -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;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index d52238ce6a9a..e5f9287fb826 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -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;
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index e7db50165879..584e059de20a 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -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);
}