DF-3455 / df3455.c
/* * DF-3455 harness -- NOL/CAC timeout arithmetic is signed-overflow and * tick-wrap unsafe (ieee80211_dfs.c). * * VERBATIM: * :483 static int ieee80211_nol_timeout = 30*60; (sysctl RW!) * :486 #define NOL_TIMEOUT msecs_to_ticks(ieee80211_nol_timeout*1000) * (ieee80211_dragonfly.h:130) * #define msecs_to_ticks(ms) (((ms)*hz)/1000) (hz=100) * :244-247 (dfs_timeout): * now = oldest = ticks; * if (ieee80211_time_after_eq(now, dfs->nol_event[i]+NOL_TIMEOUT)) * -> channel released from NOL * :268 callout_schedule_dfly(&dfs->nol_timer, oldest + NOL_TIMEOUT - now, ...) * * (a) sysctl write of nol_timeout >= 2147484 overflows * nol_timeout*1000 (signed int UB; wraps negative) -> NOL_TIMEOUT * negative -> radar'd channel released IMMEDIATELY. * (b) tick wrap: ticks is int; after ~248 days, nol_event+NOL_TIMEOUT * overflows; the addition happens BEFORE the wrap-aware compare, * so a channel whose nol_event is close to INT_MAX is released * immediately as ticks wraps. */ #include <stdio.h> #include <limits.h> static int hz = 100; #define msecs_to_ticks(ms) (((ms)*hz)/1000) #define NOL_TIMEOUT msecs_to_ticks(ieee80211_nol_timeout*1000) static int ieee80211_nol_timeout = 30*60; /* sysctl RW */ /* verbatim: wrap-aware compare from sys/systimer.h family */ #define time_after_eq(a, b) ((int)((a) - (b)) >= 0) static int dfs_timeout_release(int now, int nol_event) { if (time_after_eq(now, nol_event + NOL_TIMEOUT)) return 1; /* released from NOL */ return 0; } int main(void) { int now, ev, released_early = 0; printf("DF-3455 (DFS NOL/CAC arithmetic, ieee80211_dfs.c:486,244,268)\n"); printf("[a] default sysctl: nol_timeout=%d -> NOL_TIMEOUT=%d ticks\n", ieee80211_nol_timeout, (int)NOL_TIMEOUT); now = 1000000; ev = now; /* radar just now */ printf(" radar at t=%d: released now? %s (expect no)\n", ev, dfs_timeout_release(now, ev) ? "YES" : "no"); printf("[b] root sets sysctl net.wlan.nol_timeout=2147484\n"); ieee80211_nol_timeout = 2147484; printf(" nol_timeout*1000 = %d (signed int overflow -> UB wrap)\n", (int)(2147484 * 1000)); { int ms = ieee80211_nol_timeout * 1000; /* wrapped */ int ticks_ = (ms * hz) / 1000; /* wrapped again */ printf(" runtime NOL_TIMEOUT = msecs_to_ticks(%d) = %d" " ticks (configured value promises 180000 ticks = 30min)\n", ms, ticks_); ev = 1000000; /* radar detected */ now = ev + ticks_; /* nol_timer fires here */ int rel = dfs_timeout_release(now, ev); printf(" released after %d ticks instead of 180000: %s\n", ticks_, rel ? "EARLY RELEASE" : "no"); released_early |= (rel && ticks_ < 180000); } printf("[c] tick wrap: radar just before the wrap, timer after\n"); ieee80211_nol_timeout = 30*60; /* NOL_TIMEOUT=180000 */ ev = INT_MAX - 1000; /* radar at 2147482647 */ now = 500; /* ticks wrapped to 500 */ printf(" nol_event=%d, sum=%d (overflowed negative)\n", ev, (int)(ev + NOL_TIMEOUT)); int rel2 = dfs_timeout_release(now, ev); printf(" released ~%d ticks after radar (should be 180000)? %s\n", now + 1000, rel2 ? "YES -- EARLY RELEASE" : "no"); released_early |= rel2; printf("RESULT: %s\n", released_early ? "REPRODUCED -- radar'd channel can be released from the NOL" " immediately (root sysctl int-overflow and/or tick-wrap; both" " pre-addition overflows sit before the wrap-aware compare)" : "not reproduced"); return (released_early ? 0 : 1); } |