# DF-1217 — bktr unprivileged I2C / EEPROM / GPIO access

## Verdict
**INCONCLUSIVE (not_testable on this guest)** — bug confirmed in source; the
capture-card hardware is absent from the audit guest, so `/dev/bktrN` is never
created and the ioctls cannot be issued. Fix authored, applied, and the bktr
module compiled clean as a standalone module build.

## Finding summary
`video_ioctl()` in sys/dev/video/bktr/bktr_core.c dispatches several
hardware-control ioctls with **no privilege check** at any layer:
- `BT848_WEEPROM` (:2078) — writes persistent on-card EEPROM data.
- `BT848_I2CWR` (:2168) — drives the I2C bus at arbitrary device addresses
  (tuner PLL, audio DSP, EEPROM), both read and write.
- `BT848_GPIO_SET_EN` (:2108) / `BT848_GPIO_SET_DATA` (:2116) — toggle GPIO
  output pins, muxing analog signal paths.
- `BT848_MSP_WRITE` / `BT848_MSP_RESET` (:2195/:2204) — direct MSP34xx audio
  DSP control.

The device node is created with mode **0444** (`make_dev(... 0444, "bktr%d", unit)`
at bktr_os.c:325–327), and `bktr_ioctl()` (bktr_os.c:615) calls `video_ioctl()`
directly with no `caps_priv_check_td` / `priv_check` / `suser` gate. So any
local user who can open `/dev/bktr0` read-only (world-readable) can:
- write persistent EEPROM data that survives reboot (firmware-level tampering /
  persistent DoS of the card),
- drive the I2C bus at arbitrary device addresses (write to tuner PLL, audio
  DSP, EEPROM),
- toggle GPIO pins (mux analog paths),
- reprogram the tuner to out-of-band RF frequencies.

This is a privilege-boundary / hardware-misuse gap, not a memory-corruption bug.
No chain to `uid=0` is implied; the impact is unauthorized hardware control
(which on capture-card-equipped kiosk / PVR / embedded systems is a real
concern — persistent EEPROM writes are particularly nasty).

## Source confirmation (audited tree)
- bktr_os.c:147 — `cdevsw` declares `.d_open = bktr_open`, `.d_ioctl = bktr_ioctl`.
- bktr_os.c:325–327 — `make_dev(&bktr_ops, unit, 0, 0, 0444, "bktr%d", unit);` — world-readable.
- bktr_os.c:615–649 — `bktr_ioctl()` no priv check; dispatches to `video_ioctl()` for VIDEO_DEV.
- bktr_core.c:1202 — `video_ioctl()` entry — no priv check at entry.
- bktr_core.c:2078–2084 — `BT848_WEEPROM` → `writeEEProm()` — no priv check.
- bktr_core.c:2108–2118 — `BT848_GPIO_SET_EN` / `_SET_DATA` → `OUTL()` MMIO — no priv check.
- bktr_core.c:2168–2181 — `BT848_I2CWR` → `i2cWrite()` / `i2cRead()` — no priv check.
- bktr_core.c:2195–2206 — `BT848_MSP_WRITE` / `_RESET` → `msp_dpl_write()` / `msp_dpl_reset()` — no priv check.
- Compare to `sys/dev/misc/kbd/kbd.c:672` — `if (caps_priv_check_self(SYSCAP_RESTRICTEDROOT)) return (EPERM);` — the canonical pattern this driver is missing.

## Why not runtime-reproduced on this guest
The QEMU/KVM guest has no Brooktree Bt848/Bt878 video-capture card (no PCI
device matching; only 7 PCI devices total, all virtio/QEMU-standard). So the
`bktr` driver never attaches, no `/dev/bktrN` node is created, and the ioctls
cannot be issued. The `bktr.ko` module is present in `/boot/kernel/` but is
not loaded (the driver probe returns ENXIO without matching hardware).

This is a privilege-check gap rather than a memory bug; even on hardware that
had the card, the demonstration would be "ioctl succeeds as unprivileged user"
vs the fixed "ioctl returns EPERM as unprivileged user". The static trace
above is sufficient to confirm the bug.

## Fix (fix.diff)
Add a privilege gate at the top of `video_ioctl()` for the hardware-control
write ioctls, using the standard `caps_priv_check_td(td, SYSCAP_RESTRICTEDROOT)`
pattern (same pattern as `kbd.c:672`):

```c
#include <sys/caps.h>   /* added near other sys/ includes */

int
video_ioctl(...) {
    switch (cmd) {
    case BT848_WEEPROM:
    case BT848_I2CWR:
    case BT848_GPIO_SET_EN:
    case BT848_GPIO_SET_DATA:
    case BT848_MSP_WRITE:
    case BT848_MSP_RESET:
        if (caps_priv_check_td(td, SYSCAP_RESTRICTEDROOT))
            return (EPERM);
        break;
    default:
        break;
    }
    ...
```

Reads (`BT848_REEPROM`, `BT848_GPIO_GET_*`) are left open — they leak only
card-identity / GPIO state, not persistent-modification capability.

## Fix validation
- `git apply --check -p1` — clean.
- Applied to in-guest `/usr/src/sys/dev/video/bktr/bktr_core.c`; standalone
  module build (`make` in `/usr/src/sys/dev/video/bktr`) — compiled clean,
  `bktr.ko` produced, build rc=0.
- `fix_status: not_testable` — no capture-card hardware on this guest; cannot
  do a runtime before/after EPERM demonstration.

## Run / reproduce
Not runnable on this guest. On a system with a Bt878 capture card and the
`bktr.ko` module loaded, the PoC is simply:
```c
int fd = open("/dev/bktr0", O_RDONLY);   /* succeeds as any user (mode 0444) */
u_long i2c = (1UL<<24) | (addr<<16) | (port<<8) | data;  /* write=1 */
ioctl(fd, BT848_I2CWR, &i2c);             /* UNFIXED: succeeds; FIXED: EPERM */
```
