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.

Fixes: 160fbcf3bf ("Bluetooth: virtio_bt: Use skb_put to set length")
Cc: stable@vger.kernel.org
Cc: Soenke Huster <soenke.huster@eknoes.de>
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
Michael Bommarito 2026-04-21 13:08:45 -04:00 committed by Luiz Augusto von Dentz
parent 21bd244b6d
commit daf23014e5

View File

@ -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) static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
{ {
size_t min_hdr;
__u8 pkt_type; __u8 pkt_type;
pkt_type = *((__u8 *) skb->data); 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) { switch (pkt_type) {
case HCI_EVENT_PKT: case HCI_EVENT_PKT:
min_hdr = sizeof(struct hci_event_hdr);
break;
case HCI_ACLDATA_PKT: case HCI_ACLDATA_PKT:
min_hdr = sizeof(struct hci_acl_hdr);
break;
case HCI_SCODATA_PKT: case HCI_SCODATA_PKT:
min_hdr = sizeof(struct hci_sco_hdr);
break;
case HCI_ISODATA_PKT: case HCI_ISODATA_PKT:
hci_skb_pkt_type(skb) = pkt_type; min_hdr = sizeof(struct hci_iso_hdr);
hci_recv_frame(vbt->hdev, skb);
break; break;
default: default:
kfree_skb(skb); 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) static void virtbt_rx_work(struct work_struct *work)