# DF-2353 — VERDICT: REPRODUCED (UAF → page-fault panic; info-leak ceiling) — FIX VALIDATED

## Bottom line

The claimed use-after-free is real and was demonstrated twice, each time
with the teardown driven by the **production `uvc_drv_detach()` function**
(action `2353prod`, fabricated `device_t.softc`) or by the identical
inline call sequence (action `2353`, uvc_drv.c:2503-2515 verbatim), while
an **unprivileged user** (uid 1001) hammered `VIDIOC_ENUM_FMT` /
`VIDIOC_ENUM_FRAMESIZES` on the production-registered `/dev/videoN`
nodes (0666). Result:

```
Fatal trap 12: page fault while in kernel mode
fault virtual address = 0xfffff801186d0010
fault code = supervisor read data, page not present
Stopped at uvc_drv_enum_v4l2_fmt+0x32: testb $0x1,0x10(%r12)
```

- `testb $0x1,0x10(%r12)` = `fmt->flags & UVC_FMT_FLAG_COMPRESSED`
  (uvc_drv.c:831) inside `uvc_drv_enum_v4l2_fmt`.
- The faulting address is inside the **freed 65 KB `data->fmt`**
  allocation (the crafted device: 250 frames × 57 intervals ⇒
  96 + 250·32 + 14250·4 = 65096 bytes; `kfree()` returned its pages, so
  the post-free read hit `page not present`).
- Panicked in the FIRST detach cycle both runs — the window is not
  merely the few instructions between `uvc_drv_destroy_data()` and
  `uvc_v4l2_unreg()`, but extends across the **asynchronous**
  `destroy_dev()` (kern_conf.c:346 → devfs_core.c:757-763
  `devfs_msg_send_dev(DEVFS_DEVICE_DESTROY)`), during which open fds
  keep dispatching ioctls (and after which `si_drv1` still pointed at
  the already-`kfree`d `video` until devfs processed the destroy).

## Root cause (verified in source)

- uvc_drv.c:2503-2507 — `uvc_drv_destroy_video` + `uvc_drv_destroy_data`
  (kfree `data->fmt`, `data`) + `uvc_drv_destroy_ctrl`
- uvc_drv.c:2509-2515 — only THEN `uvc_v4l2_unreg(sc->video)` →
  `destroy_dev` (async) and `kfree(sc->video)`
- uvc_v4l2.c:548-553 — `VIDIOC_ENUM_FMT` → `uvc_drv_enum_v4l2_fmt`
  reads `v->data->nfmt` / `v->data->fmt` with **no lock**
  (uvc_drv.c:826-833; same for ENUM_FRAMESIZES 799-816,
  ENUM_FRAMEINTERVALS 739-760, G_FMT)
- uvc_v4l2.c:369-379 — `uvc_v4l2_open` also writes `v->users` on the
  freed `video` (a UAF *write* window exists during the async teardown
  as well).

## Primitive characterization (adversarial read)

- Demonstrated primitive: **kernel-side read of freed slab memory**,
  reached by an unprivileged ioctl; when the freed pages are unmapped
  (large `data->fmt`) it is a reliable panic (DoS).
- When the freed memory is instead re-used by another slab allocation
  (small `data->fmt` devices), `VIDIOC_ENUM_FMT` copies up to 32 bytes
  of it (`strlcpy(vfmt->description, fmt->name, 32)`, uvc_drv.c:833)
  back to userspace — an info-leak oracle whose content depends on what
  reclaims the buffer (the `trigger_race` "ODD description" detector
  watches for exactly this; in the patched validation run it stayed 0).
- Escalation ceiling: read-only disclosure + DoS from this handler set.
  No attacker-controlled write through ENUM_* paths was identified
  (the `v->users` UAF write at open time is a counter increment on freed
  memory — corrupting an adjacent allocation's first 8 bytes with +1
  under narrow timing; noted for completeness, not developed).

## Rig notes

- `2353prod` calls the in-tree `uvc_drv_detach()` directly (fake
  `device_t`; `device_get_softc` just returns `dev->softc`,
  subr_bus.c:1782) — the ordering bug exercised is the production code.
- Hammer quirk: `trigger_race.c`'s `v4l2_frmsizeenum` union padding made
  its VIDIOC_ENUM_FRAMESIZES number mismatch the kernel's
  (`c0dc564a` → console "need to be implement" noise). VIDIOC_ENUM_FMT —
  the handler that faulted — matched exactly. Kept as-built so the logs
  match this source (sha256 in verdict.json).
- The guest has no emulated UVC webcam; the rig synthesizes the USB
  hardware layer only.

## Fix validation

`fix.diff` reorders `uvc_drv_detach()`: `uvc_v4l2_unreg()` FIRST, then
drain `video->users` (≤10 s), then destroy/free everything. Patched rig,
same hammer: **3000 production detach cycles, 18024 ioctl iterations,
0 faults, 0 odd descriptions**, guest up, nodes cleanly removed
(fix_run.log). Residual (documented, upstream-hardening): an ioctl
already in flight when its fd's dtor drops `users` to 0 is not
reference-counted; closing the practical window needs per-call refs or a
synchronous devfs drain primitive.

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
