# DF-1215 — VERDICT

## Verdict

**INCONCLUSIVE (live) — bug CONFIRMED in source; trigger requires PCI hardware
absent from this QEMU guest.** The primitive is a real arbitrary-physical-write
via the bt848 DMA engine. Fix authored and built cleanly into a single-fix
kernel; the patched kernel is boot-stable and the cited path is closed in
source.

## Bug class

Arbitrary physical-memory write via PCI bus-master DMA, no privilege check,
reachable through a world-readable (mode 0444) character device on real
hardware.

## Source confirmation (path:line)

The bug is real. The cited ioctl handler stores the user-supplied 32-bit
physical address directly into `bktr->video.addr` with **no** privilege check
and **no** address validation:

```c
/* sys/dev/video/bktr/bktr_core.c:1396-1402 */
case METEORSVIDEO:
    video = (struct meteor_video *)arg;
    bktr->video.addr = video->addr;        /* <- user-controlled phys addr */
    bktr->video.width = video->width;
    bktr->video.banksize = video->banksize;
    bktr->video.ramsize = video->ramsize;
    break;
```

There is **no** `priv_check`, `suser`, `caps_priv_check_td`, or open-mode gate
anywhere in `bktr_core.c` (verified with `grep`). The function signature
`video_ioctl(bktr, unit, cmd, arg, td)` even passes a `thread *td` that the
case could use for a privilege check — it doesn't.

Every RISC DMA builder then consumes `bktr->video.addr` as the bus-master
target address:

| Builder                | line | sink                                          |
|------------------------|------|-----------------------------------------------|
| `rgb_vbi_prog`         | 2671 | `target_buffer = (u_long) bktr->video.addr;`  |
| `rgb_prog`             | 2841 | `target_buffer = (uint32_t) bktr->video.addr;`|
| `yuvpack_prog`         | 2996 | `target_buffer = (uint32_t) bktr->video.addr;`|
| `yuv422_prog`          | 3114 | `target_buffer = (uint32_t) bktr->video.addr;`|
| `yuv12_prog`           | 3215 | `target_buffer = (uint32_t) bktr->video.addr;`|

The bt848 RISC engine writes captured video frames to that physical address.
Since the bytes are video data (color-bar pattern via `BT848_SCBARS` is
attacker-deterministic), the attacker has substantial byte control over what
gets written. The `METEORCAPTUR` continuous-capture case (bktr_core.c:1502)
that starts the DMA also has no privilege check, completing the chain:

```
open(/dev/bktr0, O_RDONLY)            # works for any user — node is mode 0444
ioctl(fd, METEORSVIDEO, addr=X)        # sets DMA target to attacker phys addr
ioctl(fd, BT848_SCBARS, 1)             # deterministic color bars as payload
ioctl(fd, METEORCAPTUR, CONTINUOUS)    # bus-master DMA write begins
```

Device-node creation confirmed world-readable:

```c
/* sys/dev/video/bktr/bktr_os.c:325 */
make_dev(&bktr_ops, unit, 0, 0, 0444, "bktr%d", unit);
```

## Live reproduction: NOT POSSIBLE on this guest

```
$ ls /dev/bktr*
ls: /dev/bktr*: No such file or directory

$ pciconf -lv | grep -iE 'bktr|bt848|brooktree'
(no match)
```

The QEMU guest exposes only: Intel 440FX, PIIX3 ISA, PIIX3 IDE, PIIX4 ACPI,
QEMU std VGA, virtio-net, virtio-blk. **No Brooktree bt848/878 PCI video
capture device** is emulated, so the `bktr` driver never probes/attaches,
`make_dev` is never called, and `/dev/bktr0` is never created. `kldload bktr`
succeeds but creates no device node (verified).

This is case **(d)** in the procedure: *genuinely not reachable at runtime on
this guest*. The device path is live-reachable on real hardware (or in a
QEMU invocation that adds `-device bktr`-class hardware, which doesn't exist
upstream).

## Phase 6 — escalation analysis (not exercisable live)

If the device were present, the primitive would be an **arbitrary physical
memory write** of substantially-attacker-controlled bytes (color-bar frame
data is deterministic). On this guest (no SMAP/SMEP/KASLR/PTI; INVARIANTS ON
by default in GENERIC), the realistic chain on real hardware would be:

1. Identify the physical address of a victim kernel object (`proc0.p_ucred`,
   a `struct ucred`, or a function-pointer-bearing struct) via DMI/E820 or
   known DMA-consistent regions.
2. Point `video.addr` at it via `METEORSVIDEO`.
3. Trigger `METEORCAPTUR` to DMA a color-bar pattern whose bytes overwrite
   the victim's `cr_uid` (→ direct privesc) or a function pointer (→ pivot
   to userspace shellcode on this guest, since SMEP is OFF).

This is a textbook physmem-write → root chain on default GENERIC. The chain
is not demonstrable here only because the trigger device is absent.

## Fix (fix.diff)

Add a capability check before redirecting DMA away from the kernel-allocated
buffer:

```diff
+ if (video->addr != 0 &&
+     caps_priv_check_td(td, SYSCAP_RESTRICTEDROOT) != 0)
+     return (EPERM);
  bktr->video.addr = video->addr;
```

`addr == 0` is the "use kernel-allocated `bigbuf`" case (preserved for
unprivileged users); any non-zero redirect now requires privilege.

## Phase 8 — fix validation (not_testable)

- `fix.diff` applies cleanly to /usr/src via `patch -p1 --forward` (both
  hunks succeed).
- `make -j6 nativekernel KERNCONF=X86_64_GENERIC` builds the patched source
  with `cc 8.3 -Werror`, rc=0, no warnings on `bktr_core.c`.
- The patched kernel installs via `make installkernel` and boots cleanly
  (`kern.version` → `6.5-DEVELOPMENT #1: Mon Jul 20 11:01:52 UTC 2026`).
- `nm /boot/kernel/kernel` shows `T caps_priv_check_td` linked in.
- The live PoC cannot be re-run on the patched kernel to confirm "behavior
  gone" because the trigger device (`/dev/bktr0`) does not exist on this
  guest. Per the procedure, this is `fix_status: not_testable` — the diff
  applies, compiles, boots, and a source read confirms the previously-unchecked
  path now gates on `caps_priv_check_td`.

## PoC changes

The provided `poc_physdma.c` (authored pre-verification) compiles cleanly on
the guest (cc 8.3, `-Wall` no warnings) and is left unchanged. It fails at
`open("/dev/bktr0")` with ENOENT, which is the correct guest-behavior given
the absent PCI hardware.

## Files

| File | Purpose |
|------|---------|
| `poc_physdma.c`     | original live-trigger PoC (compiles, fails at open on this guest) |
| `fix.diff`          | adds `caps_priv_check_td` gate to `METEORSVIDEO` (sys/dev/video/bktr/bktr_core.c) |
| `build.sh`          | runs the PoC build |
| `run.sh`            | runs the PoC (ENOENT on guest) |
| `fix_build.log`     | full untrimmed `nativekernel` log for the patched build (rc=0) |
| `env.txt`           | guest uname / pciconf / dev nodes / kldstat / kernel sha256 |
