DF-5586 / storm.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | /* * storm.c — DF-5586/lead-2 in-vkernel fault storm (runs as GUEST USERLAND * inside the booted vkernel; a normal unprivileged static DragonFly binary). * * Hammers every fault class the vkernel must reflect for guest-user code * (the reservation's evidence classes): * T_DIVIDE integer divide by zero -> SIGFPE * T_XMMFLT unmasked 1.0/0.0 (SSE) -> SIGFPE * ud2 invalid opcode -> SIGILL * T_PAGEFLT write to read-only page -> SIGSEGV * T_PAGEFLT access to PROT_NONE page -> SIGSEGV * plus, from a forked sibling sharing a mapped region: mprotect churn that * forces cross-CPU TLB shootdowns (SIGUSR1 IPIs inside the -n 2 vkernel) * while the storm runs — pressure on the exact signal fabric exception.c * installs. * * None of these guest-user faults may reach the vkernel's exc_segfault * (they return via vmspace_ctl -> go_user -> user_trap, trap.c:1407-1439). * Success criterion: STORM_DONE with counters, vkernel still ALIVE. */ #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <sys/wait.h> #include <unistd.h> static void *volatile g_resume; static char *ro_page, *bad_page, *sh_page; static volatile unsigned long n_fpe, n_xmm, n_ill, n_prot, n_map; #define FLUSH(x) do { volatile unsigned long *_p = (unsigned long *)&(x); (void)_p; } while (0) static void h_fpe(int sig, siginfo_t *si, void *ctx) { (void)sig; (void)si; n_fpe++; ((ucontext_t *)ctx)->uc_mcontext.mc_rip = (register_t)g_resume; } static void h_ill(int sig, siginfo_t *si, void *ctx) { (void)sig; (void)si; n_ill++; ((ucontext_t *)ctx)->uc_mcontext.mc_rip = (register_t)g_resume; } static void h_segv(int sig, siginfo_t *si, void *ctx) { (void)sig; (void)si; if ((char *)si->si_addr == bad_page || ((char *)si->si_addr >= bad_page && (char *)si->si_addr < bad_page + 4096)) n_map++; else n_prot++; /* rearm whichever page faulted, then resume at g_resume */ mprotect(bad_page, 4096, PROT_READ|PROT_WRITE); mprotect(ro_page, 4096, PROT_READ|PROT_WRITE); ((ucontext_t *)ctx)->uc_mcontext.mc_rip = (register_t)g_resume; } int main(int argc, char **argv) { struct sigaction sa; volatile unsigned long one = 1, zero = 0, q; volatile double d = 1.0, dz = 0.0, dq; pid_t churn; int i, st, xmm_on = 1, iters = 400000; ro_page = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); bad_page = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); sh_page = mmap(NULL, 4096 * 32, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0); if (ro_page == MAP_FAILED || bad_page == MAP_FAILED || sh_page == MAP_FAILED) { dprintf(2, "mmap failed\n"); return (2); } *ro_page = 1; *bad_page = 1; memset(sh_page, 3, 4096 * 32); /* * Unmask SSE divide-by-zero in MXCSR (feenableexcept not in the * static libc): bit 2 = DIV_BY_ZERO_EXC mask. */ { unsigned int mx; __asm __volatile("stmxcsr %0" : "=m" (mx)); mx &= ~(1u << 2); __asm __volatile("ldmxcsr %0" :: "m" (mx)); } /* shared SIGFPE/SIGILL dispatch via mc_trapno-free trampoline */ memset(&sa, 0, sizeof(sa)); sa.sa_flags = SA_SIGINFO | SA_NODEFER; /* storm-safety: rearm fast */ sigemptyset(&sa.sa_mask); sa.sa_sigaction = h_fpe; sigaction(SIGFPE, &sa, NULL); sa.sa_sigaction = h_ill; sigaction(SIGILL, &sa, NULL); sa.sa_sigaction = h_segv; sigaction(SIGSEGV, &sa, NULL); sigaction(SIGBUS, &sa, NULL); if (argc > 1) iters = atoi(argv[1]); /* * Sibling sharing sh_page: mprotect churn against a mapping that is * ALSO live in the parent => cross-CPU TLB shootdown IPIs inside the * 2-cpu vkernel for the whole storm. */ if (argc > 2 && atoi(argv[2]) == 0) { churn = -1; goto nochurn; } churn = fork(); if (churn == 0) { unsigned k = 0; for (;;) { mprotect(sh_page + ((k & 31) * 4096), 4096, (k & 1) ? (PROT_READ) : (PROT_READ|PROT_WRITE)); sh_page[(k & 31) * 4096] = (char)k; k++; } _exit(0); } nochurn: for (i = 0; i < iters; i++) { if ((i % 25) == 0) { char pb[128]; int pl = snprintf(pb, sizeof pb, "PROGRESS i=%d fpe=%lu ill=%lu prot=%lu map=%lu xmm=%lu\n", i, n_fpe, n_ill, n_prot, n_map, n_xmm); write(2, pb, pl); } write(2,"a",1); g_resume = &&L1; q = one / zero; L1: FLUSH(q); write(2,"b",1); g_resume = &&L2; __asm __volatile("ud2"); L2: ; write(2,"c",1); g_resume = &&L3; { volatile char *p = ro_page; mprotect(ro_page, 4096, PROT_READ); *p = (char)(i + 1); /* SIGSEGV */ } L3: write(2,"d",1); g_resume = &&L4; { volatile char *p = bad_page; mprotect(bad_page, 4096, PROT_NONE); *p = 2; /* SIGSEGV */ } L4: write(2,"e",1); if (xmm_on) { g_resume = &&L5; dq = d / dz; L5: FLUSH(dq); } write(2,"f\n",2); /* keep the shared mapping hot in this process too */ if ((i & 63) == 0) sh_page[(i & 31) * 4096]++; } if (churn > 0) { kill(churn, SIGKILL); waitpid(churn, &st, 0); } printf("STORM_DONE iters=%d intdiv=%lu ill=%lu prot=%lu " "map=%lup xmm=%lu(on=%d)\n", i, n_fpe, n_ill, n_prot, n_map, n_xmm, xmm_on); printf("VKERNEL_SURVIVED_IF_YOU_SEE_THIS\n"); return (0); } |