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)
PoC verification
Evidence pack
findings/poc/DF-2310 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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
fixedConfirmed 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.
No comments yet.