# DF-5736 — Inquiry/RSSI result events allocate `hci_memo` records without bound

**File:** `sys/netbt/hci_event.c` (pass 2) + `sys/netbt/hci_misc.c`
**Severity:** Medium — CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L (CWE-770)
**Verdict: REPRODUCED** (monotonic unbounded kernel-memory growth from events).

## Root cause

`hci_event_inquiry_result()` (`sys/netbt/hci_event.c:440-470`) and
`hci_event_rssi_result()` (`sys/netbt/hci_event.c:477-507`) call
`hci_memo_new()` for **every** response carried in the event
(`sys/netbt/hci_event.c:463`, `:500`), keyed by the response's `bdaddr` — a
value fully controlled by the reporting remote device / controller.
`hci_memo_new()` (`sys/netbt/hci_misc.c:145-173`) allocates a new
`struct hci_memo` (~48 bytes) per unique address with **no cap**, and expired
memos are reaped **only inside `hci_memo_find()` lookups** after
`hci_memo_expiry` (600 s) — there is no background sweep. An inquiring host
therefore accumulates kernel memory linearly with the number of (spoofable)
unique responder addresses, for at least 10 minutes each, unbounded.

A hostile *remote* Bluetooth device can manufacture inquiry responses with
random spoofed bdaddrs (inquiry response spoofing over RF); a hostile
controller/transport-injector can do the same at HCI-USB speed (bounded only
by the 20-event input queue backpressure).

## Build & run (inside the guest, as root)

```
cd /root/hcih && sh build.sh
kldload ./hci_event_harness.ko
sysctl hw.hcih_setup=1
sysctl hw.hcih_fire_memo=1     # 128 events x 16 unique bdaddrs (2048 responses)
```

## Expected result (baseline)

```
[harness] memos before: 0
[harness] memos after: 1436 (allocated 1436 for 2048 responses, ...)
[harness] memos before: 1436
[harness] memos after: 2848 ...
[harness] memos after: 3184 ...      <- monotonic growth, nothing reclaimed
```
(`vmstat -m`: Bluetooth 150K after three bursts; ~612 responses were dropped
by the production `hci_eventq_max`=20 backpressure — the growth rate is
limited only by that drain race.)

## Fix validation

`fix.diff` adds a per-unit cap (`hci_memo_max`, default 1024, sysctl-tunable)
counted in `hci_memo_new()`. On the patched build: three consecutive floods →
`memos after: 1024`, then `1024 (allocated 0 ...)` twice — growth stops at
the cap; guest alive.
