# DF-0559 — hci_event_inquiry_result / rssi_result unbounded loops

## Verdict
**NOT TESTABLE at runtime (no Bluetooth hardware) — bug CONFIRMED at source
(certain).** Fix authored in `fix.diff`; compiled cleanly as part of a combined
`nativekernel` build (netbt.ko) carrying all four verified findings' fixes.

## Mechanism (source trace, every hop cited)

Two sibling handlers, both reached from `hci_event()` (`sys/netbt/hci_event.c`)
at `:189-190` (`HCI_EVENT_INQUIRY_RESULT`) and `:193-194`
(`HCI_EVENT_RSSI_RESULT`).

`hci_event_inquiry_result()` (`sys/netbt/hci_event.c:440-470`):
- `KKASSERT(m->m_pkthdr.len >= sizeof(ep))` (`:447`) — only the 1-byte
  `hci_inquiry_result_ep` header. Reads `ep.num_responses` (`uint8_t`, 0-255,
  attacker-controlled) (`:448-449`).
- `while(ep.num_responses--)` (`:454`) — per iteration the only guard is
  `KKASSERT(m->m_pkthdr.len >= sizeof(ir))` (`:455`) where `sizeof(ir)` = 15.
  A `KKASSERT` is an INVARIANTS-only trap, NOT a runtime bounds check: it
  panics instead of recovering. Each iteration consumes 15 bytes
  (`m_copydata`+`m_adj`, `:456-457`).

`hci_event_rssi_result()` (`sys/netbt/hci_event.c:477-507`):
- Identical shape: `KKASSERT(... sizeof(ep))` (`:484`), reads
  `ep.num_responses` (`:485`), `while(ep.num_responses--)` (`:491`) with
  `KKASSERT(m->m_pkthdr.len >= sizeof(rr))` (`:492`) where `sizeof(rr)` = 14.

A controller (or injected HCI event) sending `num_responses = N` but fewer than
`N*15` (resp. `N*14`) packed records makes the loop's `KKASSERT` trip on the
2nd+ iteration when the chain runs out → **panic** (INVARIANTS ON in
`X86_64_GENERIC`, the production default). On a non-INVARIANTS build,
`m_copydata` on an exhausted chain derefs NULL → **panic**. Either way it is a
remote unauthenticated Bluetooth DoS. `hci_event_hdr_t.length` (`:168`) is
never consulted.

INVARIANTS being ON means the per-iter `KKASSERT` *is* compiled in — the
reviewer's claim that this manifests as a panic on the default kernel is
correct (note: `nm ... | grep KASSERT == 0` is a known false negative because
`KKASSERT`/`KASSERT` expand to inline `panic()`, not named symbols).

## Why not reproduced live (the realistic constraint)
Same as DF-0558: the QEMU/KVM audit guest has **no Bluetooth controller**.
netbt is shipped only as `netbt.ko` (not in the kernel, not loaded) and no
`struct hci_unit` is ever created, so `hci_event()`/`hci_input()` are
unreachable. The bug is a genuine latent/net-radio path. The netbt-twin finding
DF-0542 (netgraph7 BT) is the same defect in the older stack.

## Fix (`fix.diff`)
For each handler: replace the per-iteration INVARIANTS-only `KKASSERT` with an
explicit **upfront** bounds check — `ep.num_responses * sizeof(ir/rr)` against
the remaining `m->m_pkthdr.len`, log + `return` on a malformed event — and drop
the now-redundant per-iter `KKASSERT`. Minimal, targeted, recovers gracefully
instead of panicking.

## PoC changes
`findings/poc/DF-0559/` was empty on arrival. This runner authored `fix.diff`,
`build.sh`, `run.sh`, `README.md`, `VERDICT.md`, `manifest.json`, `env.txt`.
