DF-4893 / trigger.c
/* * DF-4893 โ documentary trigger for the ah_osdep.c ALQ TOCTOU (LATENT). * * The race (FreeBSD-inherited, AH_DEBUG_ALQ block): * CPU0 (register access path): ah_osdep.c:297/:336/:353 * if (ath_hal_alq) { ale = ath_hal_alq_get(ah); // :264/:276 * alq_get(ath_hal_alq, ALQ_NOWAIT); ... } * CPU1 (root): sysctl hw.ath.hal.alq=0 -> ath_hal_setlogging(0) * alq_close(ath_hal_alq); ath_hal_alq = NULL; // :230-232, unlocked * -> CPU0 passes a freed `struct alq` to alq_get/alq_post => UAF write. * * THIS PROGRAM CANNOT RUN ON STOCK DRAGONFLY: the sysctl oid only exists * when the kernel is built with AH_DEBUG_ALQ, whose option mapping is * commented out (sys/conf/options:551) and whose alq(9) prerequisite does * not exist in the tree. It is kept here as the exact reproducer shape for * the day the block is revived (or for FreeBSD, where it builds). * * Shape: two threads โ * A: sysctl("hw.ath.hal.alq", 0/1) toggling in a tight loop (needs root); * B: generate ath register traffic (needs an Atheros NIC; e.g. flood * raw 802.11 frames so the HAL takes reg_read/reg_write on the hot * path โ any channel scan/reset/calibration storm works). * Win condition: panic/UAF splat in alq_get/alq_post with ath_hal_alq * freed by the concurrent alq_close. * * Build: cc -o trigger trigger.c (userland; run.sh checks preconditions * first and refuses to proceed on stock DF). */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/sysctl.h> #ifndef OID_DNE #define OID_DNE 2 /* ENOENT-class for sysctl */ #endif static int oid_exists(const char *name) { int v = 0; size_t l = sizeof(v); return sysctlbyname(name, &v, &l, NULL, 0) != -1; } int main(int argc, char **argv) { int have_alq = oid_exists("hw.ath.hal.alq"); int have_log = oid_exists("hw.ath.hal.alq_logfile"); (void)argv; printf("DF-4893 precondition probe\n"); printf(" hw.ath.hal.alq : %s\n", have_alq ? "PRESENT" : "ABSENT (AH_DEBUG_ALQ not compiled)"); printf(" hw.ath.hal.alq_logfile : %s\n", have_log ? "PRESENT" : "ABSENT"); if (!have_alq) { /* * Stock DragonFly always lands here: the ALQ sysctls live inside * #ifdef AH_DEBUG_ALQ (ah_osdep.c:209-256) and the option cannot * be enabled (sys/conf/options:551 commented out; no alq(9) in * tree; ath_hal_reg_whilst_asleep also missing unless AH_DEBUG). * The race target therefore does not exist in the running kernel. */ fprintf(stderr, "TARGET ABSENT: AH_DEBUG_ALQ block is compiled out. The\n" "ath_hal_alq TOCTOU is latent-only on this system; see\n" "findings/poc/DF-4893/VERDICT.md.\n"); exit(3); } /* * Reachable only on a hand-built kernel (or FreeBSD): become the * toggler thread and hammer enable/disable while a second process * drives ath register traffic (needs real Atheros NIC + root). */ if (geteuid() != 0) { fprintf(stderr, "toggler thread requires root\n"); exit(4); } fprintf(stderr, "target PRESENT: would toggle hw.ath.hal.alq vs. " "concurrent HAL register traffic โ see header comment\n"); return 0; } |