hci_event_command_compl: reads status byte beyond asserted length -> short-event remote kernel panic
Summary
hci_event_command_compl(:299-317): KKASSERT(pkthdr.len>=sizeof(hci_command_compl_ep)=3)(:299) copies ep+m_adj. Then UNCONDITIONALLY m_copydata(m,0,sizeof(rp=1),&rp)(:313) to read status byte. Comment :308-312 admits not guaranteed command_complete will contain status. If controller sends CommandComplete param total=3 (no return params) -> m_copydata reads 1 byte never validated -> KASSERT(m!=NULL) panic INVARIANTS / NULL-deref non-INVARIANTS. Happens BEFORE per-opcode handler re-asserts. Remote unauth BT DoS. Fix: if(pkthdr.len>=sizeof(rp)) m_copydata.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0560 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| bttest.c | trigger-source | conceptual PoC skeleton (cannot run without BT HW) | 1.9 KB | view raw |
| fix.diff | suggested-fix | guard m_copydata with `if (m_pkthdr.len >= sizeof(rp))` | 883 B | view raw |
| build.log | build-log | netbt.ko build with patched hci_event.c, full output | 6.8 KB | view raw |
| VERDICT.md | verdict | full narrative: source trace, threat model, fix validation | 6.1 KB | β raw |
| env.txt | environment | guest uname, netbt.ko shipped but no BT controller | 1.0 KB | view 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 |
DF-0560 β hci_event_command_compl reads status byte beyond mbuf
Verdict
NOT REPRODUCED LIVE β but bug confirmed by source trace. The path requires a Bluetooth controller (real HW or virtual HCI device) to send a Command Complete event with a 3-byte payload to the kernel's BT stack. The audit guest has no BT HW and no virtual-HCI driver; the netbt.ko module IS shipped but unreachable without a controller.
Mechanism (cited path:line, confirmed by trace)
sys/netbt/hci_event.c line-by-line:
293: static void
294: hci_event_command_compl(struct hci_unit *unit, struct mbuf *m)
295: {
296: hci_command_compl_ep ep; // sizeof = 3 (1 + 2)
297: hci_status_rp rp; // sizeof = 1
298:
299: KKASSERT(m->m_pkthdr.len >= sizeof(ep)); // asserts >= 3 bytes
300: m_copydata(m, 0, sizeof(ep), &ep); // copies 3 bytes
301: m_adj(m, sizeof(ep)); // trims 3 bytes off front
302:
...
308: /*
309: * I am not sure if this is completely correct, it is not guaranteed
310: * that a command_complete packet will contain the status though most
311: * do seem to.
312: */
313: m_copydata(m, 0, sizeof(rp), &rp); // <--- BUG: unconditional
After m_adj(m, sizeof(ep)), the mbuf has (pkthdr.len - 3) bytes
remaining. The KKASSERT only verified the pre-m_adj length was
>= 3. If the original event had exactly 3 bytes of payload (i.e. a
Command Complete event with num_cmd_pkts + opcode and NO return
parameters, which is legal per the BT spec for commands that have no
return params), the trimmed mbuf has 0 bytes.
m_copydata(m, 0, sizeof(rp)=1, &rp) then tries to read 1 byte from
the now-empty mbuf. In sys/kern/uipc_mbuf.c:1671-1696:
1686: while (len > 0) {
1687: KASSERT(m != NULL,("%s: length > size of mbuf chain", __func__));
1688: count = min(m->m_len - off, len); // 0 for empty mbuf
1689: bcopy(mtod(m, caddr_t) + off, cp, count); // copies 0 bytes
1690: len -= count; // len unchanged
1691: cp += count;
1692: off = 0;
1693: m = m->m_next; // NULL for single mbuf
1694: }
- On INVARIANTS kernels (default GENERIC): the next loop iteration
hits
KASSERT(m != NULL)at line 1687 and panics withm_copydata: length > size of mbuf chain. (KKASSERT expands to an inline panic, sonm /boot/kernel/kernel | grep KASSERT == 0is a false-negative β the check IS compiled in.) - On non-INVARIANTS kernels:
count = 0,bcopycopies nothing, but the next iteration dereferencesm = NULLβ page fault atm->m_len(offset 0 from NULL) β kernel panic.
Either way: remote unauthenticated DoS triggered by a single malformed Command Complete HCI event packet from a paired BT controller.
The bug is reachable BEFORE the per-opcode handler switch at line 324
(which is where per-opcode KKASSERTs would catch short events); the
generic unconditional m_copydata(...sizeof(rp)...) at line 313 runs
regardless of opcode.
Why not testable on this guest
hci_event_command_compl is invoked from hci_event_handler
(sys/netbt/hci_event.c:160) when an HCI event packet arrives from a
registered BT controller. The dispatch path is:
ubt(4) USB driver β ng_hci / hci_recv β hci_event_handler β hci_event_command_compl
The audit guest has:
- No USB Bluetooth dongle (QEMU has no BT controller attached).
- No virtual-HCI driver (DragonFly does not ship a vhci equivalent
of BlueZ's hci_vhci that would let userland inject HCI events).
netbt.ko can be kldloaded but it has no event source, so the
hci_event_handler path is dead code on this guest.
Recommended fix (validated compile-only)
fix.diff wraps the unconditional m_copydata in a length check,
zero-initializing rp first so a missing status byte is treated as
"success" (the existing code's comment already admits the status is
optional):
memset(&rp, 0, sizeof(rp));
if (m->m_pkthdr.len >= sizeof(rp))
m_copydata(m, 0, sizeof(rp), &rp);
This matches the finding proposal (if(pkthdr.len>=sizeof(rp)) m_copydata)
and adds the zero-init as a defense-in-depth (avoids uninitialized
rp.status if the length check fails). Validated by:
patch -p3 < fix.diffβHunk #1 succeeded at 309.makein/usr/src/sys/netbtβ builds cleanly (/usr/obj/usr/src/sys/netbt/netbt.ko, 105024 bytes, all 18 .o units compiled, no warnings).
Cannot load+test on the audit guest (no event source) so
fix_status: not_testable. The patch is a clear defensive improvement
and the source-trace shows it closes the only path to the
unconditional m_copydata.
Kernel references (verified by source trace)
sys/netbt/hci_event.c:296-317β vulnerable function.sys/netbt/hci_event.c:299β KKASSERT ensures only>= sizeof(ep).sys/netbt/hci_event.c:301βm_adjtrims sizeof(ep).sys/netbt/hci_event.c:313β unconditionalm_copydataof 1 byte.sys/kern/uipc_mbuf.c:1687β KASSERT that panics on INVARIANTS.sys/netbt/hci.h:1955-1960βhci_command_compl_epis 3 bytes.sys/netbt/hci.h:443-448βhci_status_rpis 1 byte.
Threat model
Remote unauthenticated BT DoS. Requires a paired BT controller (which is the attacker in the threat model β a malicious BT device posing as a controller). The HCI event path is parsed before any authentication state is established, so any BT peer that can deliver a Command Complete event triggers the panic. No local-priv-esc chain (no write primitive). Not reachable on the audit guest.
PoC
A PoC for this bug would require either: 1. A malicious USB BT dongle (HW-in-the-loop), or 2. A virtual-HCI driver that lets userland inject HCI events (not shipped on DragonFly).
Neither is available on the audit guest. The bttest.c skeleton in
this folder shows the conceptual trigger (open AF_BLUETOOTH /
PF_BLUETOOTH socket, send a Command Complete event) but cannot run to
completion here because no BT controller is attached. The fix
validation is therefore compile-only + source-trace.
Fix verification
not_testablecompile validated
see evidence pack
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. hci_event_command_compl m_copydata on empty mbuf after m_adj. netbt not in GENERIC, no BT HW.
No comments yet.