# DF-5212 — cxm(4) REMOTE_GETKEY ioctl vs detach use-after-free (cxm_ir.c path)

## What

`cxm_ir_key()` (`sys/dev/video/cxm/cxm_ir.c:100-117`) — reached from the
unprivileged `REMOTE_GETKEY` ioctl (`cxm.c:2894-2898`, `_IOR('x', 71,
struct{u_char[3]})`, `ioctl_bt848.h:266-269`) on the world-readable
0444 `/dev/cxmN` cdev (`cxm.c:1884-1885`, no `priv_check` anywhere in
`cxm_ioctl`, `cxm.c:2306`) — can sleep 50 ms inside the kernel
(`tsleep(..., hz/20)` at `cxm_ir.c:114`) with the softc referenced only
from the sleeping thread's stack, then perform a second i2c transaction
on `sc->iicbus` (`cxm_ir.c:116`). `cxm_detach()` (`cxm.c:1944-2004`)
deletes the iicbus children (`cxm.c:1993-1996`) and frees the softc
without draining in-flight ioctls — `dev_ops_remove_minor`
(`cxm.c:1957`) only dead-ectors *new* calls — so a concurrent
`kldunload cxm` during the 50 ms window leaves the waker touching freed
memory (use-after-free read of `sc->iicbus` + method dispatch on a
dangling `device_t`). Third instance of the driver-wide no-locking
family already filed as DF-3941 (read path) and DF-4753 (msp34xxx audio
path); this file/path was not covered by either.

## Build

On the QEMU DragonFly guest (see `env.txt`):

    # trigger
    cc -O2 -Wall -o /root/df5212/cxm_ir_getkey /root/df5212/cxm_ir_getkey.c
    # baseline + patched module builds (both rc=0, -Werror):
    cp -r /usr/src/sys/dev/video/cxm /root/df5212/base && (cd /root/df5212/base && make)   # build.log
    # apply fix.diff to cxm.c/cxm.h in /usr/src/sys/dev/video/cxm, then:
    cp -r /usr/src/sys/dev/video/cxm /root/df5212/fix && (cd /root/df5212/fix && make)     # fix_build.log

## Run

On a host with a Hauppauge-style Conexant iTVC15/16 card (none exists
on this guest):

    /root/df5212/cxm_ir_getkey &                 # victim: parks >= 50 ms per ioctl
    while :; do kldunload cxm; kldload cxm; done # root: the freeing half

**Expected (on hardware):** panic within a few unload iterations — the
sleeper resumes into the freed softc, reads `sc->iicbus`, and dispatches
through the destroyed iicbus device's vtable.

**Observed (this guest):** `run.log` — trigger exits 3
`hardware absent: open(/dev/cxm0): No such file or directory`;
`kldload cxm` succeeds (rc=0) but no `/dev/cxm*` appears and no
vendor-0x4444 PCI device exists, so the victim ioctl cannot be
instantiated. Runtime reproduction is **hardware-gated** (same gate as
DF-4753, established independently there). The structural defect is
proven by source trace in `VERDICT.md`.

## Fix

`fix.diff` — serialize the cdev ioctl entry against `cxm_detach` with a
softc lock (drained in detach). Subsumed by the pending DF-4753 fix
(which wraps the whole `cxm_ioctl` switch); compile-validated here as
the full `cxm.ko` module with `-Werror` (`fix_build.log`, rc=0).
