# DF-4753 VERDICT

**Status:** INCONCLUSIVE (runtime) — structural defect certain by source
trace; runtime reproduction hardware-gated (no Conexant CX2388x PCI card in
the QEMU guest); fix compile-validated (full cxm.ko, -Werror).

**Impact:** Low — UAF (panic ceiling) with a privileged trigger half, the
cxm twin of DF-4419 (bktr, Low). Bucket: memcorrupt.

## Why it is real (source trace)

1. **No serialization anywhere.** `struct cxm_softc` (cxm.h:216-264) has no
   lock; `rg lockmgr|lockinit` over cxm.c returns nothing. `cxm_ioctl`
   (cxm.c:2306) fetches `sc` and immediately dispatches.
2. **Long sleep window with sc stack-referenced only.**
   `cxm_msp_wait_for_lock` (cxm_msp34xxx.c:588-626) loops
   `tsleep(&sc->iicbus, 0, "audio", hz/20)` ×10 = ~0.5 s per call; reached
   from the unprivileged ioctl path
   `BT848_SAUDIO → cxm_encoder_wait_for_lock (cxm.c:2410) →
   cxm_msp_autodetect_standard (cxm.c:1574)`.
3. **Unprivileged surface.** `/dev/cxmN` is `make_dev(&cxm_ops, unit, 0, 0,
   0444, "cxm%d", unit)` (cxm.c:1885-1886); DF devfs does not check open
   mode for ioctls; no `priv_check` exists in cxm.c (rg: 0 hits). Pass-1
   cxm_ir audit reached the same conclusion for REMOTE_GETKEY.
4. **Freed mid-flight.** `cxm_detach` (cxm.c:1944-2004) =
   `dev_ops_remove_minor` (cxm.c:1957) → DMA teardown → `bus_generic_detach`
   + `device_delete_child` of the I2C children (cxm.c:1993-1996) →
   resource release. `destroy_dev`/`dev_ops_remove_minor` only revector NEW
   calls to dead ops (kern_conf.c:334-343, kern_device.c:83); an ioctl
   already inside the old body keeps running. Module unload then frees the
   softc (devclass free after detach). The sleeper wakes and executes
   `cxm_msp_is_locked → cxm_msp_dpl_read(sc->iicbus, …)`
   (cxm_msp34xxx.c:560) and `device_printf(sc->dev, …)` (cxm_msp34xxx.c:624)
   on freed memory → NULL/garbage device_t method dispatch → panic.
5. **Sibling defect in the same family (folded):** `cxm_open`'s
   `if (sc->is_opened) return EBUSY; sc->is_opened = 1;`
   (cxm.c:2158-2161) is an unlocked TOCTOU — racing opens defeat
   single-open and interleave iicbb transactions on the shared bus.

## Guest verification (what could and could not be done)

* `kldload cxm` succeeds (module present: /boot/kernel/cxm.ko), `kldstat -v`
  shows the cxm driver registered — **but no /dev/cxm0 appears** (no
  Conexant iTVC15/16 PCI device in QEMU) → the victim ioctl cannot be
  instantiated; `kldunload cxm` clean. See run.log.
* PoC `cxm_uaf_race.c` written with verified constants
  (`BT848_SAUDIO _IOW('x',46,int)`, `AUDIO_TUNER 0x00` from
  ioctl_bt848.h:83,130); exits 3 with a clear hardware-gated message.
* **Fix compile-validated**: baseline cxm.ko and fix.diff-patched cxm.ko
  both built on the guest against pristine /usr/src with `-Werror`
  (build.log, fix_build.log, rc=0 both).
* Guest /usr/src restored and hash-verified afterwards
  (cxm.c 22808bde…, cxm.h 3c863567…).

## Negative results for the other hunt items (this file)

* **DF-1600 carrier-detect twin: ABSENT.** cxm_msp34xxx.c has no
  carrier_detect loop (rg 'carrier' over sys/dev/video/cxm: 0 hits); the
  driver uses MSP34x5 AUTOMATIC Standard Select (init writes DEM 0x0020,
  cxm_msp34xxx.c:74-75) and polls the 2-byte STATUS/standard registers in
  `cxm_msp_is_locked` (:575-583) — no max1=-1 indexing pattern exists.
* **msp_name overflow: SAFE** (bktr DF-1850 does not carry over): cxm uses
  bounded `ksnprintf(sc->msp_name /* [10] */, ...)` (cxm_msp34xxx.c:306,
  cxm.h:249); format always emits ≥ 8 chars so `msp_name[3]/[4]` probes
  (:315-316, :332) stay in-bounds; worst-case 11-char output truncates.
* **read/write helpers**: `received`/`sent` set by the iic layer; `-1`
  return vs `sizeof()` (size_t) comparison catches errors; no short-read
  use of uninitialized stack buffers (buffers only consumed when the return
  equals the expected size).
* **No volume table walk exists** in cxm (mute/unmute are fixed register
  writes); no division anywhere in the file.

## Fix

`fix.diff` (cxm.h + cxm.c): `struct lock cxm_lock` in the softc,
`lockinit` in attach, lock held across open/close and the whole ioctl body
(thin `cxm_ioctl` wrapper around the renamed `cxm_ioctl_locked`), and a
drain (`LK_EXCLUSIVE` acquire/release) in `cxm_detach` right after
`dev_ops_remove_minor`, with `lockuninit` as the last step. Known residual
(not introduced here): `cxm_read` still runs unlocked — that path is owned
by DF-3941's read-ring rework.

## Baseline vs patched

Not runtime-testable on this guest (no cxm hardware). fix_status
=inconclusive: build verified (cxm.ko, -Werror), behavior hardware-gated.

## 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.
