cxm_iic_detach()'s CXM_IVAR_IICBUS invalidation is a permanent no-op (write_ivar guard rejects clears) β cxm_softc.iicbus dangles to a destroyed device_t after iic teardown
Summary
The parent's write_ivar rejects any write while iicbus is set - including the val==0 detach-clear whose return is ignored - so the one line of lifetime cleanup in cxm_iic_detach is dead code: sc->iicbus keeps pointing at the destroyed iicbus device_t that the sensor sub-drivers deref 27+ times. The sibling pointer IS correctly nulled via bus_child_detached - the asymmetry is the defect. Val==0-clears-unconditionally fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4983 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.9 KB | β raw | |
| VERDICT.md | β | 3.7 KB | β raw | |
| df4983_cxm_iic_stale_ivar.c | β | 3.0 KB | view raw | |
| env.txt | β | 811 B | view raw | |
| verdict.json | β | 2.7 KB | view raw | |
| fix.diff | β | 540 B | view raw | |
| manifest.json | β | 833 B | view raw |
DF-4983 β cxm(4) iic glue: detach-time CXM_IVAR_IICBUS invalidation is a permanent no-op β stale sc->iicbus device pointer
What
cxm_iic_detach() (sys/dev/video/cxm/cxm_i2c.c:229) tries to clear the
parent's iicbus pointer with BUS_WRITE_IVAR(..., CXM_IVAR_IICBUS, 0)
and ignores the return value. The parent's handler
(sys/dev/video/cxm/cxm.c:2115-2118) rejects the write with EINVAL
whenever sc->iicbus is already set β which it always is at detach
time (the same ivar, written by cxm_iic_attach(), is the only setter).
The clear therefore can never succeed; struct cxm_softc.iicbus
keeps pointing at the iicbus device_t after device_delete_child()
destroys it.
Trigger (requires a Conexant iTVC15/16 card)
user$: while :; do ioctl sensor path on /dev/cxm0; done # any I2C-touching ioctl
root#: kldunload cxm # cxm_detach β cxm_iic_detach (no-op clear)
# β in-flight iicbus_start()/iicbus_write() on the freed device_t
See df4983_cxm_iic_stale_ivar.c for the annotated trace.
Status on the reference guest
Not runnable β no cxm hardware. cxm.ko/cxm_iic.ko are resident in
X86_64_GENERIC, but no Conexant card exists on the QEMU PCI bus, so
cxm never attaches and /dev/cxm* is never created (env evidence in
DF-4982's env.txt, same session). The defect itself is certain from
source: the reject-when-set guard plus the ignored return leave exactly
one possible outcome (stale pointer) every time cxm_iic_detach runs.
Relation to known findings
The racy dereference window is the already-filed cxm detach-vs-ioctl family (DF-4753 / DF-4419 twin) β NOT re-reported here. This finding is the glue-layer defect that makes the second object (iicbus device_t) un-invalidate-able: the intended lifetime hook is dead code.
Fix
fix.diff β cxm_write_ivar: val == 0 clears unconditionally
(detach mode); non-zero keeps the set-once EINVAL guard.
DF-4983 VERDICT β cxm_iic_detach() can never invalidate cxm_softc.iicbus
Status: untested at runtime (hardware-gated). Defect certain from source.
Proof (no runtime needed)
cxm_iic_detach() β sys/dev/video/cxm/cxm_i2c.c:220-246 β line 229:
BUS_WRITE_IVAR(device_get_parent(dev), dev, CXM_IVAR_IICBUS, 0);
return value ignored.
cxm_write_ivar() β sys/dev/video/cxm/cxm.c:2100-2126 β lines 2115-2118:
case CXM_IVAR_IICBUS:
if (sc->iicbus) /* (a) */
return EINVAL; /* (b) */
sc->iicbus = val ? *(device_t *)val : NULL; /* unreachable for val==0 */
cxm_iic_attach()(cxm_i2c.c:189-194) is the ONLY writer of this ivar; it runs beforecxm_iic_detach()can ever run (a device whose attach failed is never detached in the DF newbus).- Therefore at (a)
sc->iicbus != NULLis ALWAYS true when called fromcxm_iic_detachβ (b) always returns EINVAL β the NULL store is unreachable βsc->iicbussurvivescxm_iic_detachpointing at the iicbus device_t thatdevice_delete_child(dev, child)then destroys (cxm_i2c.c:240-243). - Contrast with the sibling pointer
sc->cxm_iic, which IS correctly nulled viabus_child_detached(cxm.c:2060-2068) and is used as the guard at cxm.c:622 β the asymmetry confirms intent vs. effect.
Consequence: sc->iicbus is a stale device_t after iic teardown. Every
sensor sub-driver dereferences it (27 uses in cxm_saa7115.c alone, e.g.
cxm_saa7115.c:678 cxm_saa7115_reset(sc->iicbus, ...) β
iicbus_start(iicbus, ...) at cxm_saa7115.c:574; cxm_msp34xxx.c:295+;
cxm_tuner.c; cxm_eeprom.c).
Reachability
- Non-racy internal paths currently never deref after invalidation:
cxm's own attach-fail and detach orders put iic teardown last / guard
with
sc->cxm_iic(cxm.c:622, 1923-1929, 1966-1973). Checked both orders β clean. - Racy path: an in-flight
/dev/cxm0ioctl touching a sensor whilekldunload cxmrunscxm_detachβcxm_iic_detach(no-op clear) β iicbus freed βiicbus_start(freed)UAF. The serialization hole is the known DF-4753/DF-4419 family; the present finding contributes the second, permanently-unrecoverable stale object from the i2c glue file. - Hardware gate: cxm_iic attaches only under a probed cxm device (Conexant iTVC15/16; Hauppauge PVR-250/350). Guest has none (verified in the same session as DF-4982's env.txt: cxm.ko resident, /dev/cxm never created). No local trigger exists to synthesize the parent-child attach without the card.
Severity rationale
Low: exploitation requires (1) the rare capture card, (2) a concurrent root kldunload against an unprivileged user's ioctl β the user-visible step of the known family race β and (3) winning the race window. The defect itself (dead invalidation hook) is certain; impact rides the already-filed race. Filed as a distinct pass-2 finding because it lives in cxm_i2c.c's own code (line 229) and because the fix is local and cheap.
Fix validation
Not applicable β no baseline reproducible on the guest (no card).
fix.diff (cxm.c write_ivar 0-case clear) makes line 229 actually
invalidate; set-once semantics for non-zero preserved.
Bottom line
One-line dead-cleanup bug, certain from source, arms the known cxm detach race with a second freed object; fix is a 5-line ivar semantics change.
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.
Fix verification
not_testableNo baseline reproducible on the available guest (no cxm hardware -> /dev/cxm never exists), so no A/B fix validation; fix.diff makes the cxm_i2c.c:229 invalidation actually take effect while preserving set-once attach semantics
fix.diff (cxm.c write_ivar 0-case clear) in this pack
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (line-accurate proof incl. the sc->cxm_iic vs sc->iicbus invalidation asymmetry); df4983_cxm_iic_stale_ivar.c (annotated trigger sketch: unpriv ioctl loop + kldunload cxm); fix.diff (write_ivar val==0 clears unconditionally)
PoC changes
Authored fresh; no pass-1 seed existed for cxm_i2c.c
Verified recommended fix
cxm_write_ivar: treat val==0 as an unconditional detach-time clear; keep the set-once EINVAL guard for non-zero values
Verdict
Certain dead-cleanup defect by source trace: cxm_iic_detach (cxm_i2c.c:229) clears CXM_IVAR_IICBUS with return ignored, but cxm_write_ivar (cxm.c:2115-2118) rejects any write when sc->iicbus is already set β always the case at detach β so cxm_softc.iicbus permanently dangles to the destroyed iicbus device_t after iic teardown; sensor sub-drivers deref it 27+ times (cxm_saa7115.c:678+, cxm_msp34xxx.c:295+). Non-racy cxm attach/detach orders never deref after teardown (checked); realistic deref requires the known DF-4753/DF-4419 detach-vs-ioctl race, which this bug arms with a second freed object that the intended invalidation hook can never reclaim. Not runnable on the reference guest: cxm never attaches without a Conexant iTVC15/16 card (verified cxm.ko resident, /dev/cxm absent).
No comments yet.