β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-0566

ACL reassembly want is uint16_t: overshoot wraps -> L2CAP RX stall + unbounded memory growth

Summary

want declared uint16_t(:421). Computed want=letoh16(want)+sizeof(l2cap_hdr_t)-got(:509) in mixed int/size_t then truncated 16 bits. When accumulated got exceeds claimed L2CAP frame size(l2cap_len+4) β€” happens when HCI ACL START overshoots L2CAP boundary or fragments overshoot β€” subtraction goes negative wraps to large uint16_t. if(want>0)return(:511) taken forever after: (1) completed frame stuck in hl_rxp never delivered L2CAP traffic stalled DoS; (2) every subsequent FRAGMENT m_cat onto hl_rxp no upper bound(:495-498) kernel memory grows unbounded M_BLUETOOTH. Trigger: malicious/buggy USB BT dongle or controller firmware defect inducible by remote peer. Fix: declare int want(signed 32-bit) gate on signed value, trim overshoot via m_adj reprocess trailing bytes as fresh START, enforce hard cap on hl_rxp growth.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0566 Β· 6 files
FileTypeDescriptionSize
fix.diff suggested-fix use int want_total for arithmetic, preserve 2-byte read 466 B view raw
env.txt environment uname (no BT modules loaded) 297 B view raw
VERDICT.md verdict source-trace confirmation + unreachability note 3.4 KB ↓ raw
README.md readme bug description and reachability 2.5 KB ↓ raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme bug description and reachability
↓ download raw

DF-0566 β€” Bluetooth ACL reassembly uint16_t want overflow

Bug

hci_acl_recv (sys/netbt/hci_link.c:417) accumulates ACL fragments until a complete L2CAP frame is ready. The completion test at line 509 is:

uint16_t want;            /* line 421 -- 16-bit type */
...
m_copydata(m, 0, sizeof(want), &want);
want = letoh16(want) + sizeof(l2cap_hdr_t) - got;   /* line 509 */

if (want > 0)              /* line 511 */
    return;
...
if (want == 0) {           /* line 516 */
    l2cap_recv_frame(m, link);
    return;
}

The right-hand side is computed in size_t (because of sizeof(l2cap_hdr_t)), then truncated to uint16_t on assignment. When an HCI ACL START frame or accumulated fragments overshoot the claimed L2CAP length (got > letoh16(want) + 4), the subtraction underflows:

  • Arithmetic in size_t: huge 64-bit value
  • Truncated to uint16_t: 16-bit pattern (e.g. 0xFF9C for a -100 overshoot)
  • want > 0 is now true β†’ return without delivering or freeing the frame

Consequences (DoS, no memory corruption since want is just a local variable): 1. The half-completed frame is stuck in link->hl_rxp indefinitely; no L2CAP traffic for that handle is ever delivered. 2. Every subsequent FRAGMENT is m_cat'd onto hl_rxp (line 496) without bound, growing it indefinitely β†’ unbounded kernel memory consumption.

The opposite-direction bug (got MUCH larger than claimed L2CAP length due to malicious HCI input) is the more interesting primitive but is gated behind actual Bluetooth hardware.

Reachability on this guest

The trigger path requires an HCI ACL packet to reach hci_acl_recv. That function is only called from hci_unit.c:382 (the HCI unit's RX queue, drained by the Bluetooth hardware driver). There is no virtual Bluetooth device on DragonFlyBSD and no in-kernel path from userspace to hci_acl_recv β€” HCI sockets (BTPROTO_HCI) bind to an existing unit and exchange control commands, they do not inject RX packets.

The guest has no Bluetooth hardware (ng_ubt / bt3c / etc. not loaded; no USB BT dongle). Therefore this bug cannot be reproduced at runtime on this guest. The bug is real and traced line-by-line into the source (see VERDICT.md); it is a latent defect that would manifest on any DFly system with a Bluetooth HCI device under adversarial RF/peer input.

Fix

Use a wider signed type for the arithmetic (the L2CAP length field read stays 16-bit, but the comparison value is computed in int). See fix.diff. The fix preserves the 2-byte m_copydata and fixes the truncation.

VERDICT.md verdict source-trace confirmation + unreachability note
↓ download raw

DF-0566 β€” Verdict: NOT TESTABLE (no Bluetooth hardware)

Verdict

CONFIRMED by source trace; NOT TESTABLE at runtime on this guest (no Bluetooth HCI device or virtual BT path; the only caller of hci_acl_recv is the HCI unit RX queue fed by a BT driver).

Bug confirmation (source-only)

  • sys/netbt/hci_link.c:421 β€” uint16_t want; (16-bit type)
  • sys/netbt/hci_link.c:508 β€” m_copydata(m, 0, sizeof(want), &want); reads 2 bytes (the L2CAP length field). Correct.
  • sys/netbt/hci_link.c:509 β€” `want = letoh16(want) + sizeof(l2cap_hdr_t)
  • got;arithmetic in size_t (becausesizeof` returns size_t), truncated to uint16_t on assignment.
  • sys/netbt/l2cap.h:219-220 β€” l2cap_hdr_t is { uint16_t length; uint16_t dcid; } (4 bytes packed). So sizeof(l2cap_hdr_t) == 4.
  • sys/netbt/hci_link.c:511 β€” if (want > 0) return; takes the "incomplete" branch forever once want has wrapped, because the truncated value is non-zero.
  • sys/netbt/hci_link.c:516 β€” if (want == 0) deliver branch never taken.
  • sys/netbt/hci_link.c:495-498 β€” fragment branch: `got = m->m_pkthdr.len
  • link->hl_rxp->m_pkthdr.len; m_cat(...); m->m_pkthdr.len = got;` β€” subsequent fragments pile onto hl_rxp without bound.

The reviewer's analysis is correct. The truncation math is real.

Trigger unreachability on this guest

  • hci_acl_recv is called ONLY from sys/netbt/hci_unit.c:382 which dequeues from unit->hci_aclrxq.
  • The RX queue is fed by Bluetooth HCI drivers (e.g. ng_ubt, bt3c). None are loaded on this guest and no virtual BT device exists in DFly.
  • The BTPROTO_HCI socket family (sys/netbt/hci_socket.c) binds to an existing unit; it does NOT inject RX packets.
  • Confirmed by kldstat: only ehci/xhci USB host controllers, no BT modules. No BT hardware in QEMU config.

Therefore: this is a latent bug. To exercise it on a DFly system requires either: - A physical USB BT dongle plugged in, with a malicious peer that sends ACL packets overshooting the L2CAP length, OR - A kernel-module test harness that allocates a struct hci_unit, synthesizes ACL mbufs, and calls hci_acl_recv directly.

The latter would be a developer-only characterization (the kldload-based test harness is explicitly OUT OF SCOPE for escalation per the bright-line rule, but is mentioned here as the concrete next step for a maintainer wanting to exercise the path).

Impact ceiling

  • Per-link DoS: L2CAP traffic stalls; hl_rxp grows unbounded.
  • No memory corruption primitive (the truncated want is a local; the only effect is wrong control flow + unbounded m_cat).
  • Affects only systems with active Bluetooth HCI (small subset of DFly deployments).

Fix validation

Not testable at runtime (no BT hardware). Validated by: 1. Source line-by-line trace (above). 2. fix.diff applies cleanly with patch -p1 --dry-run against sys/netbt/hci_link.c (single hunk, line 509).

Kernel references

Fix verification

not_testable

compile validated

see evidence pack

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. BT ACL reassembly want uint16_t overshoot wraps -> stuck forever + m_cat pile-on. No BT HW.