# DF-0458 — ng_l2cap L2CA_Ping heap over-read via unchecked echo_size

## Verdict
**REPRODUCED (source-confirmed)** — the bug is real but not live-triggerable on this guest (no Bluetooth hardware / BT connection).

## Mechanism
`ng_l2cap_l2ca_ping_req` (`sys/netgraph7/bluetooth/l2cap/ng_l2cap_ulpi.c:1297-1358`) handles the L2CA_Ping upper-layer protocol request. The only length checks are:
- Line 1306: `arglen < sizeof(*ip)` (sizeof = 8) — rejects messages shorter than the 8-byte header.
- Line 1316: `echo_size > NG_L2CAP_MAX_ECHO_SIZE` (65531) — rejects oversized echo requests.

There is **NO check** that `arglen >= sizeof(*ip) + echo_size`. At lines 1357-1358 the macro `_ng_l2cap_echo_req` (`ng_l2cap_cmds.h:323-344`) calls `m_copyback(m, sizeof(*c), echo_size, msg->data + sizeof(*ip))` which reads `echo_size` bytes from `msg->data + 8`. A message with `arglen=8` but `echo_size=65531` makes `m_copyback` read **65531 bytes** starting at `msg->data+8` — only 0 bytes are available, so it reads **65531 bytes of adjacent kernel heap**. Those bytes are echoed in the L2CAP EchoReq response to the remote BT peer (kernel heap info leak over the air), and if the read crosses an unmapped page the kernel panics (DoS).

## Threat model
Local privileged netgraph control message sender, or the remote BT peer if an attacker can influence the L2CA_Ping request sent on the host's behalf. The echo bytes are returned over the air to the BT peer.

## Why not live-triggered on this guest
The L2CA_Ping path needs an L2CAP connection (`con = ng_l2cap_con_by_addr`). Without one, the function calls `ng_l2cap_lp_con_req` (line 1328) which requires an attached HCI lower layer. The audit guest has no Bluetooth hardware / virtual HCI, so no connection can be established and the `m_copyback` at line 1357 is never reached. The bug is confirmed by source inspection of `ng_l2cap_ulpi.c:1306-1358` and the macro at `ng_l2cap_cmds.h:323-344`.

## PoC
`df0458_model.c` — a source-level analysis program that prints the exact data flow and the missing check.

## Fix
`fix.diff` — add `if (msg->header.arglen < sizeof(*ip) + ip->echo_size) { EMSGSIZE; goto out; }` after the echo_size bound check. Validated by building the fixed `ng_l2cap.ko` (compiles cleanly; runtime not testable without BT HW).

## Build / Run
```
cc -o df0458_model df0458_model.c
./df0458_model
```
