# DF-2353 — use-after-free on device disconnect (cdev outlives `sc->data`)

`sys/bus/u4b/uvc/uvc_drv.c` — `uvc_drv_detach()` (uvc_drv.c:2493-2518)
frees `sc->data` (`uvc_drv_destroy_data`, uvc_drv.c:2506, which kfree's
`data->fmt` and `data`) and later `sc->video` **before** destroying the
`/dev/videoX` character device (`uvc_v4l2_unreg`, uvc_drv.c:2511).
`destroy_dev()` is asynchronous (`destroy_dev` → `devfs_destroy_dev` →
`devfs_msg_send_dev(DEVFS_DEVICE_DESTROY)`, kern_conf.c:346 /
devfs_core.c:757-763), and open file descriptors keep dispatching ioctls
into the driver until the devfs core processes the destroy. Several V4L2
handlers (`uvc_drv_enum_v4l2_fmt`, `uvc_drv_enum_v4l2_framesizes`,
`uvc_drv_enum_v4l2_frameintervals`, `uvc_drv_get_v4l2_fmt`) read
`video->data->fmt` with no mutex held → concurrent ioctls during unplug
read freed memory.

## Evidence class

Production-handler KLD dispatch rig: unprivileged user hammers
`VIDIOC_ENUM_FMT`/`VIDIOC_ENUM_FRAMESIZES` (all production dispatch),
while the rig runs reg/detach cycles — the teardown is the **actual
production `uvc_drv_detach()` function**, invoked through a fabricated
`device_t` whose `softc` points at the rig's `uvc_softc`
(action `2353prod`; an earlier variant `2353` replays the same five calls
inline — both panic identically).

The crafted device carries 250 discrete frames × 57 intervals, sizing
`data->fmt` at ~65 KB so that `kfree()` returns its pages to the VM and
post-free reads fault.

## Build / run

```
./build.sh    # push rig + hammer trigger, build uvc_rig.ko
./run.sh      # hammer as user + 2000 production uvc_drv_detach cycles as root
```

## Expected output (see panic.txt / panic.prod.txt)

```
RIG2353p: === DF-2353 via PRODUCTION uvc_drv_detach(), 2000 cycles ===
c0dc564a-uvc_v4l2_ioctl need to be implement          <- hammer's live ioctls
Fatal trap 12: page fault while in kernel mode
fault virtual address = 0xfffff801186d0010             <- inside the freed data->fmt
fault code = supervisor read data, page not present
Stopped at      uvc_drv_enum_v4l2_fmt+0x32: testb $0x1,0x10(%r12)
```

`testb $0x1, 0x10(%r12)` is `fmt->flags & UVC_FMT_FLAG_COMPRESSED`
(uvc_drv.c:831) — a read of the freed `data->fmt` array by the
unprivileged hammer. Panicked during the **first** detach cycle.

## Fix validation

`fix.diff` reorders `uvc_drv_detach()`: `uvc_v4l2_unreg()` first, then
wait (up to 10 s) for `video->users == 0`, and only then
`uvc_drv_destroy_video/data/ctrl` + `kfree(video)`. On the patched rig
(fix_run.log): **3000 production-detach cycles racing 18024 unprivileged
ioctl iterations — zero faults, zero odd descriptions**, guest stayed up
and `/dev/video*` disappeared cleanly.
