# DF-0543 — num_compl_pkts: unbounded loop reads past mbuf (netgraph7 HCI)

**File:** `sys/netgraph7/bluetooth/hci/ng_hci_evnt.c`
**Severity (fileed):** High
**Class:** Out-of-bounds read (mbuf chain over-read) → kernel panic
**Module:** `ng_hci` (netgraph7 bluetooth) — **NOT compiled, NOT shipped, NOT
loadable** on the default `X86_64_GENERIC` guest. (Same root cause class as
DF-0542: `ng_hci_process_event` never enforces `hdr->length`.)

## The claim (source-level, CONFIRMED)

`num_compl_pkts()` (`ng_hci_evnt.c:884`) pulls up only
`sizeof(ng_hci_num_compl_pkts_ep)` = **1 byte** (`num_con_handles`) and loops:

```c
for (; ep->num_con_handles > 0; ep->num_con_handles --) {   /* :891 */
    m_copydata(event, 0, sizeof(h), &h); m_adj(event, sizeof(h));   /* 2 bytes */
    m_copydata(event, 0, sizeof(p), &p); m_adj(event, sizeof(p));   /* 2 bytes */
    ...
}
```

Each iteration consumes **4 bytes** (`sizeof(h)=2 + sizeof(p)=2`; both are
`u_int16_t` declared at `:882`). The controller-supplied `num_con_handles`
(first body byte) is **never validated against the mbuf length** —
`ng_hci_process_event` (`:86`) logs `hdr->length` (`:100`) but does not enforce
it.

A 1-byte event body with `num_con_handles = 0xFF` (255) demands 1020 bytes but
the chain holds far less. Once `m_adj` drains the chain, `m_copydata()` walks
`m = m->m_next` to NULL and hits `KASSERT(m != NULL, "length > size of mbuf
chain")` at `sys/kern/uipc_mbuf.c:1687` → **panic (INVARIANTS/GENERIC)**;
non-INVARIANTS ⇒ NULL `m->m_len` deref ⇒ page-fault panic.

## Reproduction status

- **Source-level:** CONFIRMED (4-byte/iteration math and `m_copydata` KASSERT
  traced).
- **Live trigger:** **NOT possible on the default guest** — same as DF-0542:
  netgraph7 bluetooth is `optional netgraph7_bluetooth`, absent from
  `X86_64_GENERIC`, no `.ko` ships, `kldload ng_hci.ko` fails ("depends on
  ng_bluetooth - not available"), and reaching `num_compl_pkts()` needs a real
  HCI NUMBER_OF_COMPLETED_PACKETS event from a BT controller via
  `ng_ubt`/`ng_h4`. ⇒ **dead code / latent** here.

## Impact (realistic, conditional)

**Conditional on netgraph7 bluetooth being compiled in + a BT controller**:
remote-unauthenticated-packet → kernel panic (DoS). No info-leak path here
(the parsed `h`/`p` are looked up against existing connections and only a
counter is updated — `:903-919`; the over-read reaches the KASSERT/NULL-deref,
not a copyout). On the shipped default kernel this code is absent ⇒ **none**.

## Old netgraph BT?

`sys/netgraph/bluetooth/` does not exist in this tree; only netgraph7 carries
this parser. No second copy to fix.

## Fix

`fix.diff` — bound the loop by remaining mbuf length:

```c
for (; ep->num_con_handles > 0 &&
       event->m_pkthdr.len >= (sizeof(h) + sizeof(p));     /* 4 bytes/entry */
     ep->num_con_handles --) { ... }
```
git-apply clean; compiles (patched `ng_hci.ko` build rc=0, `build_hci.log`).

## Build

```
cd /usr/src/sys/netgraph7/bluetooth/hci && make     # -> ng_hci.ko (objdir), rc=0
```
Logs: `build_hci.log`, `build_result.txt`, `env.txt`.
