public inbox for linux-bluetooth@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Bluetooth: virtio_bt: clamp rx length before skb_put
@ 2026-04-18  0:01 Michael Bommarito
  2026-04-18  1:56 ` bluez.test.bot
  2026-04-20 19:17 ` [PATCH] " Luiz Augusto von Dentz
  0 siblings, 2 replies; 10+ messages in thread
From: Michael Bommarito @ 2026-04-18  0:01 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, linux-bluetooth
  Cc: linux-kernel, Soenke Huster, Michael S . Tsirkin, virtualization

virtbt_rx_work() calls skb_put(skb, len) where len comes directly
from virtqueue_get_buf() with no validation against the skb we
posted.  The RX skb is allocated as alloc_skb(1000) in
virtbt_add_inbuf().  A malicious or buggy virtio-bt backend that
reports used.len larger than the skb's tailroom causes skb_put() to
call skb_over_panic() in net/core/skbuff.c, which triggers
BUG() and panics the guest.

Reproduced on a QEMU 9.0 whose virtio-bt backend reports
used.len = 4096 into a 1000-byte rx skb:

  skbuff: skb_over_panic: text:ffffffff83958e84 len:4096 put:4096
      head:ffff88800c071000 data:ffff88800c071000 tail:0x1000
      end:0x6c0 dev:<NULL>
  ------------[ cut here ]------------
  kernel BUG at net/core/skbuff.c:214!
  Call Trace:
   skb_panic+0x160/0x162
   skb_put.cold+0x31/0x31
   virtbt_rx_work+0x94/0x250
   process_one_work+0x80d/0x1510
   worker_thread+0x4af/0xd20
   kthread+0x2cc/0x3a0

Reject any len that exceeds skb_tailroom().  Drop the skb on the
error path; virtbt_add_inbuf() reposts a fresh one for the next
iteration.  With the check in place the same harness runs without
BUG(); the driver logs "rx reply len %u exceeds skb tailroom %u"
and the device keeps running.

Same class of bug as commit c04db81cd028 ("net/9p: Fix buffer overflow in USB transport layer"),
which hardened the USB 9p transport against unchecked device-reported length.

Fixes: 160fbcf3bfb9 ("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
---
 drivers/bluetooth/virtio_bt.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
index 76d61af8a275..157e68b6e75f 100644
--- a/drivers/bluetooth/virtio_bt.c
+++ b/drivers/bluetooth/virtio_bt.c
@@ -227,8 +227,15 @@ static void virtbt_rx_work(struct work_struct *work)
 	if (!skb)
 		return;
 
-	skb_put(skb, len);
-	virtbt_rx_handle(vbt, skb);
+	if (len > skb_tailroom(skb)) {
+		bt_dev_err(vbt->hdev,
+			   "rx reply len %u exceeds skb tailroom %u\n",
+			   len, skb_tailroom(skb));
+		kfree_skb(skb);
+	} else {
+		skb_put(skb, len);
+		virtbt_rx_handle(vbt, skb);
+	}
 
 	if (virtbt_add_inbuf(vbt) < 0)
 		return;
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* RE: Bluetooth: virtio_bt: clamp rx length before skb_put
  2026-04-18  0:01 [PATCH] Bluetooth: virtio_bt: clamp rx length before skb_put Michael Bommarito
@ 2026-04-18  1:56 ` bluez.test.bot
  2026-04-20 19:17 ` [PATCH] " Luiz Augusto von Dentz
  1 sibling, 0 replies; 10+ messages in thread
From: bluez.test.bot @ 2026-04-18  1:56 UTC (permalink / raw)
  To: linux-bluetooth, michael.bommarito

[-- Attachment #1: Type: text/plain, Size: 2496 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1082712

---Test result---

Test Summary:
CheckPatch                    FAIL      0.73 seconds
GitLint                       FAIL      0.32 seconds
SubjectPrefix                 PASS      0.13 seconds
BuildKernel                   PASS      26.04 seconds
CheckAllWarning               PASS      28.12 seconds
CheckSparse                   PASS      27.11 seconds
BuildKernel32                 PASS      25.25 seconds
TestRunnerSetup               PASS      561.56 seconds
IncrementalBuild              PASS      23.96 seconds

Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
Bluetooth: virtio_bt: clamp rx length before skb_put
WARNING: Prefer a maximum 75 chars per line (possible unwrapped commit description?)
#133: 
Same class of bug as commit c04db81cd028 ("net/9p: Fix buffer overflow in USB transport layer"),

WARNING: Non-standard signature: Assisted-by:
#140: 
Assisted-by: Claude:claude-opus-4-7

ERROR: Unrecognized email address: 'Claude:claude-opus-4-7'
#140: 
Assisted-by: Claude:claude-opus-4-7

total: 1 errors, 2 warnings, 17 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/patch/14529066.patch has style problems, please review.

NOTE: Ignored message types: UNKNOWN_COMMIT_ID

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.


##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
Bluetooth: virtio_bt: clamp rx length before skb_put

WARNING: I3 - ignore-body-lines: gitlint will be switching from using Python regex 'match' (match beginning) to 'search' (match anywhere) semantics. Please review your ignore-body-lines.regex option accordingly. To remove this warning, set general.regex-style-search=True. More details: https://jorisroovers.github.io/gitlint/configuration/#regex-style-search
33: B1 Line exceeds max length (96>80): "Same class of bug as commit c04db81cd028 ("net/9p: Fix buffer overflow in USB transport layer"),"


https://github.com/bluez/bluetooth-next/pull/101

---
Regards,
Linux Bluetooth


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] Bluetooth: virtio_bt: clamp rx length before skb_put
  2026-04-18  0:01 [PATCH] Bluetooth: virtio_bt: clamp rx length before skb_put Michael Bommarito
  2026-04-18  1:56 ` bluez.test.bot
@ 2026-04-20 19:17 ` Luiz Augusto von Dentz
  2026-04-21 15:16   ` [PATCH v2] " Michael Bommarito
  2026-04-21 15:19   ` [PATCH] " Michael Bommarito
  1 sibling, 2 replies; 10+ messages in thread
From: Luiz Augusto von Dentz @ 2026-04-20 19:17 UTC (permalink / raw)
  To: Michael Bommarito
  Cc: Marcel Holtmann, linux-bluetooth, linux-kernel, Soenke Huster,
	Michael S . Tsirkin, virtualization

Hi Michael,

On Fri, Apr 17, 2026 at 8:01 PM Michael Bommarito
<michael.bommarito@gmail.com> wrote:
>
> virtbt_rx_work() calls skb_put(skb, len) where len comes directly
> from virtqueue_get_buf() with no validation against the skb we
> posted.  The RX skb is allocated as alloc_skb(1000) in
> virtbt_add_inbuf().  A malicious or buggy virtio-bt backend that
> reports used.len larger than the skb's tailroom causes skb_put() to
> call skb_over_panic() in net/core/skbuff.c, which triggers
> BUG() and panics the guest.
>
> Reproduced on a QEMU 9.0 whose virtio-bt backend reports
> used.len = 4096 into a 1000-byte rx skb:
>
>   skbuff: skb_over_panic: text:ffffffff83958e84 len:4096 put:4096
>       head:ffff88800c071000 data:ffff88800c071000 tail:0x1000
>       end:0x6c0 dev:<NULL>
>   ------------[ cut here ]------------
>   kernel BUG at net/core/skbuff.c:214!
>   Call Trace:
>    skb_panic+0x160/0x162
>    skb_put.cold+0x31/0x31
>    virtbt_rx_work+0x94/0x250
>    process_one_work+0x80d/0x1510
>    worker_thread+0x4af/0xd20
>    kthread+0x2cc/0x3a0
>
> Reject any len that exceeds skb_tailroom().  Drop the skb on the
> error path; virtbt_add_inbuf() reposts a fresh one for the next
> iteration.  With the check in place the same harness runs without
> BUG(); the driver logs "rx reply len %u exceeds skb tailroom %u"
> and the device keeps running.
>
> Same class of bug as commit c04db81cd028 ("net/9p: Fix buffer overflow in USB transport layer"),
> which hardened the USB 9p transport against unchecked device-reported length.
>
> Fixes: 160fbcf3bfb9 ("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
> ---
>  drivers/bluetooth/virtio_bt.c | 11 +++++++++--
>  1 file changed, 9 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
> index 76d61af8a275..157e68b6e75f 100644
> --- a/drivers/bluetooth/virtio_bt.c
> +++ b/drivers/bluetooth/virtio_bt.c
> @@ -227,8 +227,15 @@ static void virtbt_rx_work(struct work_struct *work)
>         if (!skb)
>                 return;
>
> -       skb_put(skb, len);
> -       virtbt_rx_handle(vbt, skb);
> +       if (len > skb_tailroom(skb)) {
> +               bt_dev_err(vbt->hdev,
> +                          "rx reply len %u exceeds skb tailroom %u\n",
> +                          len, skb_tailroom(skb));
> +               kfree_skb(skb);
> +       } else {
> +               skb_put(skb, len);
> +               virtbt_rx_handle(vbt, skb);
> +       }
>
>         if (virtbt_add_inbuf(vbt) < 0)
>                 return;
> --
> 2.53.0

https://sashiko.dev/#/patchset/20260418000138.1848813-1-michael.bommarito%40gmail.com

All seem like valid comments to me, first one is odd to me thought,
never would have though that skb_tailroom wouldn't be enough to check
if using `skb_put` is safe.

-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v2] Bluetooth: virtio_bt: clamp rx length before skb_put
  2026-04-20 19:17 ` [PATCH] " Luiz Augusto von Dentz
@ 2026-04-21 15:16   ` Michael Bommarito
  2026-04-21 16:20     ` [v2] " bluez.test.bot
  2026-04-21 15:19   ` [PATCH] " Michael Bommarito
  1 sibling, 1 reply; 10+ messages in thread
From: Michael Bommarito @ 2026-04-21 15:16 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, linux-bluetooth
  Cc: linux-kernel, Soenke Huster, Michael S . Tsirkin, virtualization

virtbt_rx_work() calls skb_put(skb, len) where len comes directly
from virtqueue_get_buf() with no validation against the buffer we
posted to the device. The RX skb is allocated in virtbt_add_inbuf()
and exposed to virtio as exactly 1000 bytes via sg_init_one().

Checking len against skb_tailroom(skb) is not sufficient because
alloc_skb() can leave more tailroom than the 1000 bytes actually
handed to the device. A malicious or buggy backend can therefore
report used.len between 1001 and skb_tailroom(skb), causing skb_put()
to include uninitialized kernel heap bytes that were never written by
the device.

The same path also accepts len == 0, in which case skb_put(skb, 0)
leaves the skb empty but virtbt_rx_handle() still reads the pkt_type
byte from skb->data, consuming uninitialized memory.

A single-byte completion also 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.

Close all three paths:

  - define VIRTBT_RX_BUF_SIZE once, reuse it in alloc_skb() and
    sg_init_one() and as the upper bound on used.len, so the gate
    matches the buffer actually exposed to the device
  - reject used.len == 0 before virtbt_rx_handle() reads pkt_type
  - after stripping pkt_type, require skb->len to cover the fixed
    header size for the selected type (event/ACL/SCO/ISO) before
    calling hci_recv_frame(); drop ratelimited otherwise

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

Same class of bug as commit c04db81cd028 ("net/9p: Fix buffer
overflow in USB transport layer"), which hardened the USB 9p
transport against unchecked device-reported length.

Fixes: 160fbcf3bfb9 ("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
---
Changes in v2:
- validate used.len against VIRTBT_RX_BUF_SIZE (the 1000 bytes
  actually exposed to the device via sg_init_one()) rather than
  skb_tailroom(), which can exceed 1000 due to slab padding
- reject used.len == 0 before virtbt_rx_handle() reads the pkt_type
  byte, so zero-length completions can no longer pull an empty skb
  into hci_recv_frame()
- in virtbt_rx_handle(), require skb->len to cover the fixed HCI
  header size for the selected pkt_type (event 2, ACL 4, SCO 3,
  ISO 4) before calling hci_recv_frame(); this prevents a one-byte
  HCI_ACLDATA_PKT completion from reaching
  hci_dev_classify_pkt_type() and dereferencing hci_acl_hdr(skb)
  over uninitialized RX buffer data when CIS/BIS/PA connections
  are present
- switch the error log to bt_dev_err_ratelimited() because the
  length and pkt_type values come from an untrusted backend that
  can otherwise flood the kernel log

 drivers/bluetooth/virtio_bt.c | 39 ++++++++++++++++++++++++++++-------
 1 file changed, 32 insertions(+), 7 deletions(-)

diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
index 76d61af8a275..140ab55c9fc5 100644
--- a/drivers/bluetooth/virtio_bt.c
+++ b/drivers/bluetooth/virtio_bt.c
@@ -12,6 +12,7 @@
 #include <net/bluetooth/hci_core.h>
 
 #define VERSION "0.1"
+#define VIRTBT_RX_BUF_SIZE 1000
 
 enum {
 	VIRTBT_VQ_TX,
@@ -33,11 +34,11 @@ static int virtbt_add_inbuf(struct virtio_bluetooth *vbt)
 	struct sk_buff *skb;
 	int err;
 
-	skb = alloc_skb(1000, GFP_KERNEL);
+	skb = alloc_skb(VIRTBT_RX_BUF_SIZE, GFP_KERNEL);
 	if (!skb)
 		return -ENOMEM;
 
-	sg_init_one(sg, skb->data, 1000);
+	sg_init_one(sg, skb->data, VIRTBT_RX_BUF_SIZE);
 
 	err = virtqueue_add_inbuf(vq, sg, 1, skb, GFP_KERNEL);
 	if (err < 0) {
@@ -197,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);
@@ -204,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)
@@ -227,8 +245,15 @@ static void virtbt_rx_work(struct work_struct *work)
 	if (!skb)
 		return;
 
-	skb_put(skb, len);
-	virtbt_rx_handle(vbt, skb);
+	if (!len || len > VIRTBT_RX_BUF_SIZE) {
+		bt_dev_err_ratelimited(vbt->hdev,
+				       "rx reply len %u outside [1, %u]\n",
+				       len, VIRTBT_RX_BUF_SIZE);
+		kfree_skb(skb);
+	} else {
+		skb_put(skb, len);
+		virtbt_rx_handle(vbt, skb);
+	}
 
 	if (virtbt_add_inbuf(vbt) < 0)
 		return;
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH] Bluetooth: virtio_bt: clamp rx length before skb_put
  2026-04-20 19:17 ` [PATCH] " Luiz Augusto von Dentz
  2026-04-21 15:16   ` [PATCH v2] " Michael Bommarito
@ 2026-04-21 15:19   ` Michael Bommarito
  2026-04-21 15:50     ` Luiz Augusto von Dentz
  1 sibling, 1 reply; 10+ messages in thread
From: Michael Bommarito @ 2026-04-21 15:19 UTC (permalink / raw)
  To: Luiz Augusto von Dentz
  Cc: Marcel Holtmann, linux-bluetooth, linux-kernel, Soenke Huster,
	Michael S . Tsirkin, virtualization

On Mon, Apr 20, 2026 at 3:17 PM Luiz Augusto von Dentz
<luiz.dentz@gmail.com> wrote:
> All seem like valid comments to me, first one is odd to me thought,
> never would have though that skb_tailroom wouldn't be enough to check
> if using `skb_put` is safe.

Thanks, those were definitely gnarly.  Went back in with
cscope/libclang and found another spot to guard too.

Let me know if you want me to re-organize/separate these into a
patchset instead of one bigger patch.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] Bluetooth: virtio_bt: clamp rx length before skb_put
  2026-04-21 15:19   ` [PATCH] " Michael Bommarito
@ 2026-04-21 15:50     ` Luiz Augusto von Dentz
  2026-04-21 17:08       ` [PATCH v3 0/2] Bluetooth: virtio_bt: harden rx against untrusted backend Michael Bommarito
  0 siblings, 1 reply; 10+ messages in thread
From: Luiz Augusto von Dentz @ 2026-04-21 15:50 UTC (permalink / raw)
  To: Michael Bommarito
  Cc: Marcel Holtmann, linux-bluetooth, linux-kernel, Soenke Huster,
	Michael S . Tsirkin, virtualization

Hi Michael,

On Tue, Apr 21, 2026 at 11:19 AM Michael Bommarito
<michael.bommarito@gmail.com> wrote:
>
> On Mon, Apr 20, 2026 at 3:17 PM Luiz Augusto von Dentz
> <luiz.dentz@gmail.com> wrote:
> > All seem like valid comments to me, first one is odd to me thought,
> > never would have though that skb_tailroom wouldn't be enough to check
> > if using `skb_put` is safe.
>
> Thanks, those were definitely gnarly.  Went back in with
> cscope/libclang and found another spot to guard too.
>
> Let me know if you want me to re-organize/separate these into a
> patchset instead of one bigger patch.

Yeah, let's have it split into different patches, otherwise the commit
message gets convoluted trying to explain the what and why of each
change.


-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: [v2] Bluetooth: virtio_bt: clamp rx length before skb_put
  2026-04-21 15:16   ` [PATCH v2] " Michael Bommarito
@ 2026-04-21 16:20     ` bluez.test.bot
  0 siblings, 0 replies; 10+ messages in thread
From: bluez.test.bot @ 2026-04-21 16:20 UTC (permalink / raw)
  To: linux-bluetooth, michael.bommarito

[-- Attachment #1: Type: text/plain, Size: 1680 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1083916

---Test result---

Test Summary:
CheckPatch                    FAIL      0.58 seconds
GitLint                       PASS      0.20 seconds
SubjectPrefix                 PASS      0.06 seconds
BuildKernel                   PASS      26.45 seconds
CheckAllWarning               PASS      28.79 seconds
CheckSparse                   PASS      27.55 seconds
BuildKernel32                 PASS      25.63 seconds
TestRunnerSetup               PASS      570.69 seconds
IncrementalBuild              PASS      25.48 seconds

Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[v2] Bluetooth: virtio_bt: clamp rx length before skb_put
WARNING: Non-standard signature: Assisted-by:
#156: 
Assisted-by: Claude:claude-opus-4-7

ERROR: Unrecognized email address: 'Claude:claude-opus-4-7'
#156: 
Assisted-by: Claude:claude-opus-4-7

total: 1 errors, 1 warnings, 79 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/patch/14532726.patch has style problems, please review.

NOTE: Ignored message types: UNKNOWN_COMMIT_ID

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.




https://github.com/bluez/bluetooth-next/pull/112

---
Regards,
Linux Bluetooth


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v3 0/2] Bluetooth: virtio_bt: harden rx against untrusted backend
  2026-04-21 15:50     ` Luiz Augusto von Dentz
@ 2026-04-21 17:08       ` Michael Bommarito
  2026-04-21 17:08         ` [PATCH v3 1/2] Bluetooth: virtio_bt: clamp rx length before skb_put Michael Bommarito
  2026-04-21 17:08         ` [PATCH v3 2/2] Bluetooth: virtio_bt: validate rx pkt_type header length Michael Bommarito
  0 siblings, 2 replies; 10+ messages in thread
From: Michael Bommarito @ 2026-04-21 17:08 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, linux-bluetooth
  Cc: linux-kernel, Soenke Huster, Michael S . Tsirkin, virtualization

Respin of the virtio_bt rx hardening patch, split per Luiz's
request on the v2 thread:

  https://lore.kernel.org/linux-bluetooth/20260421151659.3326690-1-michael.bommarito@gmail.com/

v2 bundled two independent hazards in one commit and the commit
message got convoluted trying to explain them together. This v3
keeps each hazard in its own patch so the rationale for each is
self-contained.

Patch 1/2 keeps the original v1/v2 concern: virtbt_rx_work() calls
skb_put() with a used.len the device controls, with no validation
against the 1000-byte buffer we actually exposed to virtio via
sg_init_one(). Checking against skb_tailroom() is not enough
because alloc_skb() can leave more tailroom than 1000 bytes due to
slab padding. len == 0 is also accepted, which lets an empty skb
reach virtbt_rx_handle() where the pkt_type byte is then read from
uninitialized memory. 1/2 defines VIRTBT_RX_BUF_SIZE, reuses it in
alloc_skb() and sg_init_one(), and gates virtbt_rx_work() on
[1, VIRTBT_RX_BUF_SIZE] with bt_dev_err_ratelimited() on the drop
path.

Patch 2/2 closes a residual short-payload hazard found during v2
pre-send review. After 1/2 restricts used.len to [1, 1000], a
one-byte completion still reaches hci_recv_frame() with skb->len
pulled to 0. If that byte was 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 shape
exists for every packet type because none of the switch cases in
virtbt_rx_handle() checked skb->len against the per-type minimum
HCI header. 2/2 requires 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.

Both patches carry the same Fixes: 160fbcf3bfb9 and Cc: stable@
as the v2 commit, because both hazards have existed since the
driver switched to skb_put() for used.len handling.

Fresh runtime repro of the original skb_over_panic on an unpatched
tree and a clean reject log with 1/2 applied are attached in the
evidence bundle referenced from the v2 thread; no runtime change
between v2 and v3 beyond the split (the final tree after 1/2+2/2
is byte-identical to the v2 commit).

Prior public versions of this patch on linux-bluetooth:

  v1: <20260418000138.1848813-1-michael.bommarito@gmail.com>
  v2: <20260421151659.3326690-1-michael.bommarito@gmail.com>

Michael Bommarito (2):
  Bluetooth: virtio_bt: clamp rx length before skb_put
  Bluetooth: virtio_bt: validate rx pkt_type header length

 drivers/bluetooth/virtio_bt.c | 39 ++++++++++++++++++++++++++++-------
 1 file changed, 32 insertions(+), 7 deletions(-)

-- 
2.53.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v3 1/2] Bluetooth: virtio_bt: clamp rx length before skb_put
  2026-04-21 17:08       ` [PATCH v3 0/2] Bluetooth: virtio_bt: harden rx against untrusted backend Michael Bommarito
@ 2026-04-21 17:08         ` Michael Bommarito
  2026-04-21 17:08         ` [PATCH v3 2/2] Bluetooth: virtio_bt: validate rx pkt_type header length Michael Bommarito
  1 sibling, 0 replies; 10+ messages in thread
From: Michael Bommarito @ 2026-04-21 17:08 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, linux-bluetooth
  Cc: linux-kernel, Soenke Huster, Michael S . Tsirkin, virtualization

virtbt_rx_work() calls skb_put(skb, len) where len comes directly
from virtqueue_get_buf() with no validation against the buffer we
posted to the device. The RX skb is allocated in virtbt_add_inbuf()
and exposed to virtio as exactly 1000 bytes via sg_init_one().

Checking len against skb_tailroom(skb) is not sufficient because
alloc_skb() can leave more tailroom than the 1000 bytes actually
handed to the device. A malicious or buggy backend can therefore
report used.len between 1001 and skb_tailroom(skb), causing skb_put()
to include uninitialized kernel heap bytes that were never written by
the device.

The same path also accepts len == 0, in which case skb_put(skb, 0)
leaves the skb empty but virtbt_rx_handle() still reads the pkt_type
byte from skb->data, consuming uninitialized memory.

Define VIRTBT_RX_BUF_SIZE once and reuse it in alloc_skb() and
sg_init_one(), and gate virtbt_rx_work() on that same constant so
the bound checked matches the buffer actually exposed to the device.
Reject used.len == 0 in the same gate so an empty completion can
no longer reach virtbt_rx_handle().

Use bt_dev_err_ratelimited() because the length value comes from an
untrusted backend that can otherwise flood the kernel log.

Same class of bug as commit c04db81cd028 ("net/9p: Fix buffer
overflow in USB transport layer"), which hardened the USB 9p
transport against unchecked device-reported length.

Fixes: 160fbcf3bfb9 ("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
---
Changes in v3:
- split off the per-pkt-type header-length check into its own
  patch (2/2) per Luiz's request; this patch now only covers the
  used.len vs buffer-size hazard and the len == 0 path

Changes in v2:
- validate used.len against VIRTBT_RX_BUF_SIZE (the 1000 bytes
  actually exposed to the device via sg_init_one()) rather than
  skb_tailroom(), which can exceed 1000 due to slab padding
- reject used.len == 0 before virtbt_rx_handle() reads the
  pkt_type byte, so zero-length completions can no longer pull an
  empty skb into hci_recv_frame()
- switch the error log to bt_dev_err_ratelimited() because the
  length value comes from an untrusted backend that can otherwise
  flood the kernel log

 drivers/bluetooth/virtio_bt.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
index 76d61af8a275..2c5c39356a1c 100644
--- a/drivers/bluetooth/virtio_bt.c
+++ b/drivers/bluetooth/virtio_bt.c
@@ -12,6 +12,7 @@
 #include <net/bluetooth/hci_core.h>
 
 #define VERSION "0.1"
+#define VIRTBT_RX_BUF_SIZE 1000
 
 enum {
 	VIRTBT_VQ_TX,
@@ -33,11 +34,11 @@ static int virtbt_add_inbuf(struct virtio_bluetooth *vbt)
 	struct sk_buff *skb;
 	int err;
 
-	skb = alloc_skb(1000, GFP_KERNEL);
+	skb = alloc_skb(VIRTBT_RX_BUF_SIZE, GFP_KERNEL);
 	if (!skb)
 		return -ENOMEM;
 
-	sg_init_one(sg, skb->data, 1000);
+	sg_init_one(sg, skb->data, VIRTBT_RX_BUF_SIZE);
 
 	err = virtqueue_add_inbuf(vq, sg, 1, skb, GFP_KERNEL);
 	if (err < 0) {
@@ -227,8 +228,15 @@ static void virtbt_rx_work(struct work_struct *work)
 	if (!skb)
 		return;
 
-	skb_put(skb, len);
-	virtbt_rx_handle(vbt, skb);
+	if (!len || len > VIRTBT_RX_BUF_SIZE) {
+		bt_dev_err_ratelimited(vbt->hdev,
+				       "rx reply len %u outside [1, %u]\n",
+				       len, VIRTBT_RX_BUF_SIZE);
+		kfree_skb(skb);
+	} else {
+		skb_put(skb, len);
+		virtbt_rx_handle(vbt, skb);
+	}
 
 	if (virtbt_add_inbuf(vbt) < 0)
 		return;
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v3 2/2] Bluetooth: virtio_bt: validate rx pkt_type header length
  2026-04-21 17:08       ` [PATCH v3 0/2] Bluetooth: virtio_bt: harden rx against untrusted backend Michael Bommarito
  2026-04-21 17:08         ` [PATCH v3 1/2] Bluetooth: virtio_bt: clamp rx length before skb_put Michael Bommarito
@ 2026-04-21 17:08         ` Michael Bommarito
  1 sibling, 0 replies; 10+ messages in thread
From: Michael Bommarito @ 2026-04-21 17:08 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, linux-bluetooth
  Cc: linux-kernel, Soenke Huster, Michael S . Tsirkin, virtualization

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: 160fbcf3bfb9 ("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
---
Changes in v3:
- new patch, split out of the v2 commit per Luiz's request on
  the v2 thread so the per-pkt-type header-length check can be
  reviewed on its own

Changes in v2:
- in virtbt_rx_handle(), require skb->len to cover the fixed HCI
  header size for the selected pkt_type (event 2, ACL 4, SCO 3,
  ISO 4) before calling hci_recv_frame(); this prevents a one-byte
  HCI_ACLDATA_PKT completion from reaching
  hci_dev_classify_pkt_type() and dereferencing hci_acl_hdr(skb)
  over uninitialized RX buffer data when CIS/BIS/PA connections
  are present
- switch the error log to bt_dev_err_ratelimited() because the
  length and pkt_type values come from an untrusted backend that
  can otherwise flood the kernel log

 drivers/bluetooth/virtio_bt.c | 23 ++++++++++++++++++++---
 1 file changed, 20 insertions(+), 3 deletions(-)

diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
index 2c5c39356a1c..140ab55c9fc5 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)
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-04-21 17:09 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-18  0:01 [PATCH] Bluetooth: virtio_bt: clamp rx length before skb_put Michael Bommarito
2026-04-18  1:56 ` bluez.test.bot
2026-04-20 19:17 ` [PATCH] " Luiz Augusto von Dentz
2026-04-21 15:16   ` [PATCH v2] " Michael Bommarito
2026-04-21 16:20     ` [v2] " bluez.test.bot
2026-04-21 15:19   ` [PATCH] " Michael Bommarito
2026-04-21 15:50     ` Luiz Augusto von Dentz
2026-04-21 17:08       ` [PATCH v3 0/2] Bluetooth: virtio_bt: harden rx against untrusted backend Michael Bommarito
2026-04-21 17:08         ` [PATCH v3 1/2] Bluetooth: virtio_bt: clamp rx length before skb_put Michael Bommarito
2026-04-21 17:08         ` [PATCH v3 2/2] Bluetooth: virtio_bt: validate rx pkt_type header length Michael Bommarito

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox