# DF-0286 — VERDICT

**Verdict: NOT RUNTIME-TESTABLE on this guest (no WiFi hardware) — source trace DEFINITIVELY CONFIRMS the bug is real.**

## The claim
`sys/netproto/802_11/wlan/ieee80211_mesh.c`, `mesh_recv_action_meshlmetric()`
(line 2548):

```c
2548: static int
2549: mesh_recv_action_meshlmetric(struct ieee80211_node *ni,
2550:     const struct ieee80211_frame *wh,
2551:     const uint8_t *frm, const uint8_t *efrm)
2552: {
2553:     const struct ieee80211_meshlmetric_ie *ie =
2554:         (const struct ieee80211_meshlmetric_ie *)
2555:         (frm+2); /* action + code */
2556:     struct ieee80211_meshlmetric_ie lm_rep;
2557:
2558:     if (ie->lm_flags & IEEE80211_MESH_LMETRIC_FLAGS_REQ) {
2559:         lm_rep.lm_metric = mesh_airtime_calc(ni);   /* -> DF-0287 */
...
```

The handler casts `frm+2` to `struct ieee80211_meshlmetric_ie *` and immediately
reads `ie->lm_flags` **without checking that `efrm-frm >= 2 +
sizeof(struct ieee80211_meshlmetric_ie)`**.

## What the caller actually guarantees

`mesh_recv_action_meshlmetric` is reached via the mesh action-frame dispatch
table (`ieee80211_mesh.c:586`). It is called from `ieee80211_parse_action()`
(`ieee80211_input.c:754`), which validates the frame body with:

```c
770: IEEE80211_VERIFY_LENGTH(efrm - frm,
771:     sizeof(struct ieee80211_action), return EINVAL);
```

`struct ieee80211_action` is the 2-byte `{category, action}` header — so the
caller guarantees **only 2 bytes** of frame body. For the mesh action category
there is **no** additional `IEEE80211_VERIFY_LENGTH` (the length-check switch in
`ieee80211_parse_action` covers only `CAT_BA` and `CAT_HT`, not mesh). The mesh
handler is then handed `frm`/`efrm` with as few as 2 valid bytes.

## The OOB read (confirmed from source)

`struct ieee80211_meshlmetric_ie` (`ieee80211_mesh.h:122`), `__packed`:

| field            | bytes | offset-in-IE |
|------------------|-------|--------------|
| lm_ie            | 1     | 0            |
| lm_len           | 1     | 1            |
| lm_flags         | 1     | 2            |
| lm_metric (u32)  | 4     | 3            |
| **total**        | **7** |              |

The IE is read starting at `frm+2`. With only 2 valid body bytes (`frm[0]`,
`frm[1]`), the reads of `ie->lm_flags` (at `frm+2`) and `ie->lm_metric`
(`frm+3..frm+6`) are **up to ~5-7 bytes past the valid frame body** — reading
stale mbuf residue. This is a remote, unauthenticated OOB info-leak of up to a
handful of bytes (and it is also the gateway to DF-0287: a crafted
`lm_flags & REQ` triggers `mesh_airtime_calc` on a neighbour whose `ni_txrate`
may be 0).

## Why not runtime-tested here
No 802.11 radio hardware → no mesh vap → the action-frame receive path cannot be
exercised. The OOB read is **definitively confirmed by the source trace** above.

## Realistic impact ceiling
Remote, unauthenticated OOB read of up to ~7 bytes of mbuf residue per crafted
truncated Mesh Link-Metric action frame; low-value info leak. More seriously it
is the **gateway to DF-0287** (the div-by-zero), which is a remote DoS.

## Fix
`findings/poc/DF-0286/fix.diff` adds an explicit length check at the top of
`mesh_recv_action_meshlmetric`: `if (efrm - frm < 2 +
sizeof(struct ieee80211_meshlmetric_ie)) return 0;` before the cast/read.
Verified `git apply --check` clean and compiles into the base kernel
(nativekernel, with the DF-0275/0287 fixes). `fix_status: not_testable`
(diff applies + compiles; runtime not exercisable without WiFi HW).
