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

iicread() leaks I2C bus lock on iicbus_block_read failure

Summary

iicread() acquires I2C bus with iicbus_request_bus at :253 but if iicbus_block_read fails at :263 it returns at :267 without calling iicbus_release_bus. Bus remains locked permanently making I2C bus and all devices inaccessible. Compare iicwrite() at :222-225 which always calls iicbus_release_bus regardless of error. Trigger: read against non-existent I2C address causing NACK. [VERIFIED pass2: reproduced - failed block_read leaves softc owner 0xfffff8008bb654a0 permanent; release-on-error fix-validated owner stays 0]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2311 Β· 6 files
FileTypeDescriptionSize
poc2311.c β€” 2.7 KB view raw
iic_abi.h β€” 742 B view raw
run.log β€” 1.3 KB view raw
fix.diff β€” 461 B view raw
verdict.json β€” 2.3 KB view raw
README.md β€” 1.0 KB ↓ raw

DF-2311 β€” iicread() leaks the I2C bus ownership on block_read failure

What (sys/bus/iicbus/iic.c:253-268 vs iicwrite :222-225)

iicread takes the bus (iicbus_request_bus, :253) and, when iicbus_block_read fails (:263-268), returns WITHOUT iicbus_release_bus β€” struct iicbus_softc->owner stays set forever (any other iicbus child is locked out until reboot; iicwrite always releases).

Rig

Same fakebb3 stack; the harness exposes hw.fakebb3.busowner which reads iicbus_softc->owner live. Trigger: keep sc->started armed so block_read fails at iicbus_start EINVAL (iiconf.c:178) β€” the identical early-error return at :265-268 that the finding's NACK flavor takes on hardware (the emulated peer cannot NACK read-direction data bytes).

Reproduce / Result (run.log)

owner 0 (before) -> 0xfffff8008bb654a0 (after failed read) -> unchanged +2 s. NOTE: probe only with the read-only sysctl β€” any further iic ioctl itself requests+releases the bus and masks the leak.

Fix

fix.diff releases the bus on the error path. Validated: owner stays 0.

Fix verification

fixed
↓ fix.diffper-fix-DF-2311

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log: owner BEFORE 0 -> AFTER FAILED READ 0xfffff8008bb654a0 -> +2s unchanged; read returned EINVAL from the same path

PoC changes

v1 probed with a second ioctl (self-masking: the ioctl path itself requests+releases and hid the leak) and hit the single-open EBUSY; v2/v3 use one O_NONBLOCK fd, keep sc->started armed, and probe via the read-only hw.fakebb3.busowner sysctl added to the harness.

Verified recommended fix

Call iicbus_release_bus on the block_read error path before returning (fix.diff).

Verdict

REPRODUCED with a softc-level probe: after iicread() takes the bus (iicbus_request_bus, iic.c:253) and iicbus_block_read fails (:263), the function returns at :265-268 WITHOUT iicbus_release_bus -- struct iicbus_softc->owner (iicbus.h:39) sticks at the iic0 device pointer permanently: 0 -> 0xfffff8008bb654a0 after the failed read, unchanged +2 s. Compare iicwrite :222-225 which always releases. Any other iicbus child is locked out until reboot (the same-device re-request succeeding masks the lock from the leaking fd itself -- the probe must be read-only, documented in the PoC). Trigger note: the emulated peer cannot NACK read-direction data, so the deterministic block_read failure used is the bus-already-started EINVAL of iicbus_start (iiconf.c:178) -- the identical early-error return at :265-268 that the finding NACK flavor takes on hardware.