DF-4983 / df4983_cxm_iic_stale_ivar.c
/* * DF-4983 โ cxm(4): cxm_iic_detach()'s iicbus invalidation is a * permanent no-op -> struct cxm_softc.iicbus dangles after iic teardown * * Root cause (two halves): * * sys/dev/video/cxm/cxm_i2c.c:229 (cxm_iic_detach) * BUS_WRITE_IVAR(device_get_parent(dev), dev, * CXM_IVAR_IICBUS, 0); // return IGNORED * * sys/dev/video/cxm/cxm.c:2115-2118 (cxm_write_ivar) * case CXM_IVAR_IICBUS: * if (sc->iicbus) // always TRUE at detach time โ * return EINVAL; // the clear is rejected FOREVER * sc->iicbus = val ? *(device_t *)val : NULL; * * cxm_iic_attach() is the only setter (via the same ivar), so whenever * cxm_iic_detach() runs, sc->iicbus != NULL and the write returns EINVAL * before reaching the assignment. The ignored return makes the one line * of lifetime cleanup in this driver dead code: after the iicbus/iicbb * grandchildren are destroyed, cxm_softc.iicbus still points at the * destroyed device_t. * * Consumers that dereference the stale pointer (all take sc and use * sc->iicbus directly -> iicbus_start/write/read/stop on it): * cxm_saa7115.c:678,685,689,... (27 uses) * cxm_msp34xxx.c:295,298,302,338,365,... * cxm_tuner.c / cxm_eeprom.c (same pattern) * * Reachability of a dereference after invalidation: any cxm ioctl that * touches the I2C sensors racing cxm_detach() (kldunload cxm). cxm has * zero serialization on that path โ this is the same detach-vs-ioctl * race already filed as DF-4753 (cxm_msp twin) / DF-4419 family; the * new fact here is the second freed object (the iicbus device_t itself) * that the glue layer *intended* to invalidate and cannot. * * Non-racy paths are safe today: cxm.c:622 guards stop_hardware's I2C * calls with `if (sc->cxm_iic)`, and that sibling pointer IS nulled * (cxm_child_detached, cxm.c:2066-2067). Only the ivar-cleared iicbus * pointer is left stale โ the invalidation asymmetry is the defect. * * HARDWARE GATE (why untested): cxm_iic only attaches as a child of a * cxm(4) device, which only probes on a Conexant iTVC15/iTVC16 MPEG * codec card (Hauppauge PVR-250/350, PCI vendor 0x4444). The reference * QEMU guest has none (verified: cxm.ko resident, /dev/cxm never * created). This "PoC" therefore documents the trigger; running it * needs the card. * * Sketch of the live trigger on equipped hardware (root + unpriv user): * user$: while :; do dd if=/dev/cxm0 of=/dev/null bs=64k count=1; done * (or loop a sensor ioctl: CXM_IOCTG_SOURCE / SELECT etc.) * root#: kldunload cxm_iic cxm # or just kldunload cxm * -> cxm_detach -> bus_generic_detach -> cxm_iic_detach (no-op clear) * -> device_delete_child frees iicbus/iicbb devices * -> in-flight ioctl in cxm_saa7115_* / cxm_msp_* calls * iicbus_start(freed device_t) => UAF on iicbus device * * Fix (fix.diff in this pack): give write_ivar an explicit detach-mode โ * val == 0 clears unconditionally; non-zero keeps the set-once guard. */ |