VYPR
Unrated severityNVD Advisory· Published May 28, 2026

CVE-2026-46186

CVE-2026-46186

Description

In the Linux kernel, the following vulnerability has been resolved:

Bluetooth: virtio_bt: validate rx pkt_type header length

virtbt_rx_handle() reads the leading pkt_type byte from the RX skb and forwards the remainder to hci_recv_frame() for every event/ACL/SCO/ISO type, without checking that the remaining payload is at least the fixed HCI header for that type.

After the preceding patch bounds the backend-supplied used.len to [1, VIRTBT_RX_BUF_SIZE], a one-byte completion still reaches hci_recv_frame() with skb->len already pulled to 0. If the byte happened to be HCI_ACLDATA_PKT, the ACL-vs-ISO classification fast-path in hci_dev_classify_pkt_type() dereferences hci_acl_hdr(skb)->handle whenever the HCI device has an active CIS_LINK, BIS_LINK, or PA_LINK connection, reading two bytes of uninitialized RX-buffer data. The same hazard exists for every packet type the driver accepts because none of the switch cases in virtbt_rx_handle() check skb->len against the per-type minimum HCI header size before handing the frame to the core.

After stripping pkt_type, require skb->len to cover the fixed header size for the selected type (event 2, ACL 4, SCO 3, ISO 4) before calling hci_recv_frame(); drop ratelimited otherwise. Unknown pkt_type values still take the original kfree_skb() default path.

Use bt_dev_err_ratelimited() because both the length and pkt_type values come from an untrusted backend that can otherwise flood the kernel log.

Affected products

1

Patches

10
daf23014e5d9

Bluetooth: virtio_bt: validate rx pkt_type header length

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitMichael BommaritoApr 21, 2026Fixed in 7.1-rc3via kernel-cna
2 files changed · +40 8
  • drivers/bluetooth/virtio_bt.c+20 4 modified
    diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
    index 2c5c39356a1c81..140ab55c9fc5a9 100644
    --- a/drivers/bluetooth/virtio_bt.c
    +++ b/drivers/bluetooth/virtio_bt.c
    @@ -198,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
     
     static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     {
    +	size_t min_hdr;
     	__u8 pkt_type;
     
     	pkt_type = *((__u8 *) skb->data);
    @@ -205,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     
     	switch (pkt_type) {
     	case HCI_EVENT_PKT:
    +		min_hdr = sizeof(struct hci_event_hdr);
    +		break;
     	case HCI_ACLDATA_PKT:
    +		min_hdr = sizeof(struct hci_acl_hdr);
    +		break;
     	case HCI_SCODATA_PKT:
    +		min_hdr = sizeof(struct hci_sco_hdr);
    +		break;
     	case HCI_ISODATA_PKT:
    -		hci_skb_pkt_type(skb) = pkt_type;
    -		hci_recv_frame(vbt->hdev, skb);
    +		min_hdr = sizeof(struct hci_iso_hdr);
     		break;
     	default:
     		kfree_skb(skb);
    -		break;
    +		return;
     	}
    +
    +	if (skb->len < min_hdr) {
    +		bt_dev_err_ratelimited(vbt->hdev,
    +				       "rx pkt_type 0x%02x payload %u < hdr %zu\n",
    +				       pkt_type, skb->len, min_hdr);
    +		kfree_skb(skb);
    +		return;
    +	}
    +
    +	hci_skb_pkt_type(skb) = pkt_type;
    +	hci_recv_frame(vbt->hdev, skb);
     }
     
     static void virtbt_rx_work(struct work_struct *work)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/bluetooth/virtio_bt.c+20 4 modified
    diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
    index 2c5c39356a1c81..140ab55c9fc5a9 100644
    --- a/drivers/bluetooth/virtio_bt.c
    +++ b/drivers/bluetooth/virtio_bt.c
    @@ -198,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
     
     static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     {
    +	size_t min_hdr;
     	__u8 pkt_type;
     
     	pkt_type = *((__u8 *) skb->data);
    @@ -205,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     
     	switch (pkt_type) {
     	case HCI_EVENT_PKT:
    +		min_hdr = sizeof(struct hci_event_hdr);
    +		break;
     	case HCI_ACLDATA_PKT:
    +		min_hdr = sizeof(struct hci_acl_hdr);
    +		break;
     	case HCI_SCODATA_PKT:
    +		min_hdr = sizeof(struct hci_sco_hdr);
    +		break;
     	case HCI_ISODATA_PKT:
    -		hci_skb_pkt_type(skb) = pkt_type;
    -		hci_recv_frame(vbt->hdev, skb);
    +		min_hdr = sizeof(struct hci_iso_hdr);
     		break;
     	default:
     		kfree_skb(skb);
    -		break;
    +		return;
     	}
    +
    +	if (skb->len < min_hdr) {
    +		bt_dev_err_ratelimited(vbt->hdev,
    +				       "rx pkt_type 0x%02x payload %u < hdr %zu\n",
    +				       pkt_type, skb->len, min_hdr);
    +		kfree_skb(skb);
    +		return;
    +	}
    +
    +	hci_skb_pkt_type(skb) = pkt_type;
    +	hci_recv_frame(vbt->hdev, skb);
     }
     
     static void virtbt_rx_work(struct work_struct *work)
    -- 
    cgit 1.3-korg
    
    
    
3485c7236c59

Bluetooth: virtio_bt: validate rx pkt_type header length

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitMichael BommaritoApr 21, 2026Fixed in 6.18.30via kernel-cna
2 files changed · +40 8
  • drivers/bluetooth/virtio_bt.c+20 4 modified
    diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
    index 3a0f6715cd9944..9341c5eed5b530 100644
    --- a/drivers/bluetooth/virtio_bt.c
    +++ b/drivers/bluetooth/virtio_bt.c
    @@ -198,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
     
     static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     {
    +	size_t min_hdr;
     	__u8 pkt_type;
     
     	pkt_type = *((__u8 *) skb->data);
    @@ -205,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     
     	switch (pkt_type) {
     	case HCI_EVENT_PKT:
    +		min_hdr = sizeof(struct hci_event_hdr);
    +		break;
     	case HCI_ACLDATA_PKT:
    +		min_hdr = sizeof(struct hci_acl_hdr);
    +		break;
     	case HCI_SCODATA_PKT:
    +		min_hdr = sizeof(struct hci_sco_hdr);
    +		break;
     	case HCI_ISODATA_PKT:
    -		hci_skb_pkt_type(skb) = pkt_type;
    -		hci_recv_frame(vbt->hdev, skb);
    +		min_hdr = sizeof(struct hci_iso_hdr);
     		break;
     	default:
     		kfree_skb(skb);
    -		break;
    +		return;
     	}
    +
    +	if (skb->len < min_hdr) {
    +		bt_dev_err_ratelimited(vbt->hdev,
    +				       "rx pkt_type 0x%02x payload %u < hdr %zu\n",
    +				       pkt_type, skb->len, min_hdr);
    +		kfree_skb(skb);
    +		return;
    +	}
    +
    +	hci_skb_pkt_type(skb) = pkt_type;
    +	hci_recv_frame(vbt->hdev, skb);
     }
     
     static void virtbt_rx_work(struct work_struct *work)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/bluetooth/virtio_bt.c+20 4 modified
    diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
    index 3a0f6715cd9944..9341c5eed5b530 100644
    --- a/drivers/bluetooth/virtio_bt.c
    +++ b/drivers/bluetooth/virtio_bt.c
    @@ -198,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
     
     static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     {
    +	size_t min_hdr;
     	__u8 pkt_type;
     
     	pkt_type = *((__u8 *) skb->data);
    @@ -205,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     
     	switch (pkt_type) {
     	case HCI_EVENT_PKT:
    +		min_hdr = sizeof(struct hci_event_hdr);
    +		break;
     	case HCI_ACLDATA_PKT:
    +		min_hdr = sizeof(struct hci_acl_hdr);
    +		break;
     	case HCI_SCODATA_PKT:
    +		min_hdr = sizeof(struct hci_sco_hdr);
    +		break;
     	case HCI_ISODATA_PKT:
    -		hci_skb_pkt_type(skb) = pkt_type;
    -		hci_recv_frame(vbt->hdev, skb);
    +		min_hdr = sizeof(struct hci_iso_hdr);
     		break;
     	default:
     		kfree_skb(skb);
    -		break;
    +		return;
     	}
    +
    +	if (skb->len < min_hdr) {
    +		bt_dev_err_ratelimited(vbt->hdev,
    +				       "rx pkt_type 0x%02x payload %u < hdr %zu\n",
    +				       pkt_type, skb->len, min_hdr);
    +		kfree_skb(skb);
    +		return;
    +	}
    +
    +	hci_skb_pkt_type(skb) = pkt_type;
    +	hci_recv_frame(vbt->hdev, skb);
     }
     
     static void virtbt_rx_work(struct work_struct *work)
    -- 
    cgit 1.3-korg
    
    
    
2c1143564c71

Bluetooth: virtio_bt: validate rx pkt_type header length

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitMichael BommaritoApr 21, 2026Fixed in 6.12.88via kernel-cna
2 files changed · +40 8
  • drivers/bluetooth/virtio_bt.c+20 4 modified
    diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
    index 3fe17539b4ba54..0a602f4cbccc04 100644
    --- a/drivers/bluetooth/virtio_bt.c
    +++ b/drivers/bluetooth/virtio_bt.c
    @@ -198,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
     
     static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     {
    +	size_t min_hdr;
     	__u8 pkt_type;
     
     	pkt_type = *((__u8 *) skb->data);
    @@ -205,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     
     	switch (pkt_type) {
     	case HCI_EVENT_PKT:
    +		min_hdr = sizeof(struct hci_event_hdr);
    +		break;
     	case HCI_ACLDATA_PKT:
    +		min_hdr = sizeof(struct hci_acl_hdr);
    +		break;
     	case HCI_SCODATA_PKT:
    +		min_hdr = sizeof(struct hci_sco_hdr);
    +		break;
     	case HCI_ISODATA_PKT:
    -		hci_skb_pkt_type(skb) = pkt_type;
    -		hci_recv_frame(vbt->hdev, skb);
    +		min_hdr = sizeof(struct hci_iso_hdr);
     		break;
     	default:
     		kfree_skb(skb);
    -		break;
    +		return;
     	}
    +
    +	if (skb->len < min_hdr) {
    +		bt_dev_err_ratelimited(vbt->hdev,
    +				       "rx pkt_type 0x%02x payload %u < hdr %zu\n",
    +				       pkt_type, skb->len, min_hdr);
    +		kfree_skb(skb);
    +		return;
    +	}
    +
    +	hci_skb_pkt_type(skb) = pkt_type;
    +	hci_recv_frame(vbt->hdev, skb);
     }
     
     static void virtbt_rx_work(struct work_struct *work)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/bluetooth/virtio_bt.c+20 4 modified
    diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
    index 3fe17539b4ba54..0a602f4cbccc04 100644
    --- a/drivers/bluetooth/virtio_bt.c
    +++ b/drivers/bluetooth/virtio_bt.c
    @@ -198,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
     
     static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     {
    +	size_t min_hdr;
     	__u8 pkt_type;
     
     	pkt_type = *((__u8 *) skb->data);
    @@ -205,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     
     	switch (pkt_type) {
     	case HCI_EVENT_PKT:
    +		min_hdr = sizeof(struct hci_event_hdr);
    +		break;
     	case HCI_ACLDATA_PKT:
    +		min_hdr = sizeof(struct hci_acl_hdr);
    +		break;
     	case HCI_SCODATA_PKT:
    +		min_hdr = sizeof(struct hci_sco_hdr);
    +		break;
     	case HCI_ISODATA_PKT:
    -		hci_skb_pkt_type(skb) = pkt_type;
    -		hci_recv_frame(vbt->hdev, skb);
    +		min_hdr = sizeof(struct hci_iso_hdr);
     		break;
     	default:
     		kfree_skb(skb);
    -		break;
    +		return;
     	}
    +
    +	if (skb->len < min_hdr) {
    +		bt_dev_err_ratelimited(vbt->hdev,
    +				       "rx pkt_type 0x%02x payload %u < hdr %zu\n",
    +				       pkt_type, skb->len, min_hdr);
    +		kfree_skb(skb);
    +		return;
    +	}
    +
    +	hci_skb_pkt_type(skb) = pkt_type;
    +	hci_recv_frame(vbt->hdev, skb);
     }
     
     static void virtbt_rx_work(struct work_struct *work)
    -- 
    cgit 1.3-korg
    
    
    
1e1e509b6fd2

Bluetooth: virtio_bt: validate rx pkt_type header length

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitMichael BommaritoApr 21, 2026Fixed in 6.6.140via kernel-cna
2 files changed · +40 8
  • drivers/bluetooth/virtio_bt.c+20 4 modified
    diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
    index af1775d04860b2..ca62b73699398e 100644
    --- a/drivers/bluetooth/virtio_bt.c
    +++ b/drivers/bluetooth/virtio_bt.c
    @@ -198,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
     
     static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     {
    +	size_t min_hdr;
     	__u8 pkt_type;
     
     	pkt_type = *((__u8 *) skb->data);
    @@ -205,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     
     	switch (pkt_type) {
     	case HCI_EVENT_PKT:
    +		min_hdr = sizeof(struct hci_event_hdr);
    +		break;
     	case HCI_ACLDATA_PKT:
    +		min_hdr = sizeof(struct hci_acl_hdr);
    +		break;
     	case HCI_SCODATA_PKT:
    +		min_hdr = sizeof(struct hci_sco_hdr);
    +		break;
     	case HCI_ISODATA_PKT:
    -		hci_skb_pkt_type(skb) = pkt_type;
    -		hci_recv_frame(vbt->hdev, skb);
    +		min_hdr = sizeof(struct hci_iso_hdr);
     		break;
     	default:
     		kfree_skb(skb);
    -		break;
    +		return;
     	}
    +
    +	if (skb->len < min_hdr) {
    +		bt_dev_err_ratelimited(vbt->hdev,
    +				       "rx pkt_type 0x%02x payload %u < hdr %zu\n",
    +				       pkt_type, skb->len, min_hdr);
    +		kfree_skb(skb);
    +		return;
    +	}
    +
    +	hci_skb_pkt_type(skb) = pkt_type;
    +	hci_recv_frame(vbt->hdev, skb);
     }
     
     static void virtbt_rx_work(struct work_struct *work)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/bluetooth/virtio_bt.c+20 4 modified
    diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
    index af1775d04860b2..ca62b73699398e 100644
    --- a/drivers/bluetooth/virtio_bt.c
    +++ b/drivers/bluetooth/virtio_bt.c
    @@ -198,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
     
     static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     {
    +	size_t min_hdr;
     	__u8 pkt_type;
     
     	pkt_type = *((__u8 *) skb->data);
    @@ -205,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     
     	switch (pkt_type) {
     	case HCI_EVENT_PKT:
    +		min_hdr = sizeof(struct hci_event_hdr);
    +		break;
     	case HCI_ACLDATA_PKT:
    +		min_hdr = sizeof(struct hci_acl_hdr);
    +		break;
     	case HCI_SCODATA_PKT:
    +		min_hdr = sizeof(struct hci_sco_hdr);
    +		break;
     	case HCI_ISODATA_PKT:
    -		hci_skb_pkt_type(skb) = pkt_type;
    -		hci_recv_frame(vbt->hdev, skb);
    +		min_hdr = sizeof(struct hci_iso_hdr);
     		break;
     	default:
     		kfree_skb(skb);
    -		break;
    +		return;
     	}
    +
    +	if (skb->len < min_hdr) {
    +		bt_dev_err_ratelimited(vbt->hdev,
    +				       "rx pkt_type 0x%02x payload %u < hdr %zu\n",
    +				       pkt_type, skb->len, min_hdr);
    +		kfree_skb(skb);
    +		return;
    +	}
    +
    +	hci_skb_pkt_type(skb) = pkt_type;
    +	hci_recv_frame(vbt->hdev, skb);
     }
     
     static void virtbt_rx_work(struct work_struct *work)
    -- 
    cgit 1.3-korg
    
    
    
f743eab64869

Bluetooth: virtio_bt: validate rx pkt_type header length

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitMichael BommaritoApr 21, 2026Fixed in 7.0.7via kernel-cna
2 files changed · +40 8
  • drivers/bluetooth/virtio_bt.c+20 4 modified
    diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
    index 2c5c39356a1c81..140ab55c9fc5a9 100644
    --- a/drivers/bluetooth/virtio_bt.c
    +++ b/drivers/bluetooth/virtio_bt.c
    @@ -198,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
     
     static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     {
    +	size_t min_hdr;
     	__u8 pkt_type;
     
     	pkt_type = *((__u8 *) skb->data);
    @@ -205,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     
     	switch (pkt_type) {
     	case HCI_EVENT_PKT:
    +		min_hdr = sizeof(struct hci_event_hdr);
    +		break;
     	case HCI_ACLDATA_PKT:
    +		min_hdr = sizeof(struct hci_acl_hdr);
    +		break;
     	case HCI_SCODATA_PKT:
    +		min_hdr = sizeof(struct hci_sco_hdr);
    +		break;
     	case HCI_ISODATA_PKT:
    -		hci_skb_pkt_type(skb) = pkt_type;
    -		hci_recv_frame(vbt->hdev, skb);
    +		min_hdr = sizeof(struct hci_iso_hdr);
     		break;
     	default:
     		kfree_skb(skb);
    -		break;
    +		return;
     	}
    +
    +	if (skb->len < min_hdr) {
    +		bt_dev_err_ratelimited(vbt->hdev,
    +				       "rx pkt_type 0x%02x payload %u < hdr %zu\n",
    +				       pkt_type, skb->len, min_hdr);
    +		kfree_skb(skb);
    +		return;
    +	}
    +
    +	hci_skb_pkt_type(skb) = pkt_type;
    +	hci_recv_frame(vbt->hdev, skb);
     }
     
     static void virtbt_rx_work(struct work_struct *work)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/bluetooth/virtio_bt.c+20 4 modified
    diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
    index 2c5c39356a1c81..140ab55c9fc5a9 100644
    --- a/drivers/bluetooth/virtio_bt.c
    +++ b/drivers/bluetooth/virtio_bt.c
    @@ -198,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
     
     static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     {
    +	size_t min_hdr;
     	__u8 pkt_type;
     
     	pkt_type = *((__u8 *) skb->data);
    @@ -205,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     
     	switch (pkt_type) {
     	case HCI_EVENT_PKT:
    +		min_hdr = sizeof(struct hci_event_hdr);
    +		break;
     	case HCI_ACLDATA_PKT:
    +		min_hdr = sizeof(struct hci_acl_hdr);
    +		break;
     	case HCI_SCODATA_PKT:
    +		min_hdr = sizeof(struct hci_sco_hdr);
    +		break;
     	case HCI_ISODATA_PKT:
    -		hci_skb_pkt_type(skb) = pkt_type;
    -		hci_recv_frame(vbt->hdev, skb);
    +		min_hdr = sizeof(struct hci_iso_hdr);
     		break;
     	default:
     		kfree_skb(skb);
    -		break;
    +		return;
     	}
    +
    +	if (skb->len < min_hdr) {
    +		bt_dev_err_ratelimited(vbt->hdev,
    +				       "rx pkt_type 0x%02x payload %u < hdr %zu\n",
    +				       pkt_type, skb->len, min_hdr);
    +		kfree_skb(skb);
    +		return;
    +	}
    +
    +	hci_skb_pkt_type(skb) = pkt_type;
    +	hci_recv_frame(vbt->hdev, skb);
     }
     
     static void virtbt_rx_work(struct work_struct *work)
    -- 
    cgit 1.3-korg
    
    
    
f743eab64869

Bluetooth: virtio_bt: validate rx pkt_type header length

2 files changed · +40 8
  • drivers/bluetooth/virtio_bt.c+20 4 modified
    diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
    index 2c5c39356a1c81..140ab55c9fc5a9 100644
    --- a/drivers/bluetooth/virtio_bt.c
    +++ b/drivers/bluetooth/virtio_bt.c
    @@ -198,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
     
     static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     {
    +	size_t min_hdr;
     	__u8 pkt_type;
     
     	pkt_type = *((__u8 *) skb->data);
    @@ -205,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     
     	switch (pkt_type) {
     	case HCI_EVENT_PKT:
    +		min_hdr = sizeof(struct hci_event_hdr);
    +		break;
     	case HCI_ACLDATA_PKT:
    +		min_hdr = sizeof(struct hci_acl_hdr);
    +		break;
     	case HCI_SCODATA_PKT:
    +		min_hdr = sizeof(struct hci_sco_hdr);
    +		break;
     	case HCI_ISODATA_PKT:
    -		hci_skb_pkt_type(skb) = pkt_type;
    -		hci_recv_frame(vbt->hdev, skb);
    +		min_hdr = sizeof(struct hci_iso_hdr);
     		break;
     	default:
     		kfree_skb(skb);
    -		break;
    +		return;
     	}
    +
    +	if (skb->len < min_hdr) {
    +		bt_dev_err_ratelimited(vbt->hdev,
    +				       "rx pkt_type 0x%02x payload %u < hdr %zu\n",
    +				       pkt_type, skb->len, min_hdr);
    +		kfree_skb(skb);
    +		return;
    +	}
    +
    +	hci_skb_pkt_type(skb) = pkt_type;
    +	hci_recv_frame(vbt->hdev, skb);
     }
     
     static void virtbt_rx_work(struct work_struct *work)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/bluetooth/virtio_bt.c+20 4 modified
    diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
    index 2c5c39356a1c81..140ab55c9fc5a9 100644
    --- a/drivers/bluetooth/virtio_bt.c
    +++ b/drivers/bluetooth/virtio_bt.c
    @@ -198,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
     
     static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     {
    +	size_t min_hdr;
     	__u8 pkt_type;
     
     	pkt_type = *((__u8 *) skb->data);
    @@ -205,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     
     	switch (pkt_type) {
     	case HCI_EVENT_PKT:
    +		min_hdr = sizeof(struct hci_event_hdr);
    +		break;
     	case HCI_ACLDATA_PKT:
    +		min_hdr = sizeof(struct hci_acl_hdr);
    +		break;
     	case HCI_SCODATA_PKT:
    +		min_hdr = sizeof(struct hci_sco_hdr);
    +		break;
     	case HCI_ISODATA_PKT:
    -		hci_skb_pkt_type(skb) = pkt_type;
    -		hci_recv_frame(vbt->hdev, skb);
    +		min_hdr = sizeof(struct hci_iso_hdr);
     		break;
     	default:
     		kfree_skb(skb);
    -		break;
    +		return;
     	}
    +
    +	if (skb->len < min_hdr) {
    +		bt_dev_err_ratelimited(vbt->hdev,
    +				       "rx pkt_type 0x%02x payload %u < hdr %zu\n",
    +				       pkt_type, skb->len, min_hdr);
    +		kfree_skb(skb);
    +		return;
    +	}
    +
    +	hci_skb_pkt_type(skb) = pkt_type;
    +	hci_recv_frame(vbt->hdev, skb);
     }
     
     static void virtbt_rx_work(struct work_struct *work)
    -- 
    cgit 1.3-korg
    
    
    
1e1e509b6fd2

Bluetooth: virtio_bt: validate rx pkt_type header length

2 files changed · +40 8
  • drivers/bluetooth/virtio_bt.c+20 4 modified
    diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
    index af1775d04860b2..ca62b73699398e 100644
    --- a/drivers/bluetooth/virtio_bt.c
    +++ b/drivers/bluetooth/virtio_bt.c
    @@ -198,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
     
     static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     {
    +	size_t min_hdr;
     	__u8 pkt_type;
     
     	pkt_type = *((__u8 *) skb->data);
    @@ -205,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     
     	switch (pkt_type) {
     	case HCI_EVENT_PKT:
    +		min_hdr = sizeof(struct hci_event_hdr);
    +		break;
     	case HCI_ACLDATA_PKT:
    +		min_hdr = sizeof(struct hci_acl_hdr);
    +		break;
     	case HCI_SCODATA_PKT:
    +		min_hdr = sizeof(struct hci_sco_hdr);
    +		break;
     	case HCI_ISODATA_PKT:
    -		hci_skb_pkt_type(skb) = pkt_type;
    -		hci_recv_frame(vbt->hdev, skb);
    +		min_hdr = sizeof(struct hci_iso_hdr);
     		break;
     	default:
     		kfree_skb(skb);
    -		break;
    +		return;
     	}
    +
    +	if (skb->len < min_hdr) {
    +		bt_dev_err_ratelimited(vbt->hdev,
    +				       "rx pkt_type 0x%02x payload %u < hdr %zu\n",
    +				       pkt_type, skb->len, min_hdr);
    +		kfree_skb(skb);
    +		return;
    +	}
    +
    +	hci_skb_pkt_type(skb) = pkt_type;
    +	hci_recv_frame(vbt->hdev, skb);
     }
     
     static void virtbt_rx_work(struct work_struct *work)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/bluetooth/virtio_bt.c+20 4 modified
    diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
    index af1775d04860b2..ca62b73699398e 100644
    --- a/drivers/bluetooth/virtio_bt.c
    +++ b/drivers/bluetooth/virtio_bt.c
    @@ -198,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
     
     static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     {
    +	size_t min_hdr;
     	__u8 pkt_type;
     
     	pkt_type = *((__u8 *) skb->data);
    @@ -205,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     
     	switch (pkt_type) {
     	case HCI_EVENT_PKT:
    +		min_hdr = sizeof(struct hci_event_hdr);
    +		break;
     	case HCI_ACLDATA_PKT:
    +		min_hdr = sizeof(struct hci_acl_hdr);
    +		break;
     	case HCI_SCODATA_PKT:
    +		min_hdr = sizeof(struct hci_sco_hdr);
    +		break;
     	case HCI_ISODATA_PKT:
    -		hci_skb_pkt_type(skb) = pkt_type;
    -		hci_recv_frame(vbt->hdev, skb);
    +		min_hdr = sizeof(struct hci_iso_hdr);
     		break;
     	default:
     		kfree_skb(skb);
    -		break;
    +		return;
     	}
    +
    +	if (skb->len < min_hdr) {
    +		bt_dev_err_ratelimited(vbt->hdev,
    +				       "rx pkt_type 0x%02x payload %u < hdr %zu\n",
    +				       pkt_type, skb->len, min_hdr);
    +		kfree_skb(skb);
    +		return;
    +	}
    +
    +	hci_skb_pkt_type(skb) = pkt_type;
    +	hci_recv_frame(vbt->hdev, skb);
     }
     
     static void virtbt_rx_work(struct work_struct *work)
    -- 
    cgit 1.3-korg
    
    
    
2c1143564c71

Bluetooth: virtio_bt: validate rx pkt_type header length

2 files changed · +40 8
  • drivers/bluetooth/virtio_bt.c+20 4 modified
    diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
    index 3fe17539b4ba54..0a602f4cbccc04 100644
    --- a/drivers/bluetooth/virtio_bt.c
    +++ b/drivers/bluetooth/virtio_bt.c
    @@ -198,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
     
     static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     {
    +	size_t min_hdr;
     	__u8 pkt_type;
     
     	pkt_type = *((__u8 *) skb->data);
    @@ -205,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     
     	switch (pkt_type) {
     	case HCI_EVENT_PKT:
    +		min_hdr = sizeof(struct hci_event_hdr);
    +		break;
     	case HCI_ACLDATA_PKT:
    +		min_hdr = sizeof(struct hci_acl_hdr);
    +		break;
     	case HCI_SCODATA_PKT:
    +		min_hdr = sizeof(struct hci_sco_hdr);
    +		break;
     	case HCI_ISODATA_PKT:
    -		hci_skb_pkt_type(skb) = pkt_type;
    -		hci_recv_frame(vbt->hdev, skb);
    +		min_hdr = sizeof(struct hci_iso_hdr);
     		break;
     	default:
     		kfree_skb(skb);
    -		break;
    +		return;
     	}
    +
    +	if (skb->len < min_hdr) {
    +		bt_dev_err_ratelimited(vbt->hdev,
    +				       "rx pkt_type 0x%02x payload %u < hdr %zu\n",
    +				       pkt_type, skb->len, min_hdr);
    +		kfree_skb(skb);
    +		return;
    +	}
    +
    +	hci_skb_pkt_type(skb) = pkt_type;
    +	hci_recv_frame(vbt->hdev, skb);
     }
     
     static void virtbt_rx_work(struct work_struct *work)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/bluetooth/virtio_bt.c+20 4 modified
    diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
    index 3fe17539b4ba54..0a602f4cbccc04 100644
    --- a/drivers/bluetooth/virtio_bt.c
    +++ b/drivers/bluetooth/virtio_bt.c
    @@ -198,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
     
     static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     {
    +	size_t min_hdr;
     	__u8 pkt_type;
     
     	pkt_type = *((__u8 *) skb->data);
    @@ -205,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     
     	switch (pkt_type) {
     	case HCI_EVENT_PKT:
    +		min_hdr = sizeof(struct hci_event_hdr);
    +		break;
     	case HCI_ACLDATA_PKT:
    +		min_hdr = sizeof(struct hci_acl_hdr);
    +		break;
     	case HCI_SCODATA_PKT:
    +		min_hdr = sizeof(struct hci_sco_hdr);
    +		break;
     	case HCI_ISODATA_PKT:
    -		hci_skb_pkt_type(skb) = pkt_type;
    -		hci_recv_frame(vbt->hdev, skb);
    +		min_hdr = sizeof(struct hci_iso_hdr);
     		break;
     	default:
     		kfree_skb(skb);
    -		break;
    +		return;
     	}
    +
    +	if (skb->len < min_hdr) {
    +		bt_dev_err_ratelimited(vbt->hdev,
    +				       "rx pkt_type 0x%02x payload %u < hdr %zu\n",
    +				       pkt_type, skb->len, min_hdr);
    +		kfree_skb(skb);
    +		return;
    +	}
    +
    +	hci_skb_pkt_type(skb) = pkt_type;
    +	hci_recv_frame(vbt->hdev, skb);
     }
     
     static void virtbt_rx_work(struct work_struct *work)
    -- 
    cgit 1.3-korg
    
    
    
3485c7236c59

Bluetooth: virtio_bt: validate rx pkt_type header length

2 files changed · +40 8
  • drivers/bluetooth/virtio_bt.c+20 4 modified
    diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
    index 3a0f6715cd9944..9341c5eed5b530 100644
    --- a/drivers/bluetooth/virtio_bt.c
    +++ b/drivers/bluetooth/virtio_bt.c
    @@ -198,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
     
     static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     {
    +	size_t min_hdr;
     	__u8 pkt_type;
     
     	pkt_type = *((__u8 *) skb->data);
    @@ -205,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     
     	switch (pkt_type) {
     	case HCI_EVENT_PKT:
    +		min_hdr = sizeof(struct hci_event_hdr);
    +		break;
     	case HCI_ACLDATA_PKT:
    +		min_hdr = sizeof(struct hci_acl_hdr);
    +		break;
     	case HCI_SCODATA_PKT:
    +		min_hdr = sizeof(struct hci_sco_hdr);
    +		break;
     	case HCI_ISODATA_PKT:
    -		hci_skb_pkt_type(skb) = pkt_type;
    -		hci_recv_frame(vbt->hdev, skb);
    +		min_hdr = sizeof(struct hci_iso_hdr);
     		break;
     	default:
     		kfree_skb(skb);
    -		break;
    +		return;
     	}
    +
    +	if (skb->len < min_hdr) {
    +		bt_dev_err_ratelimited(vbt->hdev,
    +				       "rx pkt_type 0x%02x payload %u < hdr %zu\n",
    +				       pkt_type, skb->len, min_hdr);
    +		kfree_skb(skb);
    +		return;
    +	}
    +
    +	hci_skb_pkt_type(skb) = pkt_type;
    +	hci_recv_frame(vbt->hdev, skb);
     }
     
     static void virtbt_rx_work(struct work_struct *work)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/bluetooth/virtio_bt.c+20 4 modified
    diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
    index 3a0f6715cd9944..9341c5eed5b530 100644
    --- a/drivers/bluetooth/virtio_bt.c
    +++ b/drivers/bluetooth/virtio_bt.c
    @@ -198,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
     
     static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     {
    +	size_t min_hdr;
     	__u8 pkt_type;
     
     	pkt_type = *((__u8 *) skb->data);
    @@ -205,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     
     	switch (pkt_type) {
     	case HCI_EVENT_PKT:
    +		min_hdr = sizeof(struct hci_event_hdr);
    +		break;
     	case HCI_ACLDATA_PKT:
    +		min_hdr = sizeof(struct hci_acl_hdr);
    +		break;
     	case HCI_SCODATA_PKT:
    +		min_hdr = sizeof(struct hci_sco_hdr);
    +		break;
     	case HCI_ISODATA_PKT:
    -		hci_skb_pkt_type(skb) = pkt_type;
    -		hci_recv_frame(vbt->hdev, skb);
    +		min_hdr = sizeof(struct hci_iso_hdr);
     		break;
     	default:
     		kfree_skb(skb);
    -		break;
    +		return;
     	}
    +
    +	if (skb->len < min_hdr) {
    +		bt_dev_err_ratelimited(vbt->hdev,
    +				       "rx pkt_type 0x%02x payload %u < hdr %zu\n",
    +				       pkt_type, skb->len, min_hdr);
    +		kfree_skb(skb);
    +		return;
    +	}
    +
    +	hci_skb_pkt_type(skb) = pkt_type;
    +	hci_recv_frame(vbt->hdev, skb);
     }
     
     static void virtbt_rx_work(struct work_struct *work)
    -- 
    cgit 1.3-korg
    
    
    
daf23014e5d9

Bluetooth: virtio_bt: validate rx pkt_type header length

2 files changed · +40 8
  • drivers/bluetooth/virtio_bt.c+20 4 modified
    diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
    index 2c5c39356a1c81..140ab55c9fc5a9 100644
    --- a/drivers/bluetooth/virtio_bt.c
    +++ b/drivers/bluetooth/virtio_bt.c
    @@ -198,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
     
     static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     {
    +	size_t min_hdr;
     	__u8 pkt_type;
     
     	pkt_type = *((__u8 *) skb->data);
    @@ -205,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     
     	switch (pkt_type) {
     	case HCI_EVENT_PKT:
    +		min_hdr = sizeof(struct hci_event_hdr);
    +		break;
     	case HCI_ACLDATA_PKT:
    +		min_hdr = sizeof(struct hci_acl_hdr);
    +		break;
     	case HCI_SCODATA_PKT:
    +		min_hdr = sizeof(struct hci_sco_hdr);
    +		break;
     	case HCI_ISODATA_PKT:
    -		hci_skb_pkt_type(skb) = pkt_type;
    -		hci_recv_frame(vbt->hdev, skb);
    +		min_hdr = sizeof(struct hci_iso_hdr);
     		break;
     	default:
     		kfree_skb(skb);
    -		break;
    +		return;
     	}
    +
    +	if (skb->len < min_hdr) {
    +		bt_dev_err_ratelimited(vbt->hdev,
    +				       "rx pkt_type 0x%02x payload %u < hdr %zu\n",
    +				       pkt_type, skb->len, min_hdr);
    +		kfree_skb(skb);
    +		return;
    +	}
    +
    +	hci_skb_pkt_type(skb) = pkt_type;
    +	hci_recv_frame(vbt->hdev, skb);
     }
     
     static void virtbt_rx_work(struct work_struct *work)
    -- 
    cgit 1.3-korg
    
    
    
  • drivers/bluetooth/virtio_bt.c+20 4 modified
    diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
    index 2c5c39356a1c81..140ab55c9fc5a9 100644
    --- a/drivers/bluetooth/virtio_bt.c
    +++ b/drivers/bluetooth/virtio_bt.c
    @@ -198,6 +198,7 @@ static int virtbt_shutdown_generic(struct hci_dev *hdev)
     
     static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     {
    +	size_t min_hdr;
     	__u8 pkt_type;
     
     	pkt_type = *((__u8 *) skb->data);
    @@ -205,16 +206,32 @@ static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
     
     	switch (pkt_type) {
     	case HCI_EVENT_PKT:
    +		min_hdr = sizeof(struct hci_event_hdr);
    +		break;
     	case HCI_ACLDATA_PKT:
    +		min_hdr = sizeof(struct hci_acl_hdr);
    +		break;
     	case HCI_SCODATA_PKT:
    +		min_hdr = sizeof(struct hci_sco_hdr);
    +		break;
     	case HCI_ISODATA_PKT:
    -		hci_skb_pkt_type(skb) = pkt_type;
    -		hci_recv_frame(vbt->hdev, skb);
    +		min_hdr = sizeof(struct hci_iso_hdr);
     		break;
     	default:
     		kfree_skb(skb);
    -		break;
    +		return;
     	}
    +
    +	if (skb->len < min_hdr) {
    +		bt_dev_err_ratelimited(vbt->hdev,
    +				       "rx pkt_type 0x%02x payload %u < hdr %zu\n",
    +				       pkt_type, skb->len, min_hdr);
    +		kfree_skb(skb);
    +		return;
    +	}
    +
    +	hci_skb_pkt_type(skb) = pkt_type;
    +	hci_recv_frame(vbt->hdev, skb);
     }
     
     static void virtbt_rx_work(struct work_struct *work)
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Missing length validation in virtbt_rx_handle() allows an untrusted virtio backend to supply an RX skb whose payload is smaller than the fixed HCI header for the declared packet type, leading to an out-of-bounds read of uninitialized buffer data."

Attack vector

An untrusted virtio backend (the device-side) can supply an RX buffer whose `used.len` is as small as 1 byte. After `virtbt_rx_handle()` strips the leading `pkt_type` byte, `skb->len` becomes 0. If that byte is `HCI_ACLDATA_PKT`, the ACL-vs-ISO classification fast-path in `hci_dev_classify_pkt_type()` dereferences `hci_acl_hdr(skb)->handle` when the HCI device has an active `CIS_LINK`, `BIS_LINK`, or `PA_LINK` connection, reading two bytes of uninitialized RX-buffer data. The same out-of-bounds read hazard exists for every accepted packet type because none of the switch cases checked `skb->len` against the minimum HCI header size [patch_id=2897943].

Affected code

The vulnerability is in the `virtbt_rx_handle()` function in `drivers/bluetooth/virtio_bt.c` [patch_id=2897943]. The function reads a `pkt_type` byte from the RX skb and forwards the remainder to `hci_recv_frame()` without checking that the remaining payload covers the fixed HCI header for that type. None of the switch cases for `HCI_EVENT_PKT`, `HCI_ACLDATA_PKT`, `HCI_SCODATA_PKT`, or `HCI_ISODATA_PKT` validated `skb->len` against the per-type minimum header size before handing the frame to the core.

What the fix does

The patch restructures the `switch` statement in `virtbt_rx_handle()` so that each accepted packet type first assigns the appropriate minimum header size (`sizeof(struct hci_event_hdr)` = 2, `sizeof(struct hci_acl_hdr)` = 4, `sizeof(struct hci_sco_hdr)` = 3, `sizeof(struct hci_iso_hdr)` = 4) to a local `min_hdr` variable. After the switch, a new length check compares `skb->len` against `min_hdr`; if the payload is too short, the skb is dropped with a rate-limited error message. Only after passing this check does the code set `hci_skb_pkt_type()` and call `hci_recv_frame()`. This closes the out-of-bounds read by ensuring the HCI header is fully present before the core Bluetooth layer accesses it [patch_id=2897943].

Preconditions

  • configThe attacker must control or compromise the virtio backend (device-side) that supplies RX buffers to the guest driver.
  • configThe HCI device must have an active CIS_LINK, BIS_LINK, or PA_LINK connection to trigger the ACL-vs-ISO fast-path that dereferences the undersized header.
  • authNo authentication is required; the backend can inject arbitrary pkt_type bytes and payload lengths.

Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.