DF-5212 / verdict.json
{ "finding_id": "DF-5212", "status": "inconclusive", "reproduced": 0, "impact": "panic", "confidence": "certain", "verdict": "Structural defect certain by source trace; runtime reproduction hardware-gated. cxm_ir_key() (sys/dev/video/cxm/cxm_ir.c:100-117) is reached from the unprivileged REMOTE_GETKEY ioctl (cxm.c:2894-2898) on the 0444 /dev/cxmN cdev (cxm.c:1884-1885; zero priv_check/locks in cxm_ioctl, cxm.c:2306). On a failed i2c read it tsleeps 50 ms (cxm_ir.c:114) with the softc referenced only from the sleeping threads stack, then transacts again on sc->iicbus (cxm_ir.c:116). cxm_detach (cxm.c:1944-2004) deletes the iic children (cxm.c:1993-1996) and frees the softc without draining in-flight ioctls - dev_ops_remove_minor (cxm.c:1957) only dead-vectors NEW calls - so kldunload cxm during the 50 ms window makes the waker read freed memory and dispatch through a dangling device_t (UAF -> panic; compounded by DF-4983s no-op iicbus invalidation). Third instance of the driver-wide no-locking family (DF-3941 read path, DF-4753 msp34xxx audio path) - the IR path was covered by neither. On this guest: trigger compiles and runs but exits 3 hardware absent - kldload cxm rc=0 yet no /dev/cxm* and no vendor-0x4444 PCI device (run.log), same gate DF-4753 hit. Buffer-math hunt on the same path is negative: _IOR 3-byte payload == kernel copyout size (sys_generic.c:668,696,730) and iicbb_read fills exactly len (iicbb.c:409-424).", "exploit_chain": "unprivileged: open /dev/cxmN O_RDONLY (0444) -> ioctl(REMOTE_GETKEY) in a loop, each iteration parking >= 50 ms at cxm_ir.c:114; privileged: while :; do kldunload cxm; kldload cxm; done frees the softc mid-sleep; on wake: UAF read of sc->iicbus + iicbus_start method dispatch on a destroyed device_t -> panic. Code-exec ceiling exists via controlled reclaim of the freed softc before wake but was not developed: hardware-gated and the freeing half is privileged (severity Low).", "evidence": "VERDICT.md (line-by-line trace incl. the buffer-math negative results); run.log (trigger exit 3, kldload cxm rc=0, no /dev/cxm*, no vendor-0x4444 device, pristine-src sha256 re-verify); build.log vs fix_build.log (baseline and lock-patched cxm.ko both rc=0, -Werror); fix.diff (softc lock + detach drain; subsumed by pending DF-4753 fix); cxm_ir_getkey.c (victim half; REMOTE_GETKEY constant via sys/ioccom.h _IOR, matches ioctl_bt848.h:269)", "kernel_refs": [ "sys/dev/video/cxm/cxm_ir.c:100-117", "sys/dev/video/cxm/cxm_ir.c:114", "sys/dev/video/cxm/cxm.c:2306", "sys/dev/video/cxm/cxm.c:1884-1885", "sys/dev/video/cxm/cxm.c:1944-2004", "sys/dev/video/cxm/cxm.c:1993-1996", "sys/dev/video/cxm/cxm.c:2894-2898" ], "poc_changes": "Authored fresh (no seed): REMOTE_GETKEY defined via _IOR(x,71,struct bktr_remote) from sys/ioccom.h so userland/kernel encodings cannot drift (kernel: ioctl_bt848.h:269); trigger exits 3 with an explicit hardware-gated message when /dev/cxm0 is absent.", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 420, "guest_dirty": 0, "build_cmd": "cc -O2 -Wall -o /root/df5212/cxm_ir_getkey /root/df5212/cxm_ir_getkey.c ; (cd /root/df5212/base && make) ; (cd /root/df5212/fix && make)", "run_cmd": "sh run.sh (victim half; killer half: while :; do kldunload cxm; kldload cxm; done as root)", "code_hash": "1768900224741f543de3b8a29a6bbe64d90d804af91ff4d534018abfda197de6", "notes": "Pass-2 cxm family close-out: cxm_eeprom clean, DF-4753 (msp), DF-3941 (read), DF-4983 (iic dangle) all interact with this path - the DF-4983 dangling sc->iicbus widens this race beyond the 50 ms window. Guest /usr/src/sys/dev/video/cxm restored pristine after fix build (sha256 verified in run.log). Hunt items killed: no decoder/key tables exist in cxm_ir.c (raw 3-byte i2c passthrough); IOC_OUT buffer pre-zeroed by sys_ioctl so no uninit leak; received<=len never used as index; cxm_ir_init bounded 1-byte attach read.", "recommended_fix": "Serialize cdev ioctls against cxm_detach (softc lock drained in detach) - see fix.diff; fully subsumed by the pending DF-4753 fix.", "fix_status": "inconclusive", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff (cxm.h lock member + lockinit + cxm_ioctl wrapper around cxm_ioctl_locked + detach drain + lockuninit) compiles clean as the full cxm.ko module (-Werror, rc=0) on guest 6.5-DEVELOPMENT source (fix_build.log); baseline module also rc=0 (build.log). Behavioral validation requires cxm hardware absent from this guest.", "fix_evidence": "fix_build.log (patched module rc=0); build.log (baseline rc=0); fix.diff; run.log tail (pristine-src sha256 re-verified after restore)" } |