DF-2244 / arc4runs.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 186 187 188 189 190 191 192 | /* * arc4runs.c -- DF-2244 verification KLD. * * Demonstrates, purely by observation on the running stock kernel, that * karc4random_buf(ptr, len) advances the per-cpu arc4 run accounting by * exactly ONE run regardless of len (sys/libkern/arc4random.c:152), so a * single large request emits an unbounded number of keystream bytes under * a single RC4 key and never crosses the ARC4_MAXRUNS (16384) reseed * threshold. * * Method: a reseed (arc4_randomstir) costs a read_random() call (chacha + * spinlock) plus an RC4 KSA plus a 768*4-byte keystream discard -- tens of * microseconds -- while a plain karc4random() costs a few hundred cycles * at most. We measure rdtsc deltas around every karc4random() call and * around karc4random_buf(1MB). * * Protocol (all on one cpu, verified per phase): * settle : 1 karc4random() (time-based reseed lands here if boot > 300s) * phaseA : 20000 karc4random() -> expect exactly one spike at i ~= 16384 * (proves threshold behavior + spike detectability) * phaseB : karc4random_buf(1MB) x5 -> expect NO spike in the first 100 * calls of phaseC (proves 5MB charged only 5 runs, not * 5*262144 runs); next spike expected near i ~= 12766 * phaseC : 20000 karc4random() * * If accounting were byte-proportional (the fixed behavior), phaseC's very * first call would spike (262144 >> 16384 runs pending). */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/module.h> #include <sys/malloc.h> #include <machine/cpufunc.h> #define NPH 20000 static uint64_t deltas[NPH]; static MALLOC_DEFINE(M_ARC4RUNS, "arc4runs", "DF-2244 test"); static void report(const char *tag, int n) { uint64_t mean = 0, maxv = 0, thresh; int i, nspike = 0; int top_i[5]; uint64_t top_v[5]; int t, j; for (i = 0; i < n; i++) { mean += deltas[i]; if (deltas[i] > maxv) maxv = deltas[i]; } mean /= n; thresh = 30 * mean; if (thresh < 20000) thresh = 20000; for (t = 0; t < 5; t++) { top_v[t] = 0; top_i[t] = -1; } for (i = 0; i < n; i++) { for (t = 0; t < 5; t++) { if (deltas[i] > top_v[t]) { for (j = 4; j > t; j--) { top_v[j] = top_v[j-1]; top_i[j] = top_i[j-1]; } top_v[t] = deltas[i]; top_i[t] = i; break; } } } kprintf("arc4runs: %s n=%d mean=%llu max=%llu thresh=%llu\n", tag, n, (unsigned long long)mean, (unsigned long long)maxv, (unsigned long long)thresh); kprintf("arc4runs: %s top5:", tag); for (t = 0; t < 5; t++) kprintf(" [%d]=%llu", top_i[t], (unsigned long long)top_v[t]); kprintf("\n"); kprintf("arc4runs: %s spikes(i>thresh):", tag); for (i = 0; i < n; i++) { if (deltas[i] > thresh) { kprintf(" %d", i); nspike++; } } kprintf(" (count=%d)\n", nspike); } static int phase(const char *tag, int n) { uint64_t t0; int i, cpu0 = mycpuid; for (i = 0; i < n; i++) { t0 = rdtsc(); karc4random(); deltas[i] = rdtsc() - t0; } if (mycpuid != cpu0) kprintf("arc4runs: %s WARNING migrated cpu%d->cpu%d, " "results unreliable\n", tag, cpu0, mycpuid); report(tag, n); return (mycpuid == cpu0 ? 0 : 1); } static int spikes_in_first(const char *tag, int n, int first) { uint64_t mean = 0, thresh; int i, cnt = 0; for (i = 0; i < n; i++) mean += deltas[i]; mean /= n; thresh = 30 * mean; if (thresh < 20000) thresh = 20000; for (i = 0; i < first && i < n; i++) if (deltas[i] > thresh) cnt++; kprintf("arc4runs: %s spikes in FIRST %d calls: %d\n", tag, first, cnt); return (cnt); } static int arc4runs_load(struct module *m, int what, void *arg) { uint8_t *mb; uint64_t t0, t1; int rc = 0; switch (what) { case MOD_LOAD: kprintf("arc4runs: === DF-2244 karc4random_buf run-accounting " "experiment (cpu%d) ===\n", mycpuid); /* settle: absorbs any pending time-based reseed */ t0 = rdtsc(); karc4random(); t1 = rdtsc() - t0; kprintf("arc4runs: settle delta=%llu%s\n", (unsigned long long)t1, t1 > 20000 ? " <== time-based reseed spike" : ""); mb = kmalloc(1 << 20, M_ARC4RUNS, M_WAITOK | M_ZERO); if (mb == NULL) { kprintf("arc4runs: no mem\n"); return (ENOMEM); } rc |= phase("phaseA(20000x karc4random)", NPH); /* 5 x 1MiB through karc4random_buf */ for (int k = 0; k < 5; k++) { t0 = rdtsc(); karc4random_buf(mb, 1 << 20); t1 = rdtsc() - t0; kprintf("arc4runs: phaseB[%d] karc4random_buf(1MiB) " "delta=%llu first16=%02x%02x%02x%02x%02x%02x%02x%02x" "%02x%02x%02x%02x%02x%02x%02x%02x\n", k, (unsigned long long)t1, mb[0],mb[1],mb[2],mb[3],mb[4],mb[5],mb[6],mb[7], mb[8],mb[9],mb[10],mb[11],mb[12],mb[13],mb[14],mb[15]); } rc |= phase("phaseC(20000x karc4random)", NPH); spikes_in_first("phaseC", NPH, 100); /* * Expected on STOCK kernel: phaseA 1 spike near i=16384; * phaseC 0 spikes in first 100 calls (5MiB charged only 5 * runs), next spike near i~12700-12800. * Expected on FIXED kernel: phaseC spikes in first 100 != 0 * (262144*5 pending runs force immediate reseed). */ kfree(mb, M_ARC4RUNS); kprintf("arc4runs: === done (cpu%d) ===\n", mycpuid); break; } return (rc); } DECLARE_MODULE(arc4runs, arc4runs_load, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_VERSION(arc4runs, 1); |