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

I2CRDWR: unchecked copyin return value sends uninitialized kernel heap to I2C device

Summary

In I2CRDWR message loop copyin(usrbufs[i] m->buf m->len) at :362 does not check return value. If copyin fails (invalid user buffer) m->buf contains uninitialized heap data (kmalloc without M_ZERO) subsequently sent to I2C peripheral via iicbus_transfer. Similarly copyout at :369 unchecked. Attacker crafts write message with invalid buf pointer copyin silently fails uninitialized kernel heap written to EEPROM then read back for indirect info leak. [VERIFIED pass2: reproduced on production iic.ko rig - kernel-range buf swallows the copyin EFAULT and the emulated slave captures stale heap ALEHEAP plus pointer qword on the wire; error-propagation plus M_ZERO fix-validated EFAULT]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2310 Β· 7 files
FileTypeDescriptionSize
poc2310.c β€” 4.8 KB view raw
iicbb_harness3.c β€” 14.3 KB view raw
rigup.sh β€” 2.6 KB view raw
run.log β€” 1.9 KB view raw
fix.diff β€” 1.6 KB view raw
verdict.json β€” 2.7 KB view raw
README.md β€” 1.7 KB ↓ raw

DF-2310 β€” I2CRDWR unchecked copyin sends uninitialized kernel heap to the I2C device

What (sys/bus/iicbus/iic.c:357-363, 366-371)

copyin(usrbufs[i], m->buf, m->len) at :362 (write-direction messages) and copyout(m->buf, usrbufs[i], m->len) at :369 (read-direction) both ignore their return values. m->buf is a fresh kmalloc(m->len, M_TEMP, M_WAITOK) with no M_ZERO (:360): a failed copyin leaves stale kernel heap in the buffer and iicbus_transfer (:364) clocks it out to the peripheral; a failed copyout silently drops read data.

Rig

Production iic.ko/iicbus.ko/iicbb.ko on the fakebb3 harness (DF-5539 family; disclosed setup patches DF-5559/5560 make /dev/iic0 reachable β€” the I2CRDWR logic under test stays stock). The harness emulates a wired I2C slave that CAPTURES every byte the master transmits (hw.fakebb3.dumprx). groom5558.ko provides the deterministic M_TEMP heap groom (marker "DF5558STALEHEAP!" + kernel-realm pointer qwords).

Reproduce (guest, root)

sh rigup.sh            # patches (disclosed setup), modules, groom, PoCs
./poc2310

Result (run.log)

  • CONTROL (valid buf): slave captures exactly the submitted 0x40..0x5f.
  • ATTACK (buf = 0xffffffff00000000 after groom): ioctl returns SUCCESS (copyin EFAULT swallowed) and the wire capture is ffffffffffffffff 414c454845415021 01000000 00000000 20000000 5a5a5a5a β€” the groom marker remnant + kernel-realm pointer qword: stale kernel heap disclosed onto the I2C bus.

Fix

fix.diff: propagate the copyin error (skip the transfer), report copyout failures, free exactly nalloc buffers, and zero-allocate per-message buffers. Validated on the fix kernel: EFAULT returned, transfer skipped, wire capture empty.

Fix verification

fixed
↓ fix.diffper-fix-DF-2310

Confirmed kernel references

Detail

Exploit chain

groom M_TEMP chunk (marker + kernel-realm qwords) -> kfree -> I2CRDWR write msg with kernel-range buf -> silent copyin EFAULT -> kmalloc recycles the chunk -> stale heap clocked out on SDA -> emulated slave capture (sysctl dump) = kernel heap disclosure over the I2C wire

Evidence (decisive lines)

run.log: CONTROL captures exactly the submitted pattern 0x40..0x5f; ATTACK capture ffffffff...414c454845415021... after groom; ioctl returned SUCCESS

PoC changes

Extended the DF-5539 harness with hw.fakebb3.dumprx/clearrx sysctls (slave wire-capture dump) and a busowner probe; PoC self-contains the iic ioctl ABI (no /usr/include/bus/iicbus).

Verified recommended fix

Check the copyin return (propagate EFAULT, skip the transfer) and the copyout return; M_ZERO the per-message buffers (fix.diff).

Verdict

REPRODUCED on the production iic.ko/iicbus.ko/iicbb.ko stack riding the fakebb3 harness (DF-5539 rig family, disclosed setup patches DF-5559/5560; the I2CRDWR logic under test is stock): a write-direction I2CRDWR message with an invalid user buffer pointer (0xffffffff00000000, kernel range) is accepted with ioctl SUCCESS -- the unchecked copyin at iic.c:362 fails silently -- and the wire capture shows the freshly kmalloc(M_WAITOK, no M_ZERO) buffer transmitted stale kernel heap to the emulated slave: bytes 8..15 of the groom marker DF5558STALEHEAP! (414c454845415021), a kernel-realm pointer-qword remnant (ffffffffffffffff) at the head, and marker sequence/size/fill tails. At least 12 bytes of provably-stale kernel heap per message reach the I2C peripheral (and are readable back by the same operator), with the error never reported to userspace. The sibling unchecked copyout at :369 is addressed by the same fix.