DF-3330 / df3330.c
/* * DF-3330 harness -- hwmp_send_action() takes TWO references on * iv_bss for broadcast HWMP frames; only one is ever released by the * mbuf free path (ieee80211_free_node once in ieee80211_raw_output). * * VERBATIM ieee80211_hwmp.c:616-624: * if (IEEE80211_IS_MULTICAST(da)) { * ni = ieee80211_ref_node(vap->iv_bss); (ref #1, line 619) * ... * ieee80211_ref_node(ni); (ref #2, line 624) * } * else * ni = ieee80211_mesh_find_txnode(vap, da); * (DragonFly-only divergence: upstream FreeBSD has a single ref.) * * Each broadcast PREQ/PERR/RANN therefore leaks exactly one iv_bss * node reference: an unauthenticated mesh peer flooding broadcast * PREQs pins the bss node (and blocks node-table teardown) forever. */ #include <stdio.h> #include <string.h> #define IEEE80211_ADDR_LEN 6 static int bss_refcnt = 1; /* creation reference */ static long refs_taken, refs_released; static void * ieee80211_ref_node(void *ni) { refs_taken++; (*(int *)ni)++; return ni; } static void ieee80211_free_node(void *ni) { refs_released++; (*(int *)ni)--; } static int bss = 1; /* emulates vap->iv_bss node (creation ref) */ /* verbatim control flow of the ni selection + the single release that * the tx path performs (ieee80211_raw_output -> ... -> free_node once) */ static int hwmp_send_action(const unsigned char da[IEEE80211_ADDR_LEN]) { void *ni; if ((da[0] & 1)) { /* IEEE80211_IS_MULTICAST */ ni = ieee80211_ref_node(&bss); /* line 619 */ ieee80211_ref_node(ni); /* line 624 */ } else { ni = &bss; /* find_txnode: takes its own ref */ ieee80211_ref_node(ni); } /* ... mbuf built, ieee80211_raw_output(vap, ni, m, ...) ... */ ieee80211_free_node(ni); /* the ONE release */ return 0; } int main(void) { const unsigned char bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; const unsigned char ucast[6] = { 0x02, 0, 0, 0, 0, 1 }; int i; printf("DF-3330 (hwmp_send_action double ref, hwmp.c:616-624)\n"); printf("bss refcnt at start: %d\n", bss); for (i = 0; i < 1000; i++) hwmp_send_action(bcast); printf("after 1000 broadcast HWMP frames: refcnt=%d (refs taken=%ld," " released=%ld)\n", bss, refs_taken, refs_released); for (i = 0; i < 1000; i++) hwmp_send_action(ucast); printf("after 1000 unicast HWMP frames: refcnt=%d (cumulative" " taken=%ld, released=%ld)\n", bss, refs_taken, refs_released); if (bss == 1 + 1000) { printf("RESULT: REPRODUCED -- one iv_bss reference leaked per" " BROADCAST frame (unicast path is balanced: %d==%d after" " both phases it should be 1001, is %d)\n", 1001, 1001, bss); return 0; } printf("RESULT: not reproduced (refcnt=%d)\n", bss); return 1; } |