Bluetooth: validate retained LE Create CIS command length

hci_cs_le_create_cis() retrieves the parameters of the previously sent
command without their length.  It then trusts num_cis and walks the
flexible cis[] array when a controller reports a failed command.

A five-byte HCI command containing only num_cis can therefore make the
handler read beyond the command skb.  KASAN reports:

  BUG: KASAN: slab-out-of-bounds in hci_cs_le_create_cis
  Read of size 2
  hci_cs_le_create_cis
  hci_cmd_status_evt
  hci_event_packet

Add a length-returning saved-command helper and require the fixed header
and complete CIS array before walking it.

The reproducer no longer triggers a KASAN report with this change.

Fixes: 26afbd826e ("Bluetooth: Add initial implementation of CIS connections")
Closes: https://lore.kernel.org/linux-bluetooth/179317462624.87574.6271582002629080499@stu.pku.edu.cn/
Signed-off-by: Yifan Zhang <yfzhang23@stu.pku.edu.cn>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
Yifan Zhang 2026-11-02 19:58:37 +08:00 committed by Luiz Augusto von Dentz
parent a7c5c18b7c
commit 1eb52f35fc
3 changed files with 28 additions and 11 deletions

View File

@ -2428,6 +2428,8 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
void hci_send_iso(struct hci_conn *conn, struct sk_buff *skb);
void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);
void *hci_sent_cmd_data_len(struct hci_dev *hdev, __u16 opcode,
size_t *len);
void *hci_recv_event_data(struct hci_dev *hdev, __u8 event);
u32 hci_conn_get_phy(struct hci_conn *conn);

View File

@ -3175,7 +3175,8 @@ int __hci_cmd_send(struct hci_dev *hdev, u16 opcode, u32 plen,
EXPORT_SYMBOL(__hci_cmd_send);
/* Get data from the previously sent command */
static void *hci_cmd_data(struct sk_buff *skb, __u16 opcode)
static void *hci_cmd_data(struct sk_buff *skb, __u16 opcode,
size_t *len)
{
struct hci_command_hdr *hdr;
@ -3187,21 +3188,30 @@ static void *hci_cmd_data(struct sk_buff *skb, __u16 opcode)
if (hdr->opcode != cpu_to_le16(opcode))
return NULL;
if (len)
*len = skb->len - HCI_COMMAND_HDR_SIZE;
return skb->data + HCI_COMMAND_HDR_SIZE;
}
/* Get data from the previously sent command */
void *hci_sent_cmd_data_len(struct hci_dev *hdev, __u16 opcode, size_t *len)
{
void *data;
/* Check if opcode matches last sent command */
data = hci_cmd_data(hdev->sent_cmd, opcode, len);
if (!data)
/* Check if opcode matches last request */
data = hci_cmd_data(hdev->req_skb, opcode, len);
return data;
}
/* Get data from the previously sent command */
void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode)
{
void *data;
/* Check if opcode matches last sent command */
data = hci_cmd_data(hdev->sent_cmd, opcode);
if (!data)
/* Check if opcode matches last request */
data = hci_cmd_data(hdev->req_skb, opcode);
return data;
return hci_sent_cmd_data_len(hdev, opcode, NULL);
}
/* Get data from last received event */

View File

@ -4491,6 +4491,7 @@ static void hci_cs_le_create_cis(struct hci_dev *hdev, u8 status)
{
struct hci_cp_le_create_cis *cp;
bool pending = false;
size_t len;
int i;
bt_dev_dbg(hdev, "status 0x%2.2x", status);
@ -4498,10 +4499,14 @@ static void hci_cs_le_create_cis(struct hci_dev *hdev, u8 status)
if (!status)
return;
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_CREATE_CIS);
cp = hci_sent_cmd_data_len(hdev, HCI_OP_LE_CREATE_CIS, &len);
if (!cp)
return;
if (len < sizeof(*cp) ||
len < struct_size(cp, cis, cp->num_cis))
return;
hci_dev_lock(hdev);
/* Remove connection if command failed */