# DF-0625 — VERDICT

**Verdict: NOT REPRODUCED at runtime (no BT hardware). Bug CONFIRMED in source.**

## Mechanism (the bug IS real in source)

`ng_l2cap_l2ca_ping_req` (`sys/netgraph7/bluetooth/l2cap/ng_l2cap_ulpi.c:1298`)
validates only:

```c
1306:	if (msg->header.arglen < sizeof(*ip)) {     /* 8-byte header fits */
1316:	if (ip->echo_size > NG_L2CAP_MAX_ECHO_SIZE) { /* echo_size <= 65531 */
```

and then, **without checking that `arglen` actually contains
`sizeof(*ip) + echo_size` bytes**, passes them downstream:

```c
1357:	_ng_l2cap_echo_req(cmd->aux, cmd->ident,
1358:	    msg->data + sizeof(*ip), ip->echo_size);
```

The macro (`ng_l2cap_cmds.h:338-340`) calls
`m_copyback((_m), sizeof(*c), (_size), (_data))` with `_size = ip->echo_size`
and `_data = msg->data + 8`. The `ng_mesg` was allocated as
`sizeof(struct ng_mesg) + arglen` (`ng_message.h:81`), so when `arglen = 8`
(just the header) and `echo_size = 200`, the read at `msg->data + 8` runs
~192 bytes past the allocation into adjacent kernel heap (`bcopy` in
`_m_copyback2`, `uipc_mbuf.c:2361-2386`). The resulting mbuf is then queued
via `ng_l2cap_link_cmd` + `ng_l2cap_lp_deliver` (`ulpi.c:1366-1367`) and
ultimately transmitted as L2CAP Echo Request payload on the air.

The sibling `ng_l2cap_l2ca_get_info_req` (`ulpi.c:1428+`) **does** do an
exact-match check:

```c
1439:	if (msg->header.arglen != sizeof(*ip)) {
```

so the fix is to mirror that pattern in the ping handler.

## Why it CANNOT be triggered on this guest

The bug path at `ulpi.c:1357` requires a valid BT connection (`con != NULL`)
at `:1325` or a successful `ng_l2cap_lp_con_req` at `:1328`. The latter
fails fast without an HCI lower layer:

```c
llpi.c:87:  if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
llpi.c:92:      return (ENOTCONN);
```

On the audit guest:
- No Bluetooth hardware (no USB BT dongle, no virtual BT adapter).
- `ng_hci` / `ng_ubt` not loaded; no HCI hook to connect to an l2cap node.
- Therefore `l2cap->hci == NULL`, `ng_l2cap_lp_con_req` returns `ENOTCONN`,
  `ng_l2cap_l2ca_ping_req` takes the early `goto out` at `:1333`, and the
  buggy `_ng_l2cap_echo_req` call at `:1357` is never reached.

Additionally, the pre-installed `/boot/kernel/netgraph.ko` in the audit
snapshot is ABI-incompatible with a freshly-built `ng_l2cap.ko` (NG_ABI_VERSION
mismatch surfaced as `KLD ng_l2cap.ko: depends on netgraph - not available or
version mismatch`), so even loading the module to confirm the path requires
rebuilding netgraph.ko too. The bug path is still **code-confirmed**.

Classification: bug CONFIRMED in source, runtime trigger requires
Bluetooth hardware/HCI emulation not present on this guest. Valid hard
blocker: *no harness can exercise the bug path without BT hardware or an
HCI lower-layer emulation, neither of which is available on this kernel.*

## Realistic impact ceiling

If the bug WERE reachable (any deployment that actually uses DragonFly's
Bluetooth L2CAP stack — typically embedded systems or routers with USB BT
dongles):

- A local user with netgraph control access (root, or in a `bluetooth`
  group if configured) sends a crafted `NGM_L2CAP_L2CA_PING` with
  `arglen=8`, `echo_size` up to 65531, and `bdaddr` pointing at a
  Bluetooth peer the attacker controls.
- The kernel reads up to `echo_size` bytes of adjacent kernel heap and
  transmits them as the Echo Request payload.
- The attacker captures the packet at their BT peer and recovers kernel
  heap contents — freed `ng_mesg` objects, slab metadata, possibly
  credential structures recycled from prior allocations.

Severity Medium with `C:H/S:C` is appropriate IF BT hardware is present;
on this guest, no demonstrated runtime impact.

## Recommended fix

`fix.diff` adds the missing exact-match arglen validation in
`ng_l2cap_l2ca_ping_req`, mirroring the pattern already used by the sibling
`ng_l2cap_l2ca_get_info_req`:

```c
if (msg->header.arglen != sizeof(*ip) + ip->echo_size) {
    NG_L2CAP_ALERT(...);
    error = EMSGSIZE;
    goto out;
}
```

Placed immediately after the existing `echo_size` cap check, so the
function returns `EMSGSIZE` for any PING whose claimed echo data does not
match the actual trailing bytes.

This **matches** the finding markdown's proposal (same check, same
placement, same error code).

## Caveats

The "no BT hardware" hard blocker applies to runtime demonstration. The
fix is small, surgical, and mirrors a proven pattern in a sibling function
— low regression risk. Recommended for merge regardless of the runtime
reproducibility status.
