Merge tag 'for-net-2025-05-15' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth
Luiz Augusto von Dentz says: ==================== bluetooth pull request for net: - btusb: use skb_pull to avoid unsafe access in QCA dump handling - L2CAP: Fix not checking l2cap_chan security level * tag 'for-net-2025-05-15' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth: Bluetooth: btusb: use skb_pull to avoid unsafe access in QCA dump handling Bluetooth: L2CAP: Fix not checking l2cap_chan security level ==================== Link: https://patch.msgid.link/20250515171909.1606243-1-luiz.dentz@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
+38
-56
@@ -3014,9 +3014,8 @@ static void btusb_coredump_qca(struct hci_dev *hdev)
|
|||||||
static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb)
|
static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
unsigned int skip = 0;
|
||||||
u8 pkt_type;
|
u8 pkt_type;
|
||||||
u8 *sk_ptr;
|
|
||||||
unsigned int sk_len;
|
|
||||||
u16 seqno;
|
u16 seqno;
|
||||||
u32 dump_size;
|
u32 dump_size;
|
||||||
|
|
||||||
@@ -3025,18 +3024,13 @@ static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
struct usb_device *udev = btdata->udev;
|
struct usb_device *udev = btdata->udev;
|
||||||
|
|
||||||
pkt_type = hci_skb_pkt_type(skb);
|
pkt_type = hci_skb_pkt_type(skb);
|
||||||
sk_ptr = skb->data;
|
skip = sizeof(struct hci_event_hdr);
|
||||||
sk_len = skb->len;
|
if (pkt_type == HCI_ACLDATA_PKT)
|
||||||
|
skip += sizeof(struct hci_acl_hdr);
|
||||||
|
|
||||||
if (pkt_type == HCI_ACLDATA_PKT) {
|
skb_pull(skb, skip);
|
||||||
sk_ptr += HCI_ACL_HDR_SIZE;
|
dump_hdr = (struct qca_dump_hdr *)skb->data;
|
||||||
sk_len -= HCI_ACL_HDR_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
sk_ptr += HCI_EVENT_HDR_SIZE;
|
|
||||||
sk_len -= HCI_EVENT_HDR_SIZE;
|
|
||||||
|
|
||||||
dump_hdr = (struct qca_dump_hdr *)sk_ptr;
|
|
||||||
seqno = le16_to_cpu(dump_hdr->seqno);
|
seqno = le16_to_cpu(dump_hdr->seqno);
|
||||||
if (seqno == 0) {
|
if (seqno == 0) {
|
||||||
set_bit(BTUSB_HW_SSR_ACTIVE, &btdata->flags);
|
set_bit(BTUSB_HW_SSR_ACTIVE, &btdata->flags);
|
||||||
@@ -3056,16 +3050,15 @@ static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
|
|
||||||
btdata->qca_dump.ram_dump_size = dump_size;
|
btdata->qca_dump.ram_dump_size = dump_size;
|
||||||
btdata->qca_dump.ram_dump_seqno = 0;
|
btdata->qca_dump.ram_dump_seqno = 0;
|
||||||
sk_ptr += offsetof(struct qca_dump_hdr, data0);
|
|
||||||
sk_len -= offsetof(struct qca_dump_hdr, data0);
|
skb_pull(skb, offsetof(struct qca_dump_hdr, data0));
|
||||||
|
|
||||||
usb_disable_autosuspend(udev);
|
usb_disable_autosuspend(udev);
|
||||||
bt_dev_info(hdev, "%s memdump size(%u)\n",
|
bt_dev_info(hdev, "%s memdump size(%u)\n",
|
||||||
(pkt_type == HCI_ACLDATA_PKT) ? "ACL" : "event",
|
(pkt_type == HCI_ACLDATA_PKT) ? "ACL" : "event",
|
||||||
dump_size);
|
dump_size);
|
||||||
} else {
|
} else {
|
||||||
sk_ptr += offsetof(struct qca_dump_hdr, data);
|
skb_pull(skb, offsetof(struct qca_dump_hdr, data));
|
||||||
sk_len -= offsetof(struct qca_dump_hdr, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!btdata->qca_dump.ram_dump_size) {
|
if (!btdata->qca_dump.ram_dump_size) {
|
||||||
@@ -3085,7 +3078,6 @@ static int handle_dump_pkt_qca(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
skb_pull(skb, skb->len - sk_len);
|
|
||||||
hci_devcd_append(hdev, skb);
|
hci_devcd_append(hdev, skb);
|
||||||
btdata->qca_dump.ram_dump_seqno++;
|
btdata->qca_dump.ram_dump_seqno++;
|
||||||
if (seqno == QCA_LAST_SEQUENCE_NUM) {
|
if (seqno == QCA_LAST_SEQUENCE_NUM) {
|
||||||
@@ -3113,68 +3105,58 @@ out:
|
|||||||
/* Return: true if the ACL packet is a dump packet, false otherwise. */
|
/* Return: true if the ACL packet is a dump packet, false otherwise. */
|
||||||
static bool acl_pkt_is_dump_qca(struct hci_dev *hdev, struct sk_buff *skb)
|
static bool acl_pkt_is_dump_qca(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
u8 *sk_ptr;
|
|
||||||
unsigned int sk_len;
|
|
||||||
|
|
||||||
struct hci_event_hdr *event_hdr;
|
struct hci_event_hdr *event_hdr;
|
||||||
struct hci_acl_hdr *acl_hdr;
|
struct hci_acl_hdr *acl_hdr;
|
||||||
struct qca_dump_hdr *dump_hdr;
|
struct qca_dump_hdr *dump_hdr;
|
||||||
|
struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
|
||||||
|
bool is_dump = false;
|
||||||
|
|
||||||
sk_ptr = skb->data;
|
if (!clone)
|
||||||
sk_len = skb->len;
|
|
||||||
|
|
||||||
acl_hdr = hci_acl_hdr(skb);
|
|
||||||
if (le16_to_cpu(acl_hdr->handle) != QCA_MEMDUMP_ACL_HANDLE)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
sk_ptr += HCI_ACL_HDR_SIZE;
|
acl_hdr = skb_pull_data(clone, sizeof(*acl_hdr));
|
||||||
sk_len -= HCI_ACL_HDR_SIZE;
|
if (!acl_hdr || (le16_to_cpu(acl_hdr->handle) != QCA_MEMDUMP_ACL_HANDLE))
|
||||||
event_hdr = (struct hci_event_hdr *)sk_ptr;
|
goto out;
|
||||||
|
|
||||||
if ((event_hdr->evt != HCI_VENDOR_PKT) ||
|
event_hdr = skb_pull_data(clone, sizeof(*event_hdr));
|
||||||
(event_hdr->plen != (sk_len - HCI_EVENT_HDR_SIZE)))
|
if (!event_hdr || (event_hdr->evt != HCI_VENDOR_PKT))
|
||||||
return false;
|
goto out;
|
||||||
|
|
||||||
sk_ptr += HCI_EVENT_HDR_SIZE;
|
dump_hdr = skb_pull_data(clone, sizeof(*dump_hdr));
|
||||||
sk_len -= HCI_EVENT_HDR_SIZE;
|
if (!dump_hdr || (dump_hdr->vse_class != QCA_MEMDUMP_VSE_CLASS) ||
|
||||||
|
|
||||||
dump_hdr = (struct qca_dump_hdr *)sk_ptr;
|
|
||||||
if ((sk_len < offsetof(struct qca_dump_hdr, data)) ||
|
|
||||||
(dump_hdr->vse_class != QCA_MEMDUMP_VSE_CLASS) ||
|
|
||||||
(dump_hdr->msg_type != QCA_MEMDUMP_MSG_TYPE))
|
(dump_hdr->msg_type != QCA_MEMDUMP_MSG_TYPE))
|
||||||
return false;
|
goto out;
|
||||||
|
|
||||||
return true;
|
is_dump = true;
|
||||||
|
out:
|
||||||
|
consume_skb(clone);
|
||||||
|
return is_dump;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return: true if the event packet is a dump packet, false otherwise. */
|
/* Return: true if the event packet is a dump packet, false otherwise. */
|
||||||
static bool evt_pkt_is_dump_qca(struct hci_dev *hdev, struct sk_buff *skb)
|
static bool evt_pkt_is_dump_qca(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
u8 *sk_ptr;
|
|
||||||
unsigned int sk_len;
|
|
||||||
|
|
||||||
struct hci_event_hdr *event_hdr;
|
struct hci_event_hdr *event_hdr;
|
||||||
struct qca_dump_hdr *dump_hdr;
|
struct qca_dump_hdr *dump_hdr;
|
||||||
|
struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
|
||||||
|
bool is_dump = false;
|
||||||
|
|
||||||
sk_ptr = skb->data;
|
if (!clone)
|
||||||
sk_len = skb->len;
|
|
||||||
|
|
||||||
event_hdr = hci_event_hdr(skb);
|
|
||||||
|
|
||||||
if ((event_hdr->evt != HCI_VENDOR_PKT)
|
|
||||||
|| (event_hdr->plen != (sk_len - HCI_EVENT_HDR_SIZE)))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
sk_ptr += HCI_EVENT_HDR_SIZE;
|
event_hdr = skb_pull_data(clone, sizeof(*event_hdr));
|
||||||
sk_len -= HCI_EVENT_HDR_SIZE;
|
if (!event_hdr || (event_hdr->evt != HCI_VENDOR_PKT))
|
||||||
|
goto out;
|
||||||
|
|
||||||
dump_hdr = (struct qca_dump_hdr *)sk_ptr;
|
dump_hdr = skb_pull_data(clone, sizeof(*dump_hdr));
|
||||||
if ((sk_len < offsetof(struct qca_dump_hdr, data)) ||
|
if (!dump_hdr || (dump_hdr->vse_class != QCA_MEMDUMP_VSE_CLASS) ||
|
||||||
(dump_hdr->vse_class != QCA_MEMDUMP_VSE_CLASS) ||
|
|
||||||
(dump_hdr->msg_type != QCA_MEMDUMP_MSG_TYPE))
|
(dump_hdr->msg_type != QCA_MEMDUMP_MSG_TYPE))
|
||||||
return false;
|
goto out;
|
||||||
|
|
||||||
return true;
|
is_dump = true;
|
||||||
|
out:
|
||||||
|
consume_skb(clone);
|
||||||
|
return is_dump;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int btusb_recv_acl_qca(struct hci_dev *hdev, struct sk_buff *skb)
|
static int btusb_recv_acl_qca(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
|
|||||||
@@ -1411,7 +1411,8 @@ static void l2cap_request_info(struct l2cap_conn *conn)
|
|||||||
sizeof(req), &req);
|
sizeof(req), &req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool l2cap_check_enc_key_size(struct hci_conn *hcon)
|
static bool l2cap_check_enc_key_size(struct hci_conn *hcon,
|
||||||
|
struct l2cap_chan *chan)
|
||||||
{
|
{
|
||||||
/* The minimum encryption key size needs to be enforced by the
|
/* The minimum encryption key size needs to be enforced by the
|
||||||
* host stack before establishing any L2CAP connections. The
|
* host stack before establishing any L2CAP connections. The
|
||||||
@@ -1425,7 +1426,7 @@ static bool l2cap_check_enc_key_size(struct hci_conn *hcon)
|
|||||||
int min_key_size = hcon->hdev->min_enc_key_size;
|
int min_key_size = hcon->hdev->min_enc_key_size;
|
||||||
|
|
||||||
/* On FIPS security level, key size must be 16 bytes */
|
/* On FIPS security level, key size must be 16 bytes */
|
||||||
if (hcon->sec_level == BT_SECURITY_FIPS)
|
if (chan->sec_level == BT_SECURITY_FIPS)
|
||||||
min_key_size = 16;
|
min_key_size = 16;
|
||||||
|
|
||||||
return (!test_bit(HCI_CONN_ENCRYPT, &hcon->flags) ||
|
return (!test_bit(HCI_CONN_ENCRYPT, &hcon->flags) ||
|
||||||
@@ -1453,7 +1454,7 @@ static void l2cap_do_start(struct l2cap_chan *chan)
|
|||||||
!__l2cap_no_conn_pending(chan))
|
!__l2cap_no_conn_pending(chan))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (l2cap_check_enc_key_size(conn->hcon))
|
if (l2cap_check_enc_key_size(conn->hcon, chan))
|
||||||
l2cap_start_connection(chan);
|
l2cap_start_connection(chan);
|
||||||
else
|
else
|
||||||
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
|
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
|
||||||
@@ -1528,7 +1529,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (l2cap_check_enc_key_size(conn->hcon))
|
if (l2cap_check_enc_key_size(conn->hcon, chan))
|
||||||
l2cap_start_connection(chan);
|
l2cap_start_connection(chan);
|
||||||
else
|
else
|
||||||
l2cap_chan_close(chan, ECONNREFUSED);
|
l2cap_chan_close(chan, ECONNREFUSED);
|
||||||
@@ -3992,7 +3993,7 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
|
|||||||
/* Check if the ACL is secure enough (if not SDP) */
|
/* Check if the ACL is secure enough (if not SDP) */
|
||||||
if (psm != cpu_to_le16(L2CAP_PSM_SDP) &&
|
if (psm != cpu_to_le16(L2CAP_PSM_SDP) &&
|
||||||
(!hci_conn_check_link_mode(conn->hcon) ||
|
(!hci_conn_check_link_mode(conn->hcon) ||
|
||||||
!l2cap_check_enc_key_size(conn->hcon))) {
|
!l2cap_check_enc_key_size(conn->hcon, pchan))) {
|
||||||
conn->disc_reason = HCI_ERROR_AUTH_FAILURE;
|
conn->disc_reason = HCI_ERROR_AUTH_FAILURE;
|
||||||
result = L2CAP_CR_SEC_BLOCK;
|
result = L2CAP_CR_SEC_BLOCK;
|
||||||
goto response;
|
goto response;
|
||||||
@@ -7352,7 +7353,7 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (chan->state == BT_CONNECT) {
|
if (chan->state == BT_CONNECT) {
|
||||||
if (!status && l2cap_check_enc_key_size(hcon))
|
if (!status && l2cap_check_enc_key_size(hcon, chan))
|
||||||
l2cap_start_connection(chan);
|
l2cap_start_connection(chan);
|
||||||
else
|
else
|
||||||
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
|
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
|
||||||
@@ -7362,7 +7363,7 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
|||||||
struct l2cap_conn_rsp rsp;
|
struct l2cap_conn_rsp rsp;
|
||||||
__u16 res, stat;
|
__u16 res, stat;
|
||||||
|
|
||||||
if (!status && l2cap_check_enc_key_size(hcon)) {
|
if (!status && l2cap_check_enc_key_size(hcon, chan)) {
|
||||||
if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
|
if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
|
||||||
res = L2CAP_CR_PEND;
|
res = L2CAP_CR_PEND;
|
||||||
stat = L2CAP_CS_AUTHOR_PEND;
|
stat = L2CAP_CS_AUTHOR_PEND;
|
||||||
|
|||||||
Reference in New Issue
Block a user