DF-2579 / rig.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 | /* * DF-2579 rig -- ieee80211_tdma.c:284-289 silent uint16_t truncation of * iv_swbmiss_period (IEEE80211_TU_TO_TICKS(...) is uint64_t) can yield 0 * -> callout_reset(..., 0, ieee80211_swbmiss) -> per-tick bmiss flood. * * Verbatim: the assignment + callout_reset lines from * ieee80211_tdma.c:284-289. Numbers that truncate to 0 with THIS GUEST's * real hz (=100): bmissthreshold=5, bintval=250, slotcnt=8, slotlen=32768 * inner TU = 2*5*250*((8*32768)/1024) = 640000 * ticks = 640000*1024*100/1000000 = 65536 -> (uint16_t)0 * * sysctl kern.dfv2579.run: * 0 = arithmetic proof (verbatim expression) for the truncating params * 1 = control params (bintval=100 -> 25600 TU -> 2621 ticks, no trunc) * 2 = callout flood A/B: arm with computed 0 period vs sane period, * count fires in 200 ms */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/malloc.h> #include <sys/sysctl.h> #include <sys/callout.h> #include <sys/interrupt.h> #include <net/if_var.h> #include <net/if_media.h> #include <netproto/802_11/ieee80211_var.h> static char df_status[4096]; static struct callout df_co; static int df_fires; static struct lock df_co_lock; /* the truncation demo fields (same types as ieee80211vap) */ static uint16_t demo_iv_swbmiss_period; static uint16_t demo_iv_swbmiss_count; static void df_swbmiss(void *arg) { atomic_add_int(&df_fires, 1); demo_iv_swbmiss_count++; /* ieee80211_swbmiss re-arms with the same period (tdma keeps 0) */ if (demo_iv_swbmiss_period == 0) callout_reset(&df_co, 0, df_swbmiss, arg); else callout_reset(&df_co, demo_iv_swbmiss_period, df_swbmiss, arg); } static void df_run2579(int mode) { char *p = df_status; int t = 0; int hz_real = hz; int bmissthreshold = 5; int bintval, slotcnt = 8, slotlen = 32768; uint64_t inner_tu, ticks64; uint16_t period16; if (mode == 1) { bintval = 100; /* control: no truncation */ } else { bintval = 250; /* truncating params */ } slotcnt = 8; slotlen = 32768; /* VERBATIM expression from ieee80211_tdma.c:284-286 */ inner_tu = 2 * bmissthreshold * bintval * ((slotcnt * slotlen) / 1024); ticks64 = IEEE80211_TU_TO_TICKS(inner_tu); period16 = ticks64; /* uint16_t store */ t += ksnprintf(p + t, sizeof(df_status) - t, "DF2579[mode=%d]: hz(real)=%d bintval=%d slotcnt=%d slotlen=%d " "bmissthreshold=%d\n", mode, hz_real, bintval, slotcnt, slotlen, bmissthreshold); t += ksnprintf(p + t, sizeof(df_status) - t, "DF2579: inner TU=%llu TU_TO_TICKS=%llu -> uint16 period=%u " "(truncated from %llu)\n", (unsigned long long)inner_tu, (unsigned long long)ticks64, period16, (unsigned long long)ticks64); if (mode <= 1) return; /* mode 2: flood A/B with real callouts */ df_fires = 0; demo_iv_swbmiss_period = period16; /* 0 for the vuln params */ callout_init(&df_co); callout_reset(&df_co, demo_iv_swbmiss_period, df_swbmiss, NULL); tsleep(&df_status, 0, "df2579a", hz_real / 5); /* 200ms */ int fires_zero = df_fires; callout_stop(&df_co); df_fires = 0; demo_iv_swbmiss_period = ticks64 ? 6553 : 1; /* ~6.5s sane period */ callout_reset(&df_co, demo_iv_swbmiss_period, df_swbmiss, NULL); tsleep(&df_status, 0, "df2579b", hz_real / 5); int fires_sane = df_fires; callout_stop(&df_co); t += ksnprintf(p + t, sizeof(df_status) - t, "DF2579: fires in 200ms with period=0: %d ; with sane period " "6553 ticks: %d\n", fires_zero, fires_sane); t += ksnprintf(p + t, sizeof(df_status) - t, "DF2579: RESULT=%s\n", fires_zero > 10 * fires_sane + 10 ? "period-0 callout FLOOD (beacon-miss storm: every tick, " "taskqueue saturation, radio DoS)" : "flood not observed this run"); } static int dfv2579_sysctl(SYSCTL_HANDLER_ARGS) { char out[4096]; int error, v = 0; if (req->newptr != NULL) { error = sysctl_handle_int(oidp, &v, 0, req); if (error) return error; df_status[0] = 0; switch (v) { case 0: df_run2579(0); break; case 1: df_run2579(1); break; case 2: df_run2579(2); break; default: return EINVAL; } return 0; } ksnprintf(out, sizeof(out), "%s", df_status); return SYSCTL_OUT(req, out, strlen(out) + 1); } SYSCTL_PROC(_kern, OID_AUTO, dfv2579_run, CTLTYPE_INT | CTLFLAG_RW, 0, 0, dfv2579_sysctl, "I", "dfv2579"); SYSCTL_PROC(_kern, OID_AUTO, dfv2579_status, CTLTYPE_STRING | CTLFLAG_RD, 0, 0, dfv2579_sysctl, "A", "dfv2579 status"); static int dfv2579_load(module_t mod, int what, void *arg) { switch (what) { case MOD_LOAD: lockinit(&df_co_lock, "df2579", 0, 0); kprintf("dfv2579: loaded (hz=%d)\n", hz); return 0; default: return 0; } } static moduledata_t dfv2579_mod = { "dfv2579", dfv2579_load, NULL }; DECLARE_MODULE(dfv2579, dfv2579_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); |