# DF-4540 — cxm_tuner: terminator-less tuner-table walks rely on unenforced cross-field data invariants (latent OOB-read hazard)

## What this is (Info / hardening — no demonstrated impact with current tables)

Both tuner-table walks in `cxm_tuner.c` run **without a terminator
element and without an array-extent bound**, stopping only when an
element field drops to/below the request:

1. Band-code walk — `cxm_tuner_select_frequency()`,
   **cxm_tuner.c:725-727**:
   `for (band_code = sc->tuner->band_codes; band_code->freq > freq; band_code++);`
   `struct cxm_tuner.band_codes` is declared `[3]`
   (**cxm.h:619**) and every tuner table in `cxm_tuners[]`
   initializes **all three** slots (cxm_tuner.c:273-498) — there is no
   zero terminator slot at all.
2. Channel-assignment walk — `cxm_tuner_select_channel()`,
   **cxm_tuner.c:789-791**:
   `for (assignments = channels->assignments; assignments->channel > channel; assignments++);`
   `assignments` is `[17]` (**cxm.h:594**) and `bg_air_channels`
   fills **all 17** slots (cxm_tuner.c:112-128).

The only thing keeping both walks in bounds today are cross-field
invariants in the static data, checked at the call sites:
* `freq >= sc->tuner->min_freq` (**cxm_tuner.c:706-708**) together with
  `band_codes[2].freq == min_freq` for **all 21** tuner entries
  (verified: cxm_tuner.c:273-498, e.g. FI1216 48250/48250,
  Microtune 45750/45750, TCL 55250/55250);
* `channel >= channels->min_channel` (**cxm_tuner.c:784-787**) together
  with the last assignment entry having `channel == min_channel` for
  **all 9** channel tables (cxm_tuner.c:72-251).

Neither invariant is enforced or asserted anywhere. A future table
entry that breaks either one (a new tuner whose `min_freq` is below its
last band-code freq, or a channel set whose last assignment channel is
above `min_channel`) makes an **unprivileged** `TVTUNER_SETFREQ` /
`TVTUNER_SETCHNL` ioctl (cxm.c:2824, 2864) walk off the end of a
`.rodata` array — an OOB read whose fetched bytes are then written to
the I2C bus (cxm_tuner.c:750).

This is the same class as the latent DF-1681 (unguarded divide,
"latent") — a data-invariant dependency with no runtime guard.

## Also checked and killed (see VERDICT.md)
`msg[5]` builders (in-bounds, `tuner_msg_len` ∈ {4,5}), `N` scaling
(checked `N > 32767`), `afcmap[16]` index (masked `& 0x0f`), system-code
lookup (bounded `NUM_ELEMENTS(codes[6])` + post-check), tuner-type
dispatch (bounds-checked, cxm_tuner.c:557), u32 kHz-conversion wrap in
cxm.c RADIO_SETFREQ/TVTUNER_SETFREQ (wraps are still range-checked
afterwards — no impact).

## Build / run
```
./build.sh   # n/a (shell probe)
./run.sh     # documents no Conexant hardware/cxm device on this guest
```

## Fix
`fix.diff` bounds both walks by array extent
(`NUM_ELEMENTS(...)`) and treats running off the end as `-1` — the
invariant becomes enforced instead of assumed. `git apply --check`
clean; never applied to sys/.
