# DF-1216 — METEORSETGEO + METEORSACTPIXFMT pixel-format mismatch

## Verdict
**REPRODUCED (source-level harness).** The bug is real and the OOB read/write
arithmetic is confirmed; impact ceiling is OOB kernel-heap read (`read()`) +
OOB kernel-heap write (`start_capture` bzero/RISC DMA). The kernel code path
cannot be triggered on this audit guest because QEMU does not emulate the
Brooktree Bt848 PCI video-capture device — `/dev/bktr0` does not exist and the
PCI vendor/device IDs (`109e:0350/0351`) are absent from `pciconf -lv`. The
fix.diff applies cleanly to the in-guest source tree and `nativekernel`
succeeds (rc=0); no run-time exercise of the path is possible on this guest
because the device is missing.

## Mechanism (cited `path:line`, confirmed against `sys/dev/video/bktr/bktr_core.c`)
1. **METEORSETGEO** at `bktr_core.c:1657` computes the bigbuf allocation as
   `temp = geo->rows * geo->columns * geo->frames * 2`. The literal `* 2`
   hard-codes YUV_422 (Bpp=2). The buffer is mapped into `alloc_pages` pages.
2. **METEORSACTPIXFMT** at `bktr_core.c:2328-2337` accepts any index `0..11`
   from the user, assigns `bktr->pixfmt = *(int *)arg`, and returns. It does
   **not** verify that the new format's `Bpp` is compatible with the already-
   allocated bigbuf, and **does not** reallocate.
3. **video_read** at `bktr_core.c:1084-1085` computes
   `count = rows * cols * pixfmt_table[bktr->pixfmt].public.Bpp`. With pixfmt=5
   (RGB 4Bpp from `pixfmt_table[5]` at `bktr_core.c:230`) and the YUV-sized
   buffer, `count` is 2× the buffer size.
4. **uiomove** at `bktr_core.c:1106` then reads `count` bytes out of `bigbuf`,
   reading up to `count - alloc_pages*PAGE_SIZE` bytes past the end (OOB heap
   read → kernel-memory info leak).
5. **start_capture** at `bktr_core.c:3519-3524` does
   `bzero(bigbuf, rows*cols*frames*pixfmt_table[bktr->pixfmt].Bpp)` and the
   RISC DMA builder uses `pitch = cols*Bpp(new)` → OOB heap write (up to ~4 MiB
   past buffer end).

The attacker needs only an open `bktr` fd (the device is `cr--r--r--`
world-readable per the finding, although the open may further be gated by
`caps_priv_check_self`); the two ioctls + read() sequence is fully unprivileged
on real hardware.

## Harness proof (`harness.c`)
Extracts the exact `pixfmt_table[]` from `bktr_core.c:220-238` and the exact
arithmetic from lines 1657, 2333, 1084, 3522, and prints the OOB length:

```
After METEORSETGEO(YUV_422): bigbuf=4186112 bytes alloc_pages=1022
video_read OOB=4177936 bytes  start_capture OOB=4177936 bytes
CONFIRMED: read() reads 4177936 bytes past bigbuf end (heap leak);
           start_capture bzero/DMA writes 4177936 bytes past bigbuf end (heap corruption)
```

Reproduced deterministically over 3 runs.

## Exploit-chain note
This is a write-capable primitive on real hardware. Because the audit guest
lacks the Bt848 device, no kernel-side chain can be developed here. The OOB
write is large (~4 MiB), so a successful trigger on real HW would corrupt
adjacent kernel heap and yield a reliable DoS at minimum; exploitation
(strings-into-heap would require additional research but the offset is
attacker-tunable via `rows`/`cols`/`pixfmt`). Documented here as a primitive
characterization only; impact ceiling for this run is heap-corruption DoS /
info-leak.

## PoC changes
- Replaced the original `poc_bpp_mismatch.c` (which attempts to open
  `/dev/bktr0` that does not exist on this guest) with `harness.c`, a
  self-contained userland reproduction of the cited arithmetic.
- Added `fix.diff`, `build.sh`, `run.sh`, `env.txt`, `VERDICT.md`,
  `manifest.json`, full `build.log`/`run.log`/`fix_build.log`.

## Fix
`fix.diff` adds the missing bounds check inside METEORSACTPIXFMT:
`bktr->rows * bktr->cols * bktr->frames * pixfmt_table[idx].Bpp` must not
exceed `alloc_pages * PAGE_SIZE` when `bigbuf != 0`. This is the targeted
root-cause fix; it supersedes the finding markdown's "clamp count in video_read"
note by closing the bug at the actual configuration-change point so neither
`video_read` nor `start_capture` can ever see a too-large Bpp.

## Fix-validation
The diff applies cleanly (`patch -p1 --forward`, hunk #1 succeeded at line
2330) and `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completes with
`rc=0` (`fix_build.log`). The kernel-side before/after run cannot be
demonstrated on this guest because the device is absent, so `fix_status:
"not_testable"` — the diff is verified to compile and the changed logic is
traced to close the cited code path.
