β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4983

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4983 Β· 7 files
FileTypeDescriptionSize
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.

VERDICT.md
↓ download raw

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 before cxm_iic_detach() can ever run (a device whose attach failed is never detached in the DF newbus).
  • Therefore at (a) sc->iicbus != NULL is ALWAYS true when called from cxm_iic_detach β†’ (b) always returns EINVAL β†’ the NULL store is unreachable β†’ sc->iicbus survives cxm_iic_detach pointing at the iicbus device_t that device_delete_child(dev, child) then destroys (cxm_i2c.c:240-243).
  • Contrast with the sibling pointer sc->cxm_iic, which IS correctly nulled via bus_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/cxm0 ioctl touching a sensor while kldunload cxm runs cxm_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_testable
baseline no→ patch + rebuild →patched clean

No 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
↓ fix.diffper-fix-DF-4983

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